Porting WaitForSingleObject to Linux - Part 2

In my last post I discussed the use of WaitForSingleObject in relation to mutexes and possible ways to implement equivalent functionality when porting such code to GNU/Linux.  In this post I will describe the use of this API with event objects in Microsoft Windows and suggest possible ways of posting such code to GNU/Linux or Unix.

First, some background on event objects.  An event object is just another type of Windows kernel dispatcher object.  From a coding prespective, an event object is a synchronization object which encapsulates one or more kernel dispatcher objects and whose synchronization semantics are accessable via WaitForSingleObject and its cousins.  At any given time a synchronization object is either nonsignaled or signaled, i.e. the object can only be in one of two possible states.

All of the WaitFor family of APIs including WaitForSingleobject wait on an object handle or handles until some specified criteria is met.  The two basic criteria for all these APIs are the signaled state of the object on whose handle it is waiting and a time-out value.  Thus a thread which calls this API waits till the specified object enters the signaled state or the specified time-out has expired.  Little or no CPU time is used when such a thread is in the wait state.

In the case of events, a CreateEvent or OpenEvent returns a handle to an event object.  When an event is in the signaled state it means that that the event has the capacity to release one or more threads waiting for this particular event to be signaled.  When an event is in the nonsignaled state it will not release any waiting thread.  Initially the state of an event is nonsignaled.  An event object's state is set explicitly to signaled by SetEvent or PulseEvent.  Event objects are also used in overlapped operations such as reading from a socket, in which case the event object state is set to signaled by the kernel rather than by an application.

Events also come in two reset types.  If an event is a manual-reset event, then all WaitForSingleObjects return that wait for that event if so configured.  In other words a manual-reset event can trigger action by one or more WaitForSingleObject or its cousins.  A manual-reset event object's state must be reset explicitly to nonsignaled by ResetEvent.

For an auto-reset event object, WaitForSingleObject and it's relations reset the state of the event object to nonsignaled before returning.  While an auto-reset event is guaranteed to set the event to nonsignaled and release a single thread that is waiting on the event to occu, if more than one thread is waiting for this particular event to occur then which particular thread is released is random.

Consider the following skeleton Microsoft Windows application.
HANDLE hEvent;
HANDLE hMutex;
HANLE hThread;
....
BOOLEAN Run = TRUE;
....

main()
{

hMutex = CreateMutex(0, FALSE, NULL);
hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
.....
hThread = CreateThread( NULL, 0 (LPTHREAD_START_ROUTINE)SendThread, 0, 0, 0);
.....

/* produce and post messages */
.....

/* clean up and exit */
CloseHandle(hEvent);
CloseHandle(hMutex);
}


DWORD SendThread (LPVOID Parm)
{
do {
/* wait for the event */
WaitForSingleObject( hEvent, INFINITE );

/* send the message */
....
....
/* lock message queue and delete message */
WaitForSingleObject( hMutex, INFINITE );
.....
.....
ReleaseMutex( hMutex );

} while ( Run };

}

PostMessage( PMESSAGE Message )
{
/* lock message queue and add message */
WaitForSingleObject( hMutex, INFINITE );
.....
.....
ReleaseMutex( hMutex );

/* signal SendThread that there is a message in the queue */
SetEvent( hEvent );
}
Assume that the purpose of this application, for whatever reason, is to broadcast UDP messages.  A separate thread of execution, i.e SendThread, is created to handle the actual broadcasting.  It simple takes whatever messages are in the send message queue amd broadcasts them.  If there are no messages in the message queue, the thread waits (yields) until it is signalled that there is one or more messages waiting to be broadcast.

Here is the same application code after it was ported to GNU/Linux using POSIX theads.
pthread_cond_t   hEvent;
pthread_mutex_t hMutex;
pthread_t hThread;
....
BOOLEAN Run = TRUE;
....

main()
{
pthread_mutexattr_t mattr;
pthread_attr_t attr;

/* initialize condition variable */
pthread_cond_init( &hEvent, NULL);

/* initialize mutex as a recursive type */
pthread_mutexattr_init( &mattr );
pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_RECURSIVE );
pthread_mutex_init( &hMutex, &mattr );

/* create thread as detached and process scope */
pthread_attr_init( &attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);
pthread_create( &hThread, &attr, SendThread, 0);

.....
/* produce and post messages */
.....

/* clean up and exit */
pthread_attr_destroy( &attr );
pthread_cond_destroy( &hEvent );
pthread_mutex_destroy( &hMutex );
}

void *
SendThread ( void *Parm )
{
do {
/* wait for the event */
pthread_mutex_lock( &hMutex );
pthread_cond_wait( &hEvent, &hMutex );
pthread_mutex_unlock( &hMutex );

/* send the message */
....
....

/* lock message queue and delete message */
pthread_mutex_lock( &hMutex );
.....
.....
pthread_mutex_unlock( &hMutex );

} while ( Run };

}

PostMessage( PMESSAGE Message )
{
/* lock message queue and add message */
pthread_mutex_lock( &hMutex );
.....
.....
pthread_cond_signal( &hEvent ); /* signal SendThread */
pthread_mutex_unlock( &hMutex );

}






*** UNDER CONSTRUCTION ***
 

0 comments:

Post a Comment