Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Ensure singleton execution via Win32's synchronization objects

2.85/5 (6 votes)
11 Aug 2008CPOL4 min read 1   221  
Synchronization object handle: another mechanism for singleton run.

Introduction

There are many scenarios that require a singleton run, meaning to enforce only one instance of a program to be active. Traditionally, there are several techniques to prevent a second instance from being loaded. To list a few:

  1. use a global static flag
  2. check the window title
  3. create a file lock

This article will explore a different mechanism on the Windows platform, using synchronization objects, namely Events, Mutexes, and Semaphores, to vouch for the singleton run of a program.

Background

Although mutexes/semaphores/events were originally conceived to solve the problem of race conditions and dead-locks among multi-threaded programs, their creation on Windows has one inherent attribute: any named object under identical conditions can only be created once in the global namespace. This globally unique handle (although Win32 provides APIs to duplicate the handles) serves as the equivalent of an advisory lock. When the lock is honored by all observant parties, it works essentially as an unequivocal yardstick for the first running instance.

Using the Code

There are three pairs of CPP and header files for mutexes, semaphores, and events, respectively. Include any one pair, along with the base class into your project, and you will get the effect of a singleton run for your program.

The included test harness provides a sample on how to use these utility classes.

C++
CSingleton *pSingleton = NULL;
switch( type )
{
    case eMutex:
      pSingleton = new CMutexSingleton(MUTEX_NAME, NULL);
      if( pSingleton )
      {
          if( pSingleton->IsExclusiveRun() )
           {
              bWait = true;
               printf("Running (mutex singleton)");
           }
           else
           {
               printf("another instance (mutex) is running, let's go home\n");
           }
       }
       break;

Points of Interest

A thorough discussion of synchronization demands volumes of writing. This article focuses on one aspect: synchronization object creation, and segway the object handle after it has been created, to the area of our interest: singleton-run. Along the way, I will try to answer, in plain language, some related questions puzzling those whose native tongue is neither English nor C++ (author admits to be one of them:-)):

  1. What is signaling? Is signaling of a mutex the same as signaling of a semaphore or event?
  2. After a mutex has been signaled, what happens next?
  3. In the similar token, how about the un-signaling of those objects?
  4. Can I use Critical Section to achieve the same result as what mutex/semaphore/event does?

Among multiple sources, on-line or in print, I think Julian Templeman has the best explanation for signaling and un-signaling. In his book "Beginning Windows NT programming", page 222, there's a table listing various object types and the corresponding meanings of "signaling".

A signalled mutex means is not owned by any process. In other words, a signaled mutex is the one available for grab. Counter-intuitive? That is the way how it is structured. When a semaphore is signaled, it means its count is greater than zero. The common folklore is that semaphore is an n-way mutex. To put it differently, mutex is a special case of semaphore, whose count is 1. Julian has pointed out one subtle difference between a semaphore of count 1 and a mutex; that is, "In Win32,a semaphore with a count of one has no WAIT_ABANDONED return value". This obscure behavoir is important when someone later calls WaitForXXXX on a semaphore with a count of 1. Do not expect to get WAIT_ABANDONED on that semaphore handle.

Of the three synchronization objects, the business of event signaling/un-signaling is the most vexing:

After an event is created via standard Win32 CreateEvent, one of the following needs to be called:

  • SetEvent()
  • ResetEvent()
  • PulseEvent()

Since an event could be created with either manual or automatic, and its initial state could be either signaled or un-signaled, let's examine which call above is used to properly set the event.

On an automatic event: SetEvent() and PulseEvent have identical effect, they set the event to signaled, then back to non-signaled, waking up a single waiting thread (Beginning Window NT programming page 251).

On a manual event: SetEvent() sets the event ot signaled and leaves it there; ResetEvent() sets the event to non-signaled and leaves it there; PulseEvent() sets the event to signaled and then back to non-signaled, waking up all waiting threads (source: ditto).

In short, if you want to create an event and hold on to it, create a manual one and call ResetEvent() on its handle.

Un-signaling has the opposite effect: unsignal a mutex means it is no longer available, and un-signaling a semaphore indicates its count is back to 0. Finally, un-signaling an event simply means it is unset from its previous state.

Critical Section is another widely used mechanism for synchronization. Unlike the three above, it is merely a structure, not a kernel object, visible within the creation process. What this article ultimately wants to do is to achieve singleton run on a per machine basis. Hence, Critical Section is not suitable for this task on a machine wide, global namespace.

History

  • Initial submission: August 11, 2008.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)