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