Jump to content

The Event Management Framework

From EDM2
Revision as of 18:24, 12 May 2021 by Ak120 (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
System Object Model Programming Guide
  1. About This Book
  2. Introduction to the SOMobjects Developer Toolkit
  3. Tutorial for Implementing SOM Classes
  4. Using SOM Classes in Client Programs
  5. SOM IDL and the SOM Compiler
  6. Implementing Classes in SOM
  7. Distributed SOM (DSOM)
  8. The SOM Interface Repository Framework
  9. The Metaclass Framework
  10. The Event Management Framework
  11. SOMobjects Error Codes
  12. SOM IDL Language Grammar
  13. Implementing Sockets Subclasses
  14. Glossary

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

The Event Management Framework is a central facility for registering all events of an application. Such a registration facilitates grouping of various application events and waiting on multiple events in a single event-processing loop. This facility is used by the Replication Framework and by DSOM to wait on their respective events of interest. The Event Management Framework must also be used by any interactive application that contains DSOM or replicated objects.

Event Management Basics

The Event Management Framework consists of an Event Manager (EMan) class, a Registration Data class and several Event classes. It provides a way to organize various application events into groups and to process all events in a single event-processing loop. The need for this kind of facility is seen very clearly in interactive applications that also need to process some background events (say, messages arriving from a remote process). Such applications must maintain contact with the user while responding to events coming from other sources.

One solution in a multi-threaded environment is to have a different thread service each different source of events. For a single-threaded environment it should be possible to recognize and process all events of interest in a single main loop. EMan offers precisely this capability. EMan can be useful even when multiple threads are available, because of its simple programming model. It avoids contention for common data objects between EMan event processing and other main-loop processing activity.

Model of EMan usage

The programming model of EMan is similar to that of many GUI toolkits. The main program initializes EMan and then registers interest in various types of events. The main program ends by calling a non-returning function of EMan that waits for events and dispatches them as and when they occur. In short, the model includes steps that:

  1. Initialize the Event Manager,
  2. Register with EMan for all events of interest, and
  3. Hand over control to EMan to loop forever and to dispatch events.

The Event Manager is a SOM object and is an instance of the SOMEEMan class. Since any application requires only one instance of this object, the SOMEEMan class is an instance of the SOMMSingleInstance class. Creation and initialization of the Event Manager is accomplished by a function call to SOMEEmanNew.

Currently, EMan supports the four kinds of events described in the following topic. An application can register or unregister for events in a callback routine (explained below) even after control has been turned over to EMan.

Event types

Event types are categorized as follows:

Timer events
These can be either one-time timers or interval timers.
Sink events (sockets, file descriptors, and message queues)
On AIX, this includes file descriptors for input/output files, sockets, pipes, and message queues. On OS/2 and Windows, only TCP/IP sockets are supported.
Note:On OS/2 and Windows, the Sockets classes for NetBIOS (NBSockets) and Novell IPX/SPX (IPXSockets) are primarily intended for use by DSOM and the Replication Framework, not for general application programming. (The Replication Framework is available as part of the full-capability SOMobjects Developer Toolkit.)
Client events (any event that the application wants to queue with EMan)
These events are defined, created, processed, and destroyed by the application. EMan simply acts as a place to queue these events for processing. EMan dispatches these client events whenever it sees them. Typically, this happens immediately after the event is queued.
Work procedure events (procedures that can be called when there is no other event)
These are typically background procedures that the application intends to execute when there are spare processor cycles. When there are no other events to process, EMan calls all registered work procedures. A work procedure event is called only after all other higher priority events have been called. A work procedure event is not called if there are no other events to be processed.

The Event Management Framework is extendible (that is, other event types can be added to it) through subclassing. The event types currently supported by EMan are at a sufficiently low level so as to enable building other higher level application events on top of them. For example, you can build an X-event handler by simply registering the file descriptor for the X connection with EMan and getting notified when any X-event occurs.

Registration

This topic illustrates how to register for an event type.

Callbacks

The programmer decides what processing needs to be done when an event occurs and then places the appropriate code either in a procedure or in a method of an object. This procedure or method is called a callback. (The callback is provided to EMan at the time of registration and is called by EMan when a registered event occurs.) The signature of a callback is fixed by the framework and must have one of the following three signatures:

void SOMLINK EMRegProc(SOMEEvent, void *);
void SOMLINK EMMethodProc(SOMObject, SOMEEvent, void *);
void SOMLINK EMMethodProcEv(SOMObject, Environment *Ev,
                            SOMEEvent, void *);
/* On OS/2, they all use "system" linkage  */
/* On Windows, the SOMLINK keyword is Not included if the
 * application is intended to support multiple instances. */

The three specified prototypes correspond to a simple callback procedure, a callback method using OIDL call style, and a callback method using IDL call style. The parameter type SOMEEvent refers to an event object passed by EMan to the callback. Event objects are described below.

NOTE
When the callbacks are methods, EMan calls these methods using Name-lookup Resolution (see Chapter 5 Section 5.3 on Method Resolution). One of the implications is that at the time of registration EMan queries the target object's class object to provide a method pointer for the method name supplied to it. Eman uses this pointer for making event callbacks.

Event classes

All event objects are instances of either the SOMEEvent class or a subclass of it. The hierarchy of event classes is as follows:

SOMObject─────────SOMEEvent─────────┼──────────SOMETimerEvent
                                    ├──────────SOMEClientEvent
                                    ├──────────SOMESinkEvent
                                    ├──────────SOMEWorkProcEvent

When called by EMan, a callback expects the appropriate event instance as a parameter. For example, a callback registered for a timer event expects a SOMETimerEvent instance from EMan.

EMan parameters

Several method calls in the Event Management Framework make use of bit masks and constants as parameters (for example, EMSinkEvent or EMInputReadMask). These methods are defined in the include file "eventmsk.h". When a user plans to extend the Event Management Framework, care must be taken to avoid name and value collisions with the definitions in "eventmsk.h". For convenience, the contents of the "eventmsk.h" file are shown below.

#ifndef H_EVENTMASKDEF
#define H_EVENTMASKDEF

/* Event Types */
#define EMTimerEvent                    54
#define EMSignalEvent                   55
#define EMSinkEvent                     56

#define EMWorkProcEvent                 57

#define EMClientEvent                   58

#define EMMsgQEvent                     59

/* Sink input/output condition mask */

#define EMInputReadMask                 (1L<0)
#define EMInputWriteMask                (1L<1)
#define EMInputExceptMask               (1L<2)

/* Process Event mask */

#define EMProcessTimerEvent             (1L<0)
#define EMProcessSinkEvent              (1L<1)
#define EMProcessWorkProcEvent          (1L<2)
#define EMProcessClientEvent            (1L<3)
#define EMProcessAllEvents              (1L<6)

#endif   /* H_EVENTMASKDEF */

Registering for events

In addition to the event classes, the Event Management Framework uses a registration data class (SOMEEMRegisterData) to capture all event-related registration information. The procedure for registering interest in an event is as follows:

  1. Create an instance of the SOMEEMRegisterData class (this will be referred to as a "RegData" object).
  2. Set the event type of "RegData."
  3. Set the various fields of "RegData" to supply information about the particular event for which an interest is being registered.
  4. Call the registration method of EMan, using "RegData" and the callback method information as parameters. The callback information varies, depending upon whether it is a simple procedure, a method called using OIDL call style, or a method called using IDL call style.

The following code segment illustrates how to register input interest in a socket "sock" and provide a callback procedure "ReadMsg".

data = SOMEEMRegisterDataNew( );       /* create a RegData object */
_someClearRegData(data, Ev);
_someSetRegDataEventMask(data,Ev,EMSinkEvent,NULL); /* Event type */
_someSetRegDataSink(data, Ev, sock);     /* provide the socket id */
_someSetRegDataSinkMask(data,Ev, EMInputReadMask );
                                                 /*input interest */
regId = _someRegisterProc(some_gEMan,Ev,data,ReadMsg,"UserData" );
/* some_gEMan points to EMan. The last parameter "userData" is any
   data the user wants to be passed to the callback procedure as a
   second parameter */

Unregistering for events

One can unregister interest in a given event type at any time. To unregister, you must provide the registration id returned by EMan at the time of registration. Unregistering a non-existent event (such as, an invalid registration id) is a no-op. The following example unregisters the socket registered above:

_someUnRegister(some_gEMan, Ev, regId);

An example callback procedure

The following code segment illustrates how to write a callback procedure:

void SOMLINK ReadMsg( SOMEEvent  event, void  *targetData )
{
int sock;
    printf( "Data = %s\n", targetData );
    switch( _somevGetEventType( event )) {
    case  EMSinkEvent:
        printf("callback: Perceived Sink Event\n");
        sock = _somevGetEventSink(event);
        /* code to read the message off the socket */
        break;
    default: printf("Unknown Event type in socket callback\n");
    }
}
/* On OS/2, "system" linkage is also required.  */
/* On Windows, callbacks do not use the SOMLINK keyword if
 * the application is intended to support multiple instances. */

Generating client events

While the other events are caused by the operating system (for example, Timer), by I/O devices, or by external processes, client events are caused by the application itself. The application creates these events and enqueues them with EMan. When client events are dispatched, they are processed in a callback routine just like any other event. The following code segment illustrates how to create and enqueue client events.

clientEvent1 = SOMEClientEventNew();  /* create a client event */
_somevSetEventClientType( clientEvent1, Ev, "MyClientType" );
_somevSetEventClientData( clientEvent1, Ev, "I can give any data here");
/* assuming that "MyClientType" is already registered with EMan */
/* enqueue the above event with EMan */
_someQueueEvent(some_gEMan, Ev, clientEvent1);

Examples of using other events

The sample program shipped with the Event Management Framework illustrates the tasks listed below. (Due to its large size, the source code is not included here.)

  • Registering and unregistering for Timer events.
  • Registering and unregistering for Workproc events.
  • Registering an AIX Message Queue, sending messages on it, and unregistering the Message Queue.
  • Registering a stream socket that listens to incoming connection requests. Also, sockets connecting, accepting a connection, and sending/receiving messages through EMan.
  • Registering a file descriptor on AIX and reading one line of the file at a time in a callback.

Processing events

After all registrations are finished, an application typically turns over control to EMan and is completely event driven thereafter. Typically, an application main program ends with the following call to EMan:

_someProcessEvents(some_gEMan, Ev);

An equivalent way to process events is to write a main loop and call someProcessEvent from inside the main loop, as indicated:

while (1) { /* do forever */
      _someProcessEvent( some_gEMan, Ev, EMProcessTimerEvent  Æ
                                         EMProcessSinkEvent   Æ
                                         EMProcessClientEvent Æ
                                         EMProcessWorkProcEvent );
      /***  Do other main loop work, as needed. ***/
}

The second way allows more precise control over what type of events to process in each call. The example above enables all four types to be processed. The required subset is formed by logically OR'ing the appropriate bit constants (these are defined in "eventmsk.h)". Another difference is that the second way is a non-blocking call to EMan. That is, if there are no events to process, control returns to the main loop immediately, whereas someProcessEvents is a non-returning blocking call. For most applications, the first way of calling EMan is better, since it does not waste processor cycles when there are no events to process.

Interactive applications

Interactive applications need special attention when coupled with EMan. Once control is turned over to EMan by calling someProcessEvents, a single-threaded application (for example, on AIX) has no way of responding to keyboard input. The user must register interest in "stdin" with EMan and provide a callback function that handles keyboard input. In a multi-threaded environment (for example, OS/2), this problem can be solved by spawning a thread to execute someProcessEvents and another to handle keyboard input. (These two options are illustrated in the sample program shipped with the Event Management Framework.)

Event Manager Advanced Topics

This section contains the following subjects:

  • Threads and thread safety
  • Writing an X or MOTIF application
  • Extending EMan
  • Using EMan from C++
  • Using EMan from other languages
  • Tips on using EMan

Threads and thread safety

As indicated earlier, on OS/2, interactive programs call someProcessEvents in one thread and process keyboard input in a separate thread. (This recommended usage is illustrated in the sample program). The event manager object (EMan) is thread safe in the sense that concurrent method invocations on EMan are serialized. Even when someProcessEvents is invoked in a thread and other methods of EMan are invoked from other threads, EMan still preserves its data integrity. However, when Eman dispatches an event, a callback can call methods on the same data objects as the other interactive thread(s). The user must protect such data objects using appropriate concurrency control techniques (for example by using semaphores).

One must also be aware of some deadlock possibilities. Consider the following situation. EMan code holds some SOMobjects Toolkit semaphores while it is running (for example, while in someProcessEvents). A user-defined object protects its data by requiring its methods to acquire and release a sempahore on the object. If a separate thread running in this object were to call an operation that requires a SOMobjects Toolkit semaphore (which is currently held by EMan) and if concurrently EMan dispatches an event whose callback invokes a method of this object, a deadlock occurs. Two possibilities exist to cope with such a situation: One is to acquire all needed semaphores ahead of time, and the other is to abort the operation when you fail to obtain a semaphore. To achieve mutual exclusion with EMan, you can call the methods someGetEManSem and someReleaseEmanSem. These methods acquire and release the SOMobject Developer Toolkit semaphores that EMan uses.

Writing an X or MOTIF application

Although the Event Manager does not recognize X events, an X or MOTIF application can be integrated with EMan as follows. First, the necessary initialization of X or MOTIF should be performed. Next, using the Xlib macro "ConnectionNumber" or the "XConnectionNumber" function, you can obtain the file descriptor of the X connection. This file descriptor can be registered with EMan as a sink. It can be registered for both input events and exception events. When there is any activity on this X file descriptor, the developer-provided callback is invoked. The callback can receive the X-event, analyze it, and do further dispatching. See the example program in Chapter 9, "The Replication Framework" (section 9.7).

Extending EMan

The current event manager can be extended without having access to the source code. The use of EMan in an X or MOTIF application mentioned above is just one such example. Several other extensions are possible. For example, new event types can be defined by subclassing either directly from SOMEEvent class or from any of its subclasses in the framework. There are three main problems to solve in adding a new event type:

  • How to register a new event type with EMan?
  • How to make EMan recognize the occurrence of the new event?
  • How to make EMan create and send the new event object (a subclass of SOMEEvent) to the callback when the event is dispatched?

Because the registration information is supplied with appropriate "set" methods of a RegData object, the RegData object should be extended to include additional methods. This can be achieved by subclassing from SOMEEMRegisterData and building a new registration data class that has methods to "set" and "get" additional fields of information that are needed to describe the new event types fully. To handle registrations with instances of new registration data subclass, we must also subclass from SOMEEMan and override the someRegister and the someUnRegister methods. These methods should handle the information in the new fields introduced by the new registration data class and call parent methods to handle the rest.

Making EMan recognize the occurrence of the new event is primarily limited by the primitive events EMan can wait on. Thus the new event would have to be wrapped in a primitive event that EMan can recognize. For example, to wait on a message queue on OS/2 concurrently with other EMan events, a separate thread can be made to wait on the message queue and to enqueue a client event with EMan when a message arrives on this message queue. We can thus bring multiple event sources into the single EMan main loop.

The third problem of creating new event objects unknown to EMan can be easily done by applying the previous technique of wrapping the new event in terms of a known event. In a callback routine of the known event, we can create and dispatch the new event unknown to EMan. Of course, this does introduce an intermediate callback routine which would not be needed if EMan directly understood the new event type.

A general way of extending EMan is to look for newly defined event types by overriding someProcessEvent and someProcessEvents in a subclass of EMan.

Using EMan from C++

The Event Management framework can be used from C++ just like any other framework in the SOMobjects Toolkit. You must ensure that the C++ usage bindings (that is, the .xh files) are available for the Event Management Framework classes. These .xh files are generated by the SOM Compiler in the SOMobjects Toolkit when the -s option includes an xh emitter.

Using EMan from other languages

The event manager and the other classes can be used from other languages, provided usage bindings are available for them. These usage bindings are produced from .idl files of the framework classes by the appropriate language emitter.

Tips on using EMan

The following are some do's and don'ts for EMan:

  • Eman callback procedures or methods must return quickly. You cannot wait for long periods of time to return from the callbacks. If such long delays occur, then the application may not notice some subsequent events in time to process them meaningfully (for example, a timer event may not be noticed until long after it occurred).
  • It follows from the previous tip that you should not do independent "select" system calls on file descriptors while inside a callback. (This applies to sockets and message queues, as well.) In general, a callback should not do any blocking of system calls. If an application must do this, then it must be done with a small timeout value.
  • Since EMan callbacks must return quickly, no callback should wait on a semaphore indefinitely. If a callback has to obtain some semaphores during its processing, then the callback should try to acquire all of them at the very beginning, and should be prepared to abort and return to EMan if it fails to acquire the necessary semaphores.
  • EMan callback methods are called using name-lookup resolution. Therefore, the parameters to an EMan registration call must be such that the class object of the object parameter must be able to provide a pointer to the method indicated by the method parameter. Although this requirement is satisfied in a majority of cases, there are exceptions. For example, if the object is a proxy (in the DSOM sense) to a remote object, then the "real" class object cannot provide a meaningful method pointer. Also note that, when somDspatch is overridden, the effect of such an override will not apply to the callback from EMan. Do not use a method callback in these situations; instead, use a procedure callback.

Limitations

The present implementation of the Event Management framework has the limitations described below. For a more up-to-date list of limitations, refer to the README file on EMan in the SOMobjects Developer Toolkit.

  • EMan supports registering a maximum of 64 AIX message queues.
  • EMan can only wait on file descriptors (including files, pipes, sockets, and message queues) on AIX, and socket identifiers on OS/2.
  • EMan supports registering a maximum of FILENO (the AIX limit on maximum number of open files) file descriptors on AIX, and FD_SETSIZE socket identifiers on OS/2 (FD_SETSIZE is defined by the TCP/IP product).

Use of EMan DLL

The Event Manager Framework uses a Sockets "select" call to wait on multiple sockets. At the time of EMan creation, the SOMEEMan class object loads one of the Sockets subclass DLLs, based on the value of the environment variable SOMSOCKETS. This environment variable should name the implementation class of sockets (see Appendix E describing the Sockets abstract class and the specific implementation DLLs available with the SOMobjects Toolkit.) The current choices for this environment variable are TCPIPSockets, (and TCPIPSockets32 for OS/2), NBSockets, and IPXSockets.