| 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 | } |