madKernel encapsulates two different kernel synchronization objects, namely
events and mutexes. Both can have a name and both can synchronize even
threads of different processes. There's one big difference though: An event
is either locked or unlocked. When a thread waits for an event which is
locked, the thread will wait until someone (else) unlocks the event. So a
thread can easily deadlock itself by first locking an event and then waiting
for it. A mutex behaves differently. A mutex is not locked or unlocked.
Instead it is owned (by a specific thread) or not owned. A thread takes
ownership of a mutex simply by waiting for it. If a thread already owns a
mutex and then waits for it again, there is no deadlock, the wait function
returns without delay. So a thread can not deadlock itself by using a mutex.
In some situations a mutex makes more sense than an event and vice versa.
Simply choose what fits your needs better.
madKernel wraps the mentioned two objects up with the interfaces IEvent and
IMutex. See also the IEvent Reference and the IMutex Reference.
 |
type
IEvent = interface (IWaitableObj) ['{53F8CE41-2C8A-11D3-A52D-00005A180D69}'];
IMutex = interface (IWaitableObj) ['{7B7FE820-8E34-11D3-A52E-00005A180D69}'];
|
|
You can create a new event by calling "NewEvent". There are two different
types of events. If you set "auto" to "true" you have an automatic event,
otherwise you have a manual event. The manual event doesn't change it's
locking state automatically, you have to manually set it by calling the
appropriate methods (described a bit later). An automatic event gets locked
automatically as soon as someone successfully waited for it. As a result
with an automatic event you can make sure (if you want) that always only one
thread runs through a specific code block. You can't do that with a manual
event. On the other hand, with a manual event you can unlock a whole bunch
of waiting threads at once, which you can't do with an automatic event.
 |
function NewEvent (auto : boolean = false;
locked : boolean = true;
name : string = '';
eventAttr : PSecurityAttributes = nil ) : IEvent;
|
|
Call "NewMutex" to create a new mutex. You can specify whether you want to
take immediate ownership of the mutex or not. Also you can give the mutex a
name to share it with other processes.
 |
function NewMutex (name : string = '';
enter : boolean = true;
mutexAttr : PSecurityAttributes = nil ) : IMutex;
mtx := NewMutex('MadshisUniqueMutexName');
|
|
Use the following functions to open an already existing named mutex/event.
It doesn't matter to which process the existing synchronization object
belongs. So please make sure that you name your objects uniquely.
 |
function OpenEvent (name : string;
access : cardinal = EVENT_ALL_ACCESS;
inheritHandles : boolean = true ) : IEvent; overload;
function OpenMutex (name : string;
access : cardinal = MUTEX_ALL_ACCESS;
inheritHandles : boolean = true ) : IMutex; overload;
mtx := OpenMutex('MadshisUniqueMutexName');
|
|
The following functions convert a win32 handle dword value or an IHandle
instance into a IEvent/IMutex interface instance:
 |
function Event (const event : IHandle ) : IEvent; overload;
function Mutex (const mutex : IHandle ) : IMutex; overload;
function Event (event : cardinal;
autoClose : boolean = true) : IEvent; overload;
function Mutex (mutex : cardinal;
autoClose : boolean = true) : IMutex; overload;
|
|
The property "Name" tells you which name the event/mutex has (no kidding!).
This works only if you got the event/mutex instance from CreateEvent/Mutex
or OpenEvent/Mutex, though.
 |
property IEvent. Name : string;
property IMutex. Name : string;
|
|
Is this event an automatic or a manual event? This method works always,
regardless where the event instance came from.
 |
function IEvent. IsAuto : boolean;
|
|
With the following methods you can manually lock, unlock or pulse an event.
This works on both manual and automatic events. Pulsing an event is the same
as locking and unlocking it at the same time.
 |
function IEvent. Lock : boolean;
function IEvent. Unlock : boolean;
function IEvent. Pulse : boolean;
|
|
Calling "Enter" has the same effect as calling IWaitableObj.WaitFor
with the "handleMessages" parameter set to "false". It makes not much sense
here to call IWaitableObj.WaitFor with "handleMessages" set to "true",
because then an internal helper thread would gain ownership of the mutex.
"TryEnter" always returns at once. If the mutex is owned by another thread,
"TryEnter" returns "false" and does not take over ownership of the mutex. If
the mutex is not currently owned, "TryEnter" behaves like "Enter".
 |
function IMutex. Enter (milliseconds: cardinal = INFINITE) : boolean;
function IMutex. Leave : boolean;
function IMutex. TryEnter : boolean;
|
|