Last edit: 26/07/2013

Kernel event subsystem may be useful when writing drivers and kernel space
services. It is not related to the subsystem of GUI events. An event, from the
kernel's point of view, is a kernel space object which is owned by the thread
that created it.

struc EVENT
{
   .magic       dd ?    ; 'EVNT'
   .destroy     dd ?    ; internal destructor
   .fd          dd ?    ; next object in list
   .bk          dd ?    ; prev object in list
   .pid         dd ?    ; owner (thread) id
   .id          dd ?    ; event uid. (just a number)
   .state       dd ?    ; internal flags; see below
   .code        dd ?    ; MSB: event class; next byte: priority
                        ; (used by kernel only, always 0 for reading),
                        ; The higher dword value the higher event priority.
                        ; Two LSBs: event code.
                rd 5    ; .data: the structure of this field is not defined and
                        ; depends on .code field. (Pass any data you need here)
   .size     =  $ - .magic
   .codesize =  $ - .code
}

Realtime events have class 0хFF. Currently defined:
EVENT.code=                             ; (Used in sound subsystem)
        RT_INP_EMPTY      = 0xFF000001
        RT_OUT_EMPTY      = 0xFF000002
        RT_INP_FULL       = 0xFF000003
        RT_OUT_FULL       = 0xFF000004


Flags of EVENT.state field are defined in gui/event.inc.
        EVENT_SIGNALED   = 0x20000000 ; bit 29: event is active/inactive
        EVENT_WATCHED    = 0x10000000 ; bit 28: owner thread is waiting for the
                                      ;         event to be active
        MANUAL_RESET     = 0x40000000 ; bit 30: do not deactivate event
                                      :         automatically on receive
        MANUAL_DESTROY   = 0x80000000 ; bit 31: do not return event to a list of
                                      ;         free ones on receive

As of SVN r3732 (assume same below) the definition is located in
/kernel/trunk/const.inc and is as follows:

struct  APPOBJ                  ; common object header
        magic           dd ?    ;
        destroy         dd ?    ; internal destructor
        fd              dd ?    ; next object in list
        bk              dd ?    ; prev object in list
        pid             dd ?    ; owner id
ends

struct  EVENT           APPOBJ
        id              dd ?    ; event uid
        state           dd ?    ; internal flags
        code            dd ?
                        rd 5    ; .data
ends

Code is located in gui/event.inc.
Event objects live in kernel memory as a double-linked list (see fields .bk and
.fd). While initialization the kernel reserves memory, creates 512 events and
places them into FreeEvents list. When out of free event, kernel creates another
512 ones etc. Each thread has own double-linked lists where an event may be
placed to:
        ObjList -- a list of kernel objects associated with the thread;
        EventList -- a list of kernel events for the thread.
When events are moved between lists or reordered their data are not copied. This
is done only via modification of .fd and .bk fields. These lists work as FIFO
queues. Sending does not block, receiving blocks. Addressing is direct, by
thread id. There always is an owner thread for an event.

Event's life cycle is defined by flags while creation. By default the kernel
uses values MANUAL_RESET = 0 and MANUAL_DESTROY = 0. Such an event is oneshot
and is automatically freed by the kernel and returned to the FreeEvents list
when received. An event with flag MANUAL_DESTROY = 1 becomes inactive when
received but remains in thread's object list and can be reused. An event with
flags MANUAL_DESTROY = 1 and MANUAL_RESET = 1 remains active when received and
can be reset via call to ClearEvent.

A life cycle example of a sound subsystem event:
 * For an audio buffer (possibly several) the driver creates an event in ObjList
   by calling CreateEvent with flag MANUAL_DESTROY.
 * Then driver calls WaitEvent for the event (waits for EVENT_SIGNALED event
   flag) and blocks waiting for buffer update request.
 * The buffer update request is sent with RaiseEvent from another thread.
 * Sending (RaiseEvent) and receiving (WaitEvent) are repeated as buffer gets
   empty.
 * Driver deactivates the event with ClearEvent when playback is stopped.

Actually, the event structure is described here only for understanding of
subsystem work principles. Direct field access is discouraged due to possible
compatibility issues in the future. Only API calls should be used. A pair
"pointer to an event" and "event id" is considered a single 64-bit id. This id
should be stored somewhere after a call to CreateEvent for further work with the
event.

The kernel exports following event related functions:
(for drivers, etc; called from kernel mode)

        CreateEvent
        RaiseEvent
        ClearEvent
        SendEvent
        DestroyEvent
        WaitEvent
        WaitEventTimeout
        GetEvent
        For user applications sysfn 68.14 (a wrapper to GetEvent)

--------------------------------------------------------------------------------
CreateEvent:
        Creates a new event in ObjList queue of current thread.
        Sets:
                EVENT.destroy   <= default internal destructor
                EVENT.pid       <= current Process id
                EVENT.id        <= unique id
                EVENT.state     <= ecx: flags
                EVENT.code      <= [esi]: size is 6*dword, do not copy if esi=0
        Returns:
                eax -- pointer to the event or 0 for error.
                edx -- Event.id.
        Destroys: eax,ebx,edx,ecx,esi,edi
--------------------------------------------------------------------------------
RaiseEvent:
        Activates existing event (may be owned by another thread) by setting
        EVENT_SIGNALED flag. Sets EVENT.code data if necessary. Does nothing
        more if EVENT_SIGNALED flag is already active in the event. If
        EVENT_SIGNALED flag is not set in the event it will be set, except when
        EVENT_WATCHED in edx = 1 and EVENT_WATCHED in the event = 0. I.e. while
        setting EVENT_WATCHED in edx it is checked if owner thread is waiting
        for event activation. No flags, except EVENT_SIGNALED, are modified in
        the event.
        Gets:
                eax     -- pointer to event
                ebx     -- id
                edx     -- flags (see EVENT.state)
        Sets:
                EVENT.code    <= [esi]: size is 6*dword, do not copy if esi=0
        Returns: ?
        Destroys: eax,ebx,edx,ecx,esi,edi
--------------------------------------------------------------------------------
ClearEvent:
        Move event to ObjList of owner thread. (May be it was already there.)
        Reset flags EVENT_SIGNALED and EVENT_WATCHED, keep other fields (.code,
        .id).
        Gets:
                eax     -- pointer to event
                ebx     -- id
        Returns: ?
        Destroys: eax,ebx,ecx,edi
--------------------------------------------------------------------------------
SendEvent:
        Create a new event in the event list of target thread. Sets
        EVENT_SIGNALED flag in the event.
        Gets:
                EVENT.pid       <= eax: target thread id;
                EVENT.code      <= [esi]: size is 6*dword, do not copy if esi=0
        Returns:
                eax -- pointer to event or 0 for error
                edx -- Event.id
        Destroys: eax,ebx,ecx,esi,edi
--------------------------------------------------------------------------------
DestroyEvent:
        Moves event to FreeEvents, clears fields .magic, .destroy, .pid, .id.
        The event may be owned by other thread.
        Gets:
                eax     -- pointer to event
                ebx     -- event id
        Returns:
                eax     -- 0 for error, non-zero for success
        Destroy: eax,ebx,ecx
--------------------------------------------------------------------------------
WaitEvent:
        Wait infinitely until flag EVENT_SIGNALED is set in the event owned by
        the caller thread. This flag is set by signaling thread via RaiseEvent.
        Waiting thread is frozen by setting APPDATA.state <= TSTATE_WAITING=5.
        Flag EVENT_WATCHED is set in the event before freeze.
        If flag MANUAL_RESET is NOT set in the event then:
                EVENT_SIGNALED and EVENT_WATCHED are reset when the event is
                received.
                When MANUAL_DESTROY is
                        inactive: the event is destroyed by DestroyEvent,
                        active: the event is moved to ObjList of current thread.
        Gets:
                eax     -- pointer to event
                ebx     -- event id
        Returns: ?
        Destroys: eax,ebx,edx,ecx,esi,edi
--------------------------------------------------------------------------------
WaitEventTimeout:
        Wait with a timeout until flag EVENT_SIGNALED is set in the event owned
        by caller thread. This flag is set by signaling thread via RaiseEvent.
        Waiting thread is frozen by setting APPDATA.state <= TSTATE_WAITING=5.
        Flag EVENT_WATCHED is set in the event before freeze.
        If flag MANUAL_RESET is NOT set in the event then:
                EVENT_SIGNALED and EVENT_WATCHED are reset when the event is
                received.
                When MANUAL_DESTROY is
                        inactive: the event is destroyed by DestroyEvent,
                        active: the event is moved to ObjList of current thread.
        Gets:
                eax     -- pointer to event
                ebx     -- event id
                ecx     -- timeout, in ticks of system timer
        Returns:
                eax     -- 0 if the event was not activated, or
                           not 0 if activated
        Destroys: eax,ebx,edx,ecx,esi,edi
--------------------------------------------------------------------------------
GetEvent:
        Waits infinitely for any event in the queue of current thread. Thread is
        frozen by setting APPDATA.state <= TSTATE_WAITING = 5. Event data
        (EVENT.code + 5*dword) are copied to specified buffer when received.
        Reset priority byte (see above) in the buffer.
        If flag MANUAL_RESET is NOT set in the event then:
                EVENT_SIGNALED and EVENT_WATCHED are reset when the event is
                received.
                When MANUAL_DESTROY is
                        inactive: the event is destroyed by DestroyEvent,
                        active: the event is moved to ObjList of current thread.
        Gets:
                edi     -- pointer to buffer to copy data
        Returns:
                buffer with following data:
                +0: (EVENT.code) dword: id of following signal data
                +4: (EVENT.data) 5*dword: signal data, format depends on
                    EVENT.code
        Destroys: eax,ebx,edx,ecx,esi,edi
--------------------------------------------------------------------------------
SysFn 68.14 for application:    ; wrapped GetEvent
        Waits infinitely for any event in the queue of current thread. Thread is
        frozen by setting APPDATA.state <= TSTATE_WAITING = 5. Event data
        (EVENT.code + 5*dword) are copied to specified buffer when received.
        Reset priority byte (see above) in the buffer.
        Gets:
                eax     -- 68: function number
                ebx     -- 14: subfunction number
                ecx     -- pointer to data buffer (size is 6*dword)
        Returns:
                ecx = buffer with following data:
                +0: (EVENT.code) dword: id of following signal data
                +4: (EVENT.data) 5*dword: signal data, format depends on
                    EVENT.code
        Destroys:
                eax
