src/thread/win32/win_ce_semaphore.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
permissions -rw-r--r--
more tweaking indent options
     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
    39 CreateSemaphoreCE(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,  /* pointer to security attributes */
    40                   LONG lInitialCount,   /* initial count */
    41                   LONG lMaximumCount,   /* maximum count */
    42                   LPCTSTR lpName)
    43 /* Semaphore for use with Windows CE that does not support them directly.
    44    Requires a counter, a mutex to protect the counter, and an
    45    autoreset event.
    46 
    47    Here are the rules that must always hold between the autoreset event
    48    and the mutex (any violation of these rules by the CE semaphore functions
    49    will, in all likelihood, result in a defect):
    50     1. No thread can set, pulse, or reset the event,
    51        nor can it access any part of the SYNCHHANDLE structure,
    52        without first gaining ownership of the mutex.
    53        BUT, a thread can wait on the event without owning the mutex
    54        (this is clearly necessary or else the event could never be set).
    55     2. The event is in a signaled state if and only if the current semaphore
    56        count ("CurCount") is greater than zero.
    57     3. The semaphore count is always >= 0 and <= the maximum count */
    58 {
    59     SYNCHHANDLE hSynch = NULL, result = NULL;
    60 
    61     __try {
    62         if (lInitialCount > lMaximumCount || lMaximumCount < 0
    63             || lInitialCount < 0) {
    64             /* Bad parameters */
    65             SetLastError(SYNCH_ERROR);
    66             __leave;
    67         }
    68 
    69         hSynch =
    70             HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SYNCH_HANDLE_SIZE);
    71         if (hSynch == NULL)
    72             __leave;
    73 
    74         hSynch->MaxCount = lMaximumCount;
    75         hSynch->CurCount = lInitialCount;
    76         hSynch->lpName = lpName;
    77 
    78         hSynch->hMutex = CreateMutex(lpSemaphoreAttributes, FALSE, NULL);
    79 
    80         WaitForSingleObject(hSynch->hMutex, INFINITE);
    81         /*  Create the event. It is initially signaled if and only if the
    82            initial count is > 0 */
    83         hSynch->hEvent = CreateEvent(lpSemaphoreAttributes, FALSE,
    84                                      lInitialCount > 0, NULL);
    85         ReleaseMutex(hSynch->hMutex);
    86         hSynch->hSemph = NULL;
    87     }
    88     __finally {
    89         /* Return with the handle, or, if there was any error, return
    90            a null after closing any open handles and freeing any allocated memory. */
    91         result =
    92             CleanUp(hSynch, 6 /* An event and a mutex, but no semaphore. */ );
    93     }
    94 
    95     return result;
    96 }
    97 
    98 BOOL
    99 ReleaseSemaphoreCE(SYNCHHANDLE hSemCE, LONG cReleaseCount,
   100                    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         WaitForSingleObject(hSemCE->hMutex, INFINITE);
   110         /* reply only if asked to */
   111         if (lpPreviousCount != NULL)
   112             *lpPreviousCount = hSemCE->CurCount;
   113         if (hSemCE->CurCount + cReleaseCount > hSemCE->MaxCount
   114             || cReleaseCount <= 0) {
   115             SetLastError(SYNCH_ERROR);
   116             Result = FALSE;
   117             __leave;
   118         }
   119         hSemCE->CurCount += cReleaseCount;
   120 
   121         /*  Set the autoreset event, releasing exactly one waiting thread, now or
   122            in the future.  */
   123 
   124         SetEvent(hSemCE->hEvent);
   125     }
   126     __finally {
   127         ReleaseMutex(hSemCE->hMutex);
   128     }
   129 
   130     return Result;
   131 }
   132 
   133 DWORD
   134 WaitForSemaphoreCE(SYNCHHANDLE hSemCE, DWORD dwMilliseconds)
   135    /* Windows CE semaphore equivalent of WaitForSingleObject. */
   136 {
   137     DWORD WaitResult;
   138 
   139     WaitResult = WaitForSingleObject(hSemCE->hMutex, dwMilliseconds);
   140     if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0)
   141         return WaitResult;
   142     while (hSemCE->CurCount <= 0) {
   143 
   144         /* The count is 0, and the thread must wait on the event (which, by
   145            the rules, is currently reset) for semaphore resources to become
   146            available. First, of course, the mutex must be released so that another
   147            thread will be capable of setting the event. */
   148 
   149         ReleaseMutex(hSemCE->hMutex);
   150 
   151         /*  Wait for the event to be signaled, indicating a semaphore state change.
   152            The event is autoreset and signaled with a SetEvent (not PulseEvent)
   153            so exactly one waiting thread (whether or not there is currently
   154            a waiting thread) is released as a result of the SetEvent. */
   155 
   156         WaitResult = WaitForSingleObject(hSemCE->hEvent, dwMilliseconds);
   157         if (WaitResult != WAIT_OBJECT_0)
   158             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)
   171             return WaitResult;
   172 
   173     }
   174     /* The count is not zero and this thread owns the mutex.  */
   175 
   176     hSemCE->CurCount--;
   177     /* The event is now unsignaled, BUT, the semaphore count may not be
   178        zero, in which case the event should be signaled again
   179        before releasing the mutex. */
   180 
   181     if (hSemCE->CurCount > 0)
   182         SetEvent(hSemCE->hEvent);
   183     ReleaseMutex(hSemCE->hMutex);
   184     return WaitResult;
   185 }
   186 
   187 BOOL
   188 CloseSynchHandle(SYNCHHANDLE hSynch)
   189 /* Close a synchronization handle. 
   190    Improvement: Test for a valid handle before dereferencing the handle. */
   191 {
   192     BOOL Result = TRUE;
   193     if (hSynch->hEvent != NULL)
   194         Result = Result && CloseHandle(hSynch->hEvent);
   195     if (hSynch->hMutex != NULL)
   196         Result = Result && CloseHandle(hSynch->hMutex);
   197     if (hSynch->hSemph != NULL)
   198         Result = Result && CloseHandle(hSynch->hSemph);
   199     HeapFree(GetProcessHeap(), 0, hSynch);
   200     return (Result);
   201 }
   202 
   203 static SYNCHHANDLE
   204 CleanUp(SYNCHHANDLE hSynch, DWORD Flags)
   205 {                               /* Prepare to return from a create of a synchronization handle.
   206                                    If there was any failure, free any allocated resources.
   207                                    "Flags" indicates which Win32 objects are required in the 
   208                                    synchronization handle. */
   209 
   210     BOOL ok = TRUE;
   211 
   212     if (hSynch == NULL)
   213         return NULL;
   214     if ((Flags & 4) == 1 && (hSynch->hEvent == NULL))
   215         ok = FALSE;
   216     if ((Flags & 2) == 1 && (hSynch->hMutex == NULL))
   217         ok = FALSE;
   218     if ((Flags & 1) == 1 && (hSynch->hEvent == NULL))
   219         ok = FALSE;
   220     if (!ok) {
   221         CloseSynchHandle(hSynch);
   222         return NULL;
   223     }
   224     /* Everything worked */
   225     return hSynch;
   226 }
   227 
   228 /* vi: set ts=4 sw=4 expandtab: */