Home | History | Annotate | Download | only in win32
      1 /* win_ce_semaphore.c
      2 
      3    Copyright (c) 1998, Johnson M. Hart
      4    (with corrections 2001 by Rainer Loritz)
      5    Permission is granted for any and all use providing that this
      6    copyright is properly acknowledged.
      7    There are no assurances of suitability for any use whatsoever.
      8 
      9    WINDOWS CE: There is a collection of Windows CE functions to simulate
     10    semaphores using only a mutex and an event. As Windows CE events cannot
     11    be named, these simulated semaphores cannot be named either.
     12 
     13    Implementation notes:
     14    1. All required internal data structures are allocated on the process's heap.
     15    2. Where appropriate, a new error code is returned (see the header
     16       file), or, if the error is a Win32 error, that code is unchanged.
     17    3. Notice the new handle type "SYNCHHANDLE" that has handles, counters,
     18       and other information. This structure will grow as new objects are added
     19       to this set; some members are specific to only one or two of the objects.
     20    4. Mutexes are used for critical sections. These could be replaced with
     21       CRITICAL_SECTION objects but then this would give up the time out
     22       capability.
     23    5. The implementation shows several interesting aspects of synchronization, some
     24       of which are specific to Win32 and some of which are general. These are pointed
     25       out in the comments as appropriate.
     26    6. The wait function emulates WaitForSingleObject only. An emulation of
     27       WaitForMultipleObjects is much harder to implement outside the kernel,
     28       and it is not clear how to handle a mixture of WCE semaphores and normal
     29       events and mutexes. */
     30 
     31 #define WIN32_LEAN_AND_MEAN
     32 #include <windows.h>
     33 
     34 #include "win_ce_semaphore.h"
     35 
     36 static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags);
     37 
     38 SYNCHHANDLE CreateSemaphoreCE (
     39 
     40    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,  /* pointer to security attributes */
     41       LONG lInitialCount,   /* initial count */
     42       LONG lMaximumCount,   /* maximum count */
     43       LPCTSTR lpName )
     44 
     45 /* Semaphore for use with Windows CE that does not support them directly.
     46    Requires a counter, a mutex to protect the counter, and an
     47    autoreset event.
     48 
     49    Here are the rules that must always hold between the autoreset event
     50    and the mutex (any violation of these rules by the CE semaphore functions
     51    will, in all likelihood, result in a defect):
     52     1. No thread can set, pulse, or reset the event,
     53        nor can it access any part of the SYNCHHANDLE structure,
     54        without first gaining ownership of the mutex.
     55        BUT, a thread can wait on the event without owning the mutex
     56        (this is clearly necessary or else the event could never be set).
     57     2. The event is in a signaled state if and only if the current semaphore
     58        count ("CurCount") is greater than zero.
     59     3. The semaphore count is always >= 0 and <= the maximum count */
     60 
     61 {
     62    SYNCHHANDLE hSynch = NULL, result = NULL;
     63 
     64    __try
     65 	{
     66       if (lInitialCount > lMaximumCount || lMaximumCount < 0 || lInitialCount < 0)
     67 	  {
     68               /* Bad parameters */
     69          SetLastError (SYNCH_ERROR);
     70          __leave;
     71       }
     72 
     73       hSynch = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, SYNCH_HANDLE_SIZE);
     74       if (hSynch == NULL) __leave;
     75 
     76       hSynch->MaxCount = lMaximumCount;
     77       hSynch->CurCount = lInitialCount;
     78       hSynch->lpName = lpName;
     79 
     80       hSynch->hMutex = CreateMutex (lpSemaphoreAttributes, FALSE, NULL);
     81 
     82       WaitForSingleObject (hSynch->hMutex, INFINITE);
     83       /*  Create the event. It is initially signaled if and only if the
     84           initial count is > 0 */
     85       hSynch->hEvent = CreateEvent (lpSemaphoreAttributes, FALSE,
     86               lInitialCount > 0, NULL);
     87       ReleaseMutex (hSynch->hMutex);
     88       hSynch->hSemph = NULL;
     89    }
     90    __finally
     91    {
     92        /* Return with the handle, or, if there was any error, return
     93         a null after closing any open handles and freeing any allocated memory. */
     94       result=CleanUp(hSynch, 6 /* An event and a mutex, but no semaphore. */);
     95    }
     96 
     97    return result;
     98 }
     99 
    100 BOOL ReleaseSemaphoreCE (SYNCHHANDLE hSemCE, LONG cReleaseCount, LPLONG lpPreviousCount)
    101 /* Windows CE equivalent to ReleaseSemaphore. */
    102 {
    103    BOOL Result = TRUE;
    104 
    105    /* Gain access to the object to assure that the release count
    106       would not cause the total count to exceed the maximum. */
    107 
    108    __try
    109    {
    110       WaitForSingleObject (hSemCE->hMutex, INFINITE);
    111 	  /* reply only if asked to */
    112 	  if (lpPreviousCount!=NULL)
    113 		 *lpPreviousCount = hSemCE->CurCount;
    114       if (hSemCE->CurCount + cReleaseCount > hSemCE->MaxCount || cReleaseCount <= 0)
    115 	  {
    116          SetLastError (SYNCH_ERROR);
    117          Result = FALSE;
    118          __leave;
    119       }
    120       hSemCE->CurCount += cReleaseCount;
    121 
    122       /*  Set the autoreset event, releasing exactly one waiting thread, now or
    123           in the future.  */
    124 
    125       SetEvent (hSemCE->hEvent);
    126    }
    127    __finally
    128    {
    129       ReleaseMutex (hSemCE->hMutex);
    130    }
    131 
    132    return Result;
    133 }
    134 
    135 DWORD WaitForSemaphoreCE (SYNCHHANDLE hSemCE, DWORD dwMilliseconds)
    136    /* Windows CE semaphore equivalent of WaitForSingleObject. */
    137 {
    138    DWORD WaitResult;
    139 
    140    WaitResult = WaitForSingleObject (hSemCE->hMutex, dwMilliseconds);
    141    if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0) return WaitResult;
    142    while (hSemCE->CurCount <= 0)
    143    {
    144 
    145       /* The count is 0, and the thread must wait on the event (which, by
    146          the rules, is currently reset) for semaphore resources to become
    147          available. First, of course, the mutex must be released so that another
    148          thread will be capable of setting the event. */
    149 
    150       ReleaseMutex (hSemCE->hMutex);
    151 
    152       /*  Wait for the event to be signaled, indicating a semaphore state change.
    153           The event is autoreset and signaled with a SetEvent (not PulseEvent)
    154           so exactly one waiting thread (whether or not there is currently
    155           a waiting thread) is released as a result of the SetEvent. */
    156 
    157       WaitResult = WaitForSingleObject (hSemCE->hEvent, dwMilliseconds);
    158       if (WaitResult != WAIT_OBJECT_0) return WaitResult;
    159 
    160       /*  This is where the properties of setting of an autoreset event is critical
    161           to assure that, even if the semaphore state changes between the
    162           preceding Wait and the next, and even if NO threads are waiting
    163           on the event at the time of the SetEvent, at least one thread
    164           will be released.
    165           Pulsing a manual reset event would appear to work, but it would have
    166           a defect which could appear if the semaphore state changed between
    167           the two waits. */
    168 
    169       WaitResult = WaitForSingleObject (hSemCE->hMutex, dwMilliseconds);
    170       if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0) return WaitResult;
    171 
    172    }
    173    /* The count is not zero and this thread owns the mutex.  */
    174 
    175    hSemCE->CurCount--;
    176    /* The event is now unsignaled, BUT, the semaphore count may not be
    177       zero, in which case the event should be signaled again
    178       before releasing the mutex. */
    179 
    180    if (hSemCE->CurCount > 0) SetEvent (hSemCE->hEvent);
    181    ReleaseMutex (hSemCE->hMutex);
    182    return WaitResult;
    183 }
    184 
    185 BOOL CloseSynchHandle (SYNCHHANDLE hSynch)
    186 /* Close a synchronization handle.
    187    Improvement: Test for a valid handle before dereferencing the handle. */
    188 {
    189    BOOL Result = TRUE;
    190    if (hSynch->hEvent != NULL) Result = Result && CloseHandle (hSynch->hEvent);
    191    if (hSynch->hMutex != NULL) Result = Result && CloseHandle (hSynch->hMutex);
    192    if (hSynch->hSemph != NULL) Result = Result && CloseHandle (hSynch->hSemph);
    193    HeapFree (GetProcessHeap (), 0, hSynch);
    194    return (Result);
    195 }
    196 
    197 static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags)
    198 { /* Prepare to return from a create of a synchronization handle.
    199      If there was any failure, free any allocated resources.
    200      "Flags" indicates which Win32 objects are required in the
    201      synchronization handle. */
    202 
    203    BOOL ok = TRUE;
    204 
    205    if (hSynch == NULL) return NULL;
    206    if ((Flags & 4) == 1 && (hSynch->hEvent == NULL)) ok = FALSE;
    207    if ((Flags & 2) == 1 && (hSynch->hMutex == NULL)) ok = FALSE;
    208    if ((Flags & 1) == 1 && (hSynch->hEvent == NULL)) ok = FALSE;
    209    if (!ok)
    210    {
    211       CloseSynchHandle (hSynch);
    212       return NULL;
    213    }
    214    /* Everything worked */
    215    return hSynch;
    216 }
    217