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
36static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags);
37
38SYNCHHANDLE 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
100BOOL 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
135DWORD 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
185BOOL 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
197static 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