Events and Mutexes

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.

  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;

// Example:
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;

// Example:
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;