]>
Commit | Line | Data |
---|---|---|
282113ba CV |
1 | /* bsd_mutex.cc |
2 | ||
282113ba CV |
3 | This file is part of Cygwin. |
4 | ||
5 | This software is a copyrighted work licensed under the terms of the | |
6 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
7 | details. */ | |
8 | #ifdef __OUTSIDE_CYGWIN__ | |
9 | #include "woutsup.h" | |
1dcd520b | 10 | #include <errno.h> |
282113ba CV |
11 | #define _KERNEL 1 |
12 | #define __BSD_VISIBLE 1 | |
13 | #include <sys/smallprint.h> | |
f312634c | 14 | #include <limits.h> |
dafef5e2 CV |
15 | #include <stdlib.h> |
16 | #include <sys/msg.h> | |
17 | #include <sys/sem.h> | |
282113ba CV |
18 | |
19 | #include "process.h" | |
20 | #include "cygserver_ipc.h" | |
21 | ||
22 | /* A BSD kernel global mutex. */ | |
23 | struct mtx Giant; | |
24 | ||
25 | void | |
26 | mtx_init (mtx *m, const char *name, const void *, int) | |
27 | { | |
28 | m->name = name; | |
29 | m->owner = 0; | |
dafef5e2 | 30 | m->cnt = 0; |
282113ba CV |
31 | /* Can't use Windows Mutexes here since Windows Mutexes are only |
32 | unlockable by the lock owner. */ | |
33 | m->h = CreateSemaphore (NULL, 1, 1, NULL); | |
34 | if (!m->h) | |
681bb2f7 | 35 | panic ("couldn't allocate %s mutex, %u\n", name, GetLastError ()); |
282113ba CV |
36 | } |
37 | ||
38 | void | |
39 | _mtx_lock (mtx *m, DWORD winpid, const char *file, int line) | |
40 | { | |
663b4ab8 | 41 | _debug (file, line, "Try locking mutex %s (%u) (hold: %u)", |
dafef5e2 | 42 | m->name, winpid, m->owner); |
282113ba | 43 | if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0) |
681bb2f7 | 44 | _panic (file, line, "wait for %s in %d failed, %u", m->name, winpid, |
62688407 | 45 | GetLastError ()); |
282113ba | 46 | m->owner = winpid; |
663b4ab8 | 47 | _debug (file, line, "Locked mutex %s/%u (owner: %u)", |
dafef5e2 | 48 | m->name, ++m->cnt, winpid); |
282113ba CV |
49 | } |
50 | ||
51 | int | |
dafef5e2 | 52 | mtx_owned (mtx *m, DWORD winpid) |
282113ba | 53 | { |
dafef5e2 | 54 | return m->owner == winpid; |
282113ba CV |
55 | } |
56 | ||
57 | void | |
dafef5e2 | 58 | _mtx_assert (mtx *m, int what, DWORD winpid, const char *file, int line) |
282113ba CV |
59 | { |
60 | switch (what) | |
61 | { | |
62 | case MA_OWNED: | |
dafef5e2 | 63 | if (!mtx_owned (m, winpid)) |
96e949de | 64 | _panic (file, line, "Mutex %s not owned", m->name); |
282113ba CV |
65 | break; |
66 | case MA_NOTOWNED: | |
dafef5e2 | 67 | if (mtx_owned (m, winpid)) |
96e949de | 68 | _panic (file, line, "Mutex %s is owned", m->name); |
282113ba CV |
69 | break; |
70 | default: | |
71 | break; | |
72 | } | |
73 | } | |
74 | ||
75 | void | |
76 | _mtx_unlock (mtx *m, const char *file, int line) | |
77 | { | |
dafef5e2 CV |
78 | DWORD owner = m->owner; |
79 | unsigned long cnt = m->cnt; | |
282113ba CV |
80 | m->owner = 0; |
81 | /* Cautiously check if mtx_destroy has been called (shutdown). | |
82 | In that case, m->h is NULL. */ | |
83 | if (m->h && !ReleaseSemaphore (m->h, 1, NULL)) | |
84 | { | |
7131554a CV |
85 | /* Check if the semaphore was already on it's max value. */ |
86 | if (GetLastError () != ERROR_TOO_MANY_POSTS) | |
681bb2f7 | 87 | _panic (file, line, "release of mutex %s failed, %u", m->name, |
62688407 | 88 | GetLastError ()); |
282113ba | 89 | } |
663b4ab8 | 90 | _debug (file, line, "Unlocked mutex %s/%u (owner: %u)", |
dafef5e2 | 91 | m->name, cnt, owner); |
282113ba CV |
92 | } |
93 | ||
94 | void | |
95 | mtx_destroy (mtx *m) | |
96 | { | |
97 | HANDLE tmp = m->h; | |
98 | m->h = NULL; | |
99 | if (tmp) | |
100 | CloseHandle (tmp); | |
101 | } | |
102 | ||
103 | /* | |
104 | * Helper functions for msleep/wakeup. | |
105 | */ | |
f312634c | 106 | |
282113ba CV |
107 | static int |
108 | win_priority (int priority) | |
109 | { | |
f8e58afb | 110 | int p = (int)((priority) & PRIO_MASK) - PZERO; |
282113ba | 111 | /* Generating a valid priority value is a bit tricky. The only valid |
7131554a | 112 | values on NT4 are -15, -2, -1, 0, 1, 2, 15. */ |
282113ba CV |
113 | switch (p) |
114 | { | |
115 | case -15: case -14: case -13: case -12: case -11: | |
116 | return THREAD_PRIORITY_IDLE; | |
117 | case -10: case -9: case -8: case -7: case -6: | |
118 | return THREAD_PRIORITY_LOWEST; | |
119 | case -5: case -4: case -3: case -2: case -1: | |
120 | return THREAD_PRIORITY_BELOW_NORMAL; | |
121 | case 0: | |
122 | return THREAD_PRIORITY_NORMAL; | |
123 | case 1: case 2: case 3: case 4: case 5: | |
124 | return THREAD_PRIORITY_ABOVE_NORMAL; | |
125 | case 6: case 7: case 8: case 9: case 10: | |
126 | return THREAD_PRIORITY_HIGHEST; | |
127 | case 11: case 12: case 13: case 14: case 15: | |
128 | return THREAD_PRIORITY_TIME_CRITICAL; | |
129 | } | |
130 | return THREAD_PRIORITY_NORMAL; | |
131 | } | |
132 | ||
133 | /* | |
134 | * Sets the thread priority, returns the old priority. | |
135 | */ | |
136 | static int | |
137 | set_priority (int priority) | |
138 | { | |
139 | int old_prio = GetThreadPriority (GetCurrentThread ()); | |
f5133f95 | 140 | if (!SetThreadPriority (GetCurrentThread (), win_priority (priority))) |
282113ba | 141 | log (LOG_WARNING, |
681bb2f7 | 142 | "Warning: Setting thread priority to %d failed with error %u\n", |
f5133f95 | 143 | win_priority (priority), GetLastError ()); |
282113ba CV |
144 | return old_prio; |
145 | } | |
146 | ||
f5133f95 CV |
147 | /* |
148 | * Original description from BSD code: | |
149 | * | |
150 | * General sleep call. Suspends the current process until a wakeup is | |
151 | * performed on the specified identifier. The process will then be made | |
152 | * runnable with the specified priority. Sleeps at most timo/hz seconds | |
153 | * (0 means no timeout). If pri includes PCATCH flag, signals are checked | |
154 | * before and after sleeping, else signals are not checked. Returns 0 if | |
155 | * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a | |
156 | * signal needs to be delivered, ERESTART is returned if the current system | |
157 | * call should be restarted if possible, and EINTR is returned if the system | |
158 | * call should be interrupted by the signal (return EINTR). | |
159 | * | |
160 | * The mutex argument is exited before the caller is suspended, and | |
161 | * entered before msleep returns. If priority includes the PDROP | |
162 | * flag the mutex is not entered before returning. | |
163 | */ | |
164 | static HANDLE msleep_glob_evt; | |
c4004194 CV |
165 | |
166 | class msleep_sync_array | |
167 | { | |
168 | enum msleep_action { | |
169 | MSLEEP_ENTER = 0, | |
170 | MSLEEP_LEAVE, | |
171 | MSLEEP_WAKEUP | |
172 | }; | |
173 | ||
174 | CRITICAL_SECTION cs; | |
c5ca43f3 CV |
175 | long cnt; |
176 | long max_cnt; | |
177 | struct msleep_record { | |
178 | void *ident; | |
179 | HANDLE wakeup_evt; | |
180 | LONG threads; | |
181 | } *a; | |
182 | ||
183 | int find_ident (void *ident, msleep_action action) | |
184 | { | |
185 | int i; | |
186 | for (i = 0; i < cnt; ++i) | |
187 | if (a[i].ident == ident) | |
188 | return i; | |
189 | if (i >= max_cnt) | |
190 | panic ("ident %x not found and run out of slots.", ident); | |
191 | if (i >= cnt && action == MSLEEP_LEAVE) | |
192 | panic ("ident %x not found (%d).", ident, action); | |
193 | return i; | |
194 | } | |
195 | ||
196 | HANDLE first_entry (int i, void *ident) | |
197 | { | |
198 | debug ("New ident %x, index %d", ident, i); | |
199 | a[i].ident = ident; | |
200 | a[i].wakeup_evt = CreateEvent (NULL, TRUE, FALSE, NULL); | |
201 | if (!a[i].wakeup_evt) | |
202 | panic ("CreateEvent failed: %u", GetLastError ()); | |
203 | debug ("i = %d, CreateEvent: %x", i, a[i].wakeup_evt); | |
204 | a[i].threads = 1; | |
205 | ++cnt; | |
206 | return a[i].wakeup_evt; | |
207 | } | |
208 | ||
209 | HANDLE next_entry (int i) | |
210 | { | |
211 | if (a[i].ident && WaitForSingleObject (a[i].wakeup_evt, 0) != WAIT_OBJECT_0) | |
212 | { | |
213 | ++a[i].threads; | |
214 | return a[i].wakeup_evt; | |
215 | } | |
216 | return NULL; | |
217 | } | |
c4004194 CV |
218 | |
219 | public: | |
220 | ||
c5ca43f3 | 221 | msleep_sync_array (int count) : cnt (0), max_cnt (count) |
c4004194 CV |
222 | { |
223 | InitializeCriticalSection (&cs); | |
c5ca43f3 | 224 | if (!(a = new msleep_record[count])) |
c4004194 CV |
225 | panic ("Allocating msleep records failed: %d", errno); |
226 | } | |
227 | ||
c5ca43f3 | 228 | ~msleep_sync_array () { delete a; } |
11172790 | 229 | |
c5ca43f3 | 230 | HANDLE enter (void *ident) |
c4004194 | 231 | { |
c5ca43f3 CV |
232 | HANDLE evt = NULL; |
233 | while (!evt) | |
c4004194 | 234 | { |
c5ca43f3 CV |
235 | EnterCriticalSection (&cs); |
236 | int i = find_ident (ident, MSLEEP_ENTER); | |
237 | if (i >= cnt) | |
238 | evt = first_entry (i, ident); | |
239 | else if (!(evt = next_entry (i))) | |
c4004194 | 240 | { |
c5ca43f3 CV |
241 | /* wakeup has been called, so sleep to wait until all |
242 | formerly waiting threads have left and retry. */ | |
243 | LeaveCriticalSection (&cs); | |
244 | Sleep (1L); | |
c4004194 CV |
245 | } |
246 | } | |
c5ca43f3 CV |
247 | LeaveCriticalSection (&cs); |
248 | return evt; | |
c4004194 CV |
249 | } |
250 | ||
c5ca43f3 | 251 | void leave (void *ident) |
c4004194 | 252 | { |
c5ca43f3 CV |
253 | EnterCriticalSection (&cs); |
254 | int i = find_ident (ident, MSLEEP_LEAVE); | |
255 | if (--a[i].threads == 0) | |
256 | { | |
257 | debug ("i = %d, CloseEvent: %x", i, a[i].wakeup_evt); | |
258 | CloseHandle (a[i].wakeup_evt); | |
259 | a[i].ident = NULL; | |
260 | --cnt; | |
261 | if (i < cnt) | |
262 | a[i] = a[cnt]; | |
263 | } | |
264 | LeaveCriticalSection (&cs); | |
c4004194 CV |
265 | } |
266 | ||
c5ca43f3 | 267 | void wakeup (void *ident) |
c4004194 | 268 | { |
c5ca43f3 CV |
269 | EnterCriticalSection (&cs); |
270 | int i = find_ident (ident, MSLEEP_WAKEUP); | |
271 | if (i < cnt && a[i].ident) | |
272 | SetEvent (a[i].wakeup_evt); | |
273 | LeaveCriticalSection (&cs); | |
c4004194 CV |
274 | } |
275 | }; | |
276 | ||
277 | static msleep_sync_array *msleep_sync; | |
3a956a9b KB |
278 | extern struct msginfo msginfo; |
279 | extern struct seminfo seminfo; | |
f5133f95 CV |
280 | |
281 | void | |
282 | msleep_init (void) | |
283 | { | |
284 | msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); | |
285 | if (!msleep_glob_evt) | |
681bb2f7 | 286 | panic ("CreateEvent in msleep_init failed: %u", GetLastError ()); |
c5ca43f3 CV |
287 | int32_t msgmni = support_msgqueues ? msginfo.msgmni : 0; |
288 | int32_t semmni = support_semaphores ? seminfo.semmni : 0; | |
289 | TUNABLE_INT_FETCH ("kern.ipc.msgmni", &msgmni); | |
290 | TUNABLE_INT_FETCH ("kern.ipc.semmni", &semmni); | |
291 | debug ("Try allocating msgmni (%d) + semmni (%d) msleep records", | |
292 | msgmni, semmni); | |
293 | msleep_sync = new msleep_sync_array (msgmni + semmni); | |
c4004194 | 294 | if (!msleep_sync) |
dafef5e2 | 295 | panic ("Allocating msleep records in msleep_init failed: %d", errno); |
f5133f95 CV |
296 | } |
297 | ||
282113ba | 298 | int |
c5ca43f3 | 299 | _msleep (void *ident, struct mtx *mtx, int priority, |
282113ba CV |
300 | const char *wmesg, int timo, struct thread *td) |
301 | { | |
302 | int ret = -1; | |
f312634c | 303 | |
c5ca43f3 | 304 | HANDLE evt = msleep_sync->enter (ident); |
f312634c | 305 | |
282113ba CV |
306 | if (mtx) |
307 | mtx_unlock (mtx); | |
308 | int old_priority = set_priority (priority); | |
f5133f95 CV |
309 | HANDLE obj[4] = |
310 | { | |
c4004194 | 311 | evt, |
f5133f95 CV |
312 | msleep_glob_evt, |
313 | td->client->handle (), | |
8d8f4036 | 314 | td->ipcblk->signal_arrived |
f5133f95 | 315 | }; |
373a036f CV |
316 | /* PCATCH handling. If PCATCH is given and signal_arrived is a valid |
317 | handle, then it's used in the WaitFor call and EINTR is returned. */ | |
318 | int obj_cnt = 3; | |
8d8f4036 | 319 | if ((priority & PCATCH) && obj[3]) |
373a036f CV |
320 | obj_cnt = 4; |
321 | switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE)) | |
282113ba CV |
322 | { |
323 | case WAIT_OBJECT_0: /* wakeup() has been called. */ | |
324 | ret = 0; | |
c4004194 | 325 | debug ("msleep wakeup called for %d", td->td_proc->winpid); |
282113ba | 326 | break; |
f5133f95 | 327 | case WAIT_OBJECT_0 + 1: /* Shutdown event (triggered by wakeup_all). */ |
282113ba | 328 | priority |= PDROP; |
225d376b | 329 | fallthrough; |
f5133f95 | 330 | case WAIT_OBJECT_0 + 2: /* The dependent process has exited. */ |
c4004194 | 331 | debug ("msleep process exit or shutdown for %d", td->td_proc->winpid); |
282113ba CV |
332 | ret = EIDRM; |
333 | break; | |
373a036f | 334 | case WAIT_OBJECT_0 + 3: /* Signal for calling process arrived. */ |
c4004194 | 335 | debug ("msleep process got signal for %d", td->td_proc->winpid); |
373a036f CV |
336 | ret = EINTR; |
337 | break; | |
282113ba CV |
338 | case WAIT_TIMEOUT: |
339 | ret = EWOULDBLOCK; | |
340 | break; | |
341 | default: | |
dafef5e2 CV |
342 | /* There's a chance that a process has been terminated before |
343 | WaitForMultipleObjects has been called. In this case the handles | |
344 | might be invalid. The error code returned is ERROR_INVALID_HANDLE. | |
345 | Since we can trust the values of these handles otherwise, we | |
346 | treat an ERROR_INVALID_HANDLE as a normal process termination and | |
347 | hope for the best. */ | |
348 | if (GetLastError () != ERROR_INVALID_HANDLE) | |
681bb2f7 CV |
349 | panic ("wait in msleep (%s) failed, %u", wmesg, GetLastError ()); |
350 | debug ("wait in msleep (%s) failed for %d, %u", wmesg, | |
62688407 | 351 | td->td_proc->winpid, GetLastError ()); |
dafef5e2 | 352 | ret = EIDRM; |
282113ba CV |
353 | break; |
354 | } | |
f312634c | 355 | |
c5ca43f3 CV |
356 | msleep_sync->leave (ident); |
357 | ||
282113ba | 358 | set_priority (old_priority); |
f312634c | 359 | |
a9185492 | 360 | if (mtx && !(priority & PDROP)) |
282113ba | 361 | mtx_lock (mtx); |
282113ba CV |
362 | return ret; |
363 | } | |
364 | ||
365 | /* | |
366 | * Make all threads sleeping on the specified identifier runnable. | |
367 | */ | |
368 | int | |
c5ca43f3 | 369 | wakeup (void *ident) |
282113ba | 370 | { |
c5ca43f3 | 371 | msleep_sync->wakeup (ident); |
282113ba CV |
372 | return 0; |
373 | } | |
374 | ||
375 | /* | |
376 | * Wakeup all sleeping threads. Only called in the context of cygserver | |
377 | * shutdown. | |
378 | */ | |
379 | void | |
380 | wakeup_all (void) | |
381 | { | |
c4004194 | 382 | SetEvent (msleep_glob_evt); |
282113ba CV |
383 | } |
384 | #endif /* __OUTSIDE_CYGWIN__ */ |