Event Manager Interface
Event manager interface was introduced to generalize I/O multiplexing and close functionality.
It is portable across platforms, but interface might be slightly different. Read/Write indications
and timers are supported on all platforms, while signals are specific to UNIX and WSA indications,
events, window messages are specific to windows.
Main design goal of this component is to provide generic, platform-independent way of managing
thread's main loop. This interface was introduced to minimize software component's knowledge
about particular event manager implementation.
Object model is comprised of main event manager component and several event objects, that can be
registered/de-registered with event manager. Every event object has bound type, describing the nature
of the event. Currently supported event types are read/write indications on I/O descriptors, timers,
signals, events, window messages.
Event object also has callback interface. This interface is used to notify the user, that requested
event occurred. This interface is normally implemented by client code, interested in receiving
notifications.
The type of the event and callback interface implementation are bound to event object at construction
time. But the fact whether this callback is invoked is controlled by presence of the event object
in particular event manager. And generally speaking, single instance of event object should be bound
to single event manager, i.e. it is not recommended to have single event object, registered with different
event managers (i.e. in different threads). This would require extra synchronization, that may have
an impact on performance with a small functional improvement.
Important property of the event object of any type is persistence, i.e. if event object is registered
with event manager, it will fire events according to its type as long as it remains registered and specified
condition is met. For example timer will invoke callback interface every period T. There is no need
to re-activate event object on every callback execution.
Event manager abstract class is IEventManagerInterface. In addition to pure virtual functions, it also
has several typedefs:
- IEventManagerInterface::CEventObject - event object class
- IEventManagerInterface::CCallback - user callback interface
-
IEventManagerInterface::ERequestedEvent - enumeration of all supported event types. Currently
supported values are:
-
eTimer - fired when specified timeout period expires.
- eDescriptorRead - fired when specified I/O descriptor has read condition.
- eDescriptorWrite - fired when specified I/O descriptor has write condition.
-
eDescriptorConnect - windows-specific. Fired when specified socket is in connect-completed
state.
-
eDescriptorAccept - windows-specific. Fired when specified listening socket has
pending connection.
-
eDescriptorClose - windows-specific. Fired when specified socket's disconnection detected.
-
eAsyncFlag - unix-specific. Fired when specified flag variable has been turned on. Used
in conjunction with signals. It is not always safe to perform complex manipulations inside signal
handler. It is safe to assign a value to volatile variable although. Later, when main thread resumes,
it can detect this change after terminated system call, where corresponding callback is fired.
-
eWindowsEventObject - windows-specific. Fired when specified event is in signaled state.
Note that implementation does not clear signaled state, this is responsibility of the user code.
-
eWindowsThreadMsgClass - windows-specific. Fired when new message is detected in
the thread's message queue.
These constants are exported via IEventManagerInterface::CEventObject typedef. For example
IEventManagerInterface::CEventObject::eDescriptorRead.
Back to the top ↑
Lifetime. Event objects are normally instantiated in client code and kept alive as long as
there is an interest in corresponding event.
When event object is being registered in event manager, the
copy of supplied object is stored internally.
There is a reason to keep the instance of event object outside event manager even after event object is
registered, because event object itself is key for removal.
Event object may be altered; for example,
internally stored socket registered as read interest can be changed. However if change operation applies
to user copy of event object, it will not automatically alter the copy, stored in event manager.
This change can be activated by either removing/inserting event object or by altering the copy stored
inside event manager. This copy can be accessed via the reference, returned from AddEvent call.
Generally event object can be thought as a placeholder for the following concepts:
-
Callback interface storage. Event object is associated with callback interface
implementation at construction time. This interface is invoked when corresponding event arrives.
-
Unique interest identification. When callback interface method is called, user code receives a reference
to the event object instance, stored in the event manager internals. This reference be used for
identification of the event, which caused callback invocation. Every event object has bound identifier,
but it is meaningful only to particular event manager and allocated on event object insertion.
Event object exports following types:
-
io_descriptor - platform-specific type, representing I/O descriptor
Event object has the following methods (IEventManagerInterface is omitted for clarity):
-
CEventObject(); Default constructor; mainly introduced to cover default-constructible requirement of many
STL containers.
-
CEventObject(ERequestedEvent _eEventRequest,CCallback* _pCallbackInterface);
Constructor bounds event type and callback interface implementation to event object.
These are invariants and cannot be changed after construction.
Every event object is uniquely identified by bound integer value. Unique value is assigned to every
instance when it is registered with event manager. Copy or assignment does not generate new identifier.
Method returns current value of the unique identifier:
TIdentifier GetEventObjectID()const throw();
Following methods allow comparison of different instances:
- bool operator== (const CEventObjectType& _crefEventObject) const throw();
- bool operator!= (const CEventObjectType& _crefEventObject) const throw();
- bool operator< (const CEventObjectType& _crefEventObject) const throw();
Method returns the type, bound to event object:
EInternalRequestedEvent GetEventType() const throw();
These methods are specific to particular types of event object:
-
void SetTimerInterval(unsigned long _ulTimerIntervalSeconds, unsigned long _ulTimerIntervalMicroSec) throw();
eTimer-specific method. Requests callback method to be invoked every _ulTimerIntervalSeconds and
_ulTimerIntervalMicroSec. The second parameter is rounded if microsecond precision is not available
in the system.
-
CTimeVal GetTimerPeriod() const throw();
eTimer-specific method. Returns previously assigned timeout value.
-
CTimeVal GetTimerLastFireTime() const throw();
eTimer-specific method. Returns the timestamp of the last callback execution
-
void SetFileDescriptor(io_descriptor _ifd) throw();
Used when declared event object type is one of eDescriptorRead, eDescriptorWrite,
eDescriptorConnect, eDescriptorAccept, eDescriptorClose. Method assigns specified I/O descriptor
to event object.
-
void SetFileDescriptor(io_descriptor _ifd) throw();
Returns previously assigned descriptor.
-
void SetWindowsEvent(HANDLE _hExternalEvent) throw();
Used with eWindowsEventObject event type. Windows-specific. Assigns supplied system event handle
to event object. Callback is fired when signaled state is detected.
-
HANDLE GetWindowsEventObject() const throw();
Used with eWindowsEventObject event type. Windows-specific.
Returns the handle of the currently bound windows event object
-
void SetAsyncFlag(volatile bool& _refAsyncFlag) throw();
eAsyncFlag-specific call. Requests event object for notification when specified flag is
signaled asynchronously. This functionality is used with unix signals to provide safe way
for controlling asynchronous notifications, by "converting" asynchronous interrupts into
synchronous and C++-friendly. This functionality requires certain support from the environment:
-
Signal handler should be implemented in user code and assign "true" to controlled
variable when software interrupt occurred.
-
Destination signal should be configured in OS so that system calls are not automatically restarted.
This may add certain complexity to application, but it provides a way for event manager to
detect the signal.
-
There is no need to either initialize or to reset controlled flag in user code; this should
be done in event manager implementation.
-
bool GetAsyncFlagValue() const throw();
eAsyncFlag-specific call. Returns current value of the registered async
notification flag.
-
void SetWindowsThreadMsgMask(DWORD _dwMessageWakeMask) throw();
Windows-specific. Parameter specifies the mask of windows message categories that
are of interest to user. The mask can be constructed using QS_* constants, defining
message queue status.
-
DWORD GetWindowsThreadMsgMask() const throw();
Windows-specific. Returns a mask of previously assigned windows messages to notify about.
Note that all event object calls, described above, are specific to particular subset of event object
types. If event object was constructed with the the type, incompatible with called function,
the result is undefined. In particular, debug assertion is thrown and such behavior may result in
malfunction in release version, or at least in improper behavior.
Back to the top ↑
Callback interface has embedded enumeration ECallbackStatus, representing possible return
values from callback method.
Callback method has the following prototype:
virtual ECallbackStatus OnEvent(const CEventObjectType& _crefEventObject) = 0;
User class normally derives from callback interface and overrides this method, providing a reference
to implementation during event object construction. When requested event occurs, overridden callback
method is called, supplying a reference to event object, stored inside event manager. This reference
can be used to make a difference between objects, which may cause particular callback execution.
This gives an ability to use single sink method to catch events bound to different event objects.
Callback interface method has return value. This value is used to say event manager whether subsequent
event notifications are required or not. If callback returns eSucc constant, event manager should
continue notifications for target event object. Otherwise eRemoveCorrEventObject is returned and
event manager should de-register event object and stop notifications. Note that the same behavior
can be achieved by calling event manager's RemoveEvent method. Note that it is safe to perform
several RemoveEvent invocations from inside callback method, de-registering different event objects.
But beware of removing target event object with RemoveEvent and returning eRemoveCorrEventObject at the
same time. This is not guaranteed to work.
eRemoveCorrEventObject also has a special mission: it was introduced to optimize
removal process. When RemoveEvent method is called, implementation is likely
to search through internal structures to locate event object to be removed. On the other
side event manager can optimize removal process when eRemoveCorrEventObject is returned
from callback method, because it already has positioned iterator.
Back to the top ↑
Event manager interface defines following pure virtual methods:
-
virtual CEventObject& AddEvent(CEventObject& _refEventObject) = 0;
AddEvent method registers supplied event object. It stores fully identical
copy in the internal structures, returning a reference to copy. This reference
may be used to alter event object. It is guaranteed to be valid as long
as corresponding event object remains registered. This method can be called
from callback context, but there is a limitation: it should not be called
twice; i.e. duplicate registration of same event object is not allowed.
Note that parameter is passed by reference. Mutable reference is given here
intentionally: the main goal for event manager implementation is to call
AssignUniqueIdentifier method for both - user and internal copies. AddEvent
implementation must allocate unique (within particular event manager instance)
identifier and refer added event object using this value. From user perspective
this means that event object is meaningless until it is registered.
-
virtual void RemoveEvent(CEventObject& _refEventObject)
= 0;
This method removes event object from event manager
internals and stops corresponding notifications. It can be
called from callback context. Attempt to remove not registered
event object is not considered erroneous. This freedom is
given to allow plain cleanup procedures implementation. In
addition to removal from internal structures, the parameter is
also used to reset previously allocated identifier for event
object to zero. Hence freed identifier can be reused later.
-
virtual void Start() = 0;
The heart of event manager. This call starts thread's main loop. It never returns unless
thread/application termination function is called or exception is thrown.
-
virtual size_t Capacity(ERequestedEvent _eEventType) const = 0;
Sometimes number of operating system resources, used by event manager, is limited.
So this method can be used to check how many additional event objects of particular
type can be registered.
Event manager interface is dependent on system-specific functionality for handling I/O multiplexing,
events, windows messages, time support.... cassert functionality is used to diagnose improper
usage on early stage of development.
Back to the top ↑
History:
-
May 14 2002. Event object was extended to support windows message queue
update notifications. Now windows event manager is able to serve GUI threads as well.
This section is primary intended for event manager interface implementors and
can be safely skipped if new implementation is not planned.
Event object class has additional interface for exclusive use by event manager
implementation. This interface is comprised of the following methods:
-
IInternalCallbackInterface::ECallbackStatus CheckTimeAndFire();
eTimer-specific method. Checks whether specified period expired and
calls callback interface method of the event object.
-
IInternalCallbackInterface::ECallbackStatus CheckFlagAndFire();
eAsyncFlag-specific method. Checks whether signal-triggered flag is set
and calls registered callback method.
-
IInternalCallbackInterface::ECallbackStatus Fire();
Method unconditionally executes callback interface method.
-
void SetCurrentTime();
Normally used by eTimer-specific functionality of event manager.
Sets current time to internally stored member.
-
void AssignUniqueIdentifier(TIdentifier _tIdentifier);
Event manager is required to implement unique identifier allocation
for every event object. This method is intended for use from event manager's
AddEvent method when unique identifier is allocated to synchronize this value
between internal and user copy of event object.
Note that value 0 is reserved to identify event object, that is not bound
to particular event manager. AddEvent method should assign non-zero value,
while RemoveEvent method should reset this to 0. This adds safety when
previously allocated identifier is reused.
Interface header is created to facilitate integration
of event manager functionality into independent components. Event manager implementations currently
exist for Windows and UNIX platforms and based on rich experience of our team in developing robust
middle-ware applications.
Back to the top ↑
Last modified: Wed Jun 12 10:55:44 JST 2002
© Orchid Technology K.K. 2002 All rights reserved