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