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