]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygserver/bsd_mutex.cc
82ce4bf27c09c979477b789079e6d3a24ff76ebf
[newlib-cygwin.git] / winsup / cygserver / bsd_mutex.cc
1 /* bsd_mutex.cc
2
3 Copyright 2003, 2004, 2005 Red Hat Inc.
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"
12 #include <errno.h>
13 #define _KERNEL 1
14 #define __BSD_VISIBLE 1
15 #include <sys/smallprint.h>
16 #include <limits.h>
17
18 #include "process.h"
19 #include "cygserver_ipc.h"
20
21 /* A BSD kernel global mutex. */
22 struct mtx Giant;
23
24 void
25 mtx_init (mtx *m, const char *name, const void *, int)
26 {
27 m->name = name;
28 m->owner = 0;
29 /* Can't use Windows Mutexes here since Windows Mutexes are only
30 unlockable by the lock owner. */
31 m->h = CreateSemaphore (NULL, 1, 1, NULL);
32 if (!m->h)
33 panic ("couldn't allocate %s mutex, %E\n", name);
34 }
35
36 void
37 _mtx_lock (mtx *m, DWORD winpid, const char *file, int line)
38 {
39 _log (file, line, LOG_DEBUG, "Try locking mutex %s", m->name);
40 if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0)
41 _panic (file, line, "wait for %s in %d failed, %E", m->name, winpid);
42 m->owner = winpid;
43 _log (file, line, LOG_DEBUG, "Locked mutex %s", m->name);
44 }
45
46 int
47 mtx_owned (mtx *m)
48 {
49 return m->owner > 0;
50 }
51
52 void
53 _mtx_assert (mtx *m, int what, const char *file, int line)
54 {
55 switch (what)
56 {
57 case MA_OWNED:
58 if (!mtx_owned (m))
59 _panic (file, line, "Mutex %s not owned", m->name);
60 break;
61 case MA_NOTOWNED:
62 if (mtx_owned (m))
63 _panic (file, line, "Mutex %s is owned", m->name);
64 break;
65 default:
66 break;
67 }
68 }
69
70 void
71 _mtx_unlock (mtx *m, const char *file, int line)
72 {
73 m->owner = 0;
74 /* Cautiously check if mtx_destroy has been called (shutdown).
75 In that case, m->h is NULL. */
76 if (m->h && !ReleaseSemaphore (m->h, 1, NULL))
77 {
78 /* Check if the semaphore was already on it's max value. In this case,
79 ReleaseSemaphore returns FALSE with an error code which *sic* depends
80 on the OS. */
81 if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_PARAMETER)
82 || (wincap.is_winnt () && GetLastError () != ERROR_TOO_MANY_POSTS))
83 _panic (file, line, "release of mutex %s failed, %E", m->name);
84 }
85 _log (file, line, LOG_DEBUG, "Unlocked mutex %s", m->name);
86 }
87
88 void
89 mtx_destroy (mtx *m)
90 {
91 HANDLE tmp = m->h;
92 m->h = NULL;
93 if (tmp)
94 CloseHandle (tmp);
95 }
96
97 /*
98 * Helper functions for msleep/wakeup.
99 */
100
101 /* Values for which */
102 #define MSLEEP_MUTEX 0
103 #define MSLEEP_SEM 1
104 #define MSLEEP_EVENT 2
105
106 static char *
107 msleep_event_name (void *ident, char *name, int which)
108 {
109 if (wincap.has_terminal_services ())
110 __small_sprintf (name, "Global\\cygserver.msleep.evt.%1d.%08x",
111 which, ident);
112 else
113 __small_sprintf (name, "cygserver.msleep.evt.%1d.%08x", which, ident);
114 return name;
115 }
116
117 static int
118 win_priority (int priority)
119 {
120 int p = (int)((priority) & PRIO_MASK) - PZERO;
121 /* Generating a valid priority value is a bit tricky. The only valid
122 values on 9x and NT4 are -15, -2, -1, 0, 1, 2, 15. */
123 switch (p)
124 {
125 case -15: case -14: case -13: case -12: case -11:
126 return THREAD_PRIORITY_IDLE;
127 case -10: case -9: case -8: case -7: case -6:
128 return THREAD_PRIORITY_LOWEST;
129 case -5: case -4: case -3: case -2: case -1:
130 return THREAD_PRIORITY_BELOW_NORMAL;
131 case 0:
132 return THREAD_PRIORITY_NORMAL;
133 case 1: case 2: case 3: case 4: case 5:
134 return THREAD_PRIORITY_ABOVE_NORMAL;
135 case 6: case 7: case 8: case 9: case 10:
136 return THREAD_PRIORITY_HIGHEST;
137 case 11: case 12: case 13: case 14: case 15:
138 return THREAD_PRIORITY_TIME_CRITICAL;
139 }
140 return THREAD_PRIORITY_NORMAL;
141 }
142
143 /*
144 * Sets the thread priority, returns the old priority.
145 */
146 static int
147 set_priority (int priority)
148 {
149 int old_prio = GetThreadPriority (GetCurrentThread ());
150 if (!SetThreadPriority (GetCurrentThread (), win_priority (priority)))
151 log (LOG_WARNING,
152 "Warning: Setting thread priority to %d failed with error %lu\n",
153 win_priority (priority), GetLastError ());
154 return old_prio;
155 }
156
157 /*
158 * Original description from BSD code:
159 *
160 * General sleep call. Suspends the current process until a wakeup is
161 * performed on the specified identifier. The process will then be made
162 * runnable with the specified priority. Sleeps at most timo/hz seconds
163 * (0 means no timeout). If pri includes PCATCH flag, signals are checked
164 * before and after sleeping, else signals are not checked. Returns 0 if
165 * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a
166 * signal needs to be delivered, ERESTART is returned if the current system
167 * call should be restarted if possible, and EINTR is returned if the system
168 * call should be interrupted by the signal (return EINTR).
169 *
170 * The mutex argument is exited before the caller is suspended, and
171 * entered before msleep returns. If priority includes the PDROP
172 * flag the mutex is not entered before returning.
173 */
174 static HANDLE msleep_glob_evt;
175
176 void
177 msleep_init (void)
178 {
179 msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL);
180 if (!msleep_glob_evt)
181 panic ("CreateEvent in msleep_init failed: %E");
182 }
183
184 int
185 _msleep (void *ident, struct mtx *mtx, int priority,
186 const char *wmesg, int timo, struct thread *td)
187 {
188 int ret = -1;
189 char name[64];
190
191 /* The mutex is used to indicate an ident specific critical section.
192 The critical section is needed to synchronize access to the
193 semaphore and eventually the event object. The whole idea is
194 that a wakeup is *guaranteed* to wakeup *all* threads. If that's
195 not synchronized, sleeping threads could return into the msleep
196 function before all other threads have called CloseHandle(evt).
197 That's bad, since the event still exists and is signalled! */
198 HANDLE mutex = CreateMutex (NULL, FALSE,
199 msleep_event_name (ident, name, MSLEEP_MUTEX));
200 if (!mutex)
201 panic ("CreateMutex in msleep (%s) failed: %E", wmesg);
202 WaitForSingleObject (mutex, INFINITE);
203
204 /* Ok, we're in the critical section now. We create an ident specific
205 semaphore, which is used to synchronize the waiting threads. */
206 HANDLE sem = CreateSemaphore (NULL, 0, LONG_MAX,
207 msleep_event_name (ident, name, MSLEEP_SEM));
208 if (!sem)
209 panic ("CreateSemaphore in msleep (%s) failed: %E", wmesg);
210
211 /* This thread is one more thread sleeping. The semaphore value is
212 so used as a counter of sleeping threads. That info is needed by
213 the wakeup function. */
214 ReleaseSemaphore (sem, 1, NULL);
215
216 /* Leave critical section. */
217 ReleaseMutex (mutex);
218
219 HANDLE evt = CreateEvent (NULL, TRUE, FALSE,
220 msleep_event_name (ident, name, MSLEEP_EVENT));
221 if (!evt)
222 panic ("CreateEvent in msleep (%s) failed: %E", wmesg);
223 if (mtx)
224 mtx_unlock (mtx);
225 int old_priority = set_priority (priority);
226 HANDLE obj[4] =
227 {
228 evt,
229 msleep_glob_evt,
230 td->client->handle (),
231 td->client->signal_arrived ()
232 };
233 /* PCATCH handling. If PCATCH is given and signal_arrived is a valid
234 handle, then it's used in the WaitFor call and EINTR is returned. */
235 int obj_cnt = 3;
236 if ((priority & PCATCH)
237 && td->client->signal_arrived () != INVALID_HANDLE_VALUE)
238 obj_cnt = 4;
239
240 switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE))
241 {
242 case WAIT_OBJECT_0: /* wakeup() has been called. */
243 ret = 0;
244 break;
245 case WAIT_OBJECT_0 + 1: /* Shutdown event (triggered by wakeup_all). */
246 priority |= PDROP;
247 /*FALLTHRU*/
248 case WAIT_OBJECT_0 + 2: /* The dependent process has exited. */
249 ret = EIDRM;
250 break;
251 case WAIT_OBJECT_0 + 3: /* Signal for calling process arrived. */
252 ret = EINTR;
253 break;
254 case WAIT_TIMEOUT:
255 ret = EWOULDBLOCK;
256 break;
257 default:
258 panic ("wait in msleep (%s) failed, %E", wmesg);
259 break;
260 }
261
262 CloseHandle (evt);
263 /* wakeup has reset the semaphore to 0. Now indicate that this thread
264 has called CloseHandle (evt) and enter the critical section. The
265 critical section is still hold by wakeup, until all formerly sleeping
266 threads have indicated that the event has been dismissed. That's
267 the signal for wakeup that it's the only thread still holding a
268 handle to the event object. wakeup will then close the last handle
269 and leave the critical section. */
270 ReleaseSemaphore (sem, 1, NULL);
271 WaitForSingleObject (mutex, INFINITE);
272 CloseHandle (sem);
273 ReleaseMutex (mutex);
274 CloseHandle (mutex);
275
276 set_priority (old_priority);
277
278 if (mtx && !(priority & PDROP))
279 mtx_lock (mtx);
280 return ret;
281 }
282
283 /*
284 * Make all threads sleeping on the specified identifier runnable.
285 */
286 int
287 wakeup (void *ident)
288 {
289 char name[64];
290 LONG threads;
291
292 HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE,
293 msleep_event_name (ident, name, MSLEEP_EVENT));
294 if (!evt) /* No thread is waiting. */
295 {
296 /* Another round of different error codes returned by 9x and NT
297 systems. Oh boy... */
298 if ( (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME)
299 || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND))
300 panic ("OpenEvent (%s) in wakeup failed: %E", name);
301 return 0;
302 }
303
304 /* The mutex is used to indicate an ident specific critical section.
305 The critical section is needed to synchronize access to the
306 semaphore and eventually the event object. The whole idea is
307 that a wakeup is *guaranteed* to wakeup *all* threads. If that's
308 not synchronized, sleeping threads could return into the msleep
309 function before all other threads have called CloseHandle(evt).
310 That's bad, since the event still exists and is signalled! */
311 HANDLE mutex = OpenMutex (MUTEX_ALL_ACCESS, FALSE,
312 msleep_event_name (ident, name, MSLEEP_MUTEX));
313 if (!mutex)
314 panic ("OpenMutex (%s) in wakeup failed: %E", name);
315 WaitForSingleObject (mutex, INFINITE);
316 /* Ok, we're in the critical section now. We create an ident specific
317 semaphore, which is used to synchronize the waiting threads. */
318 HANDLE sem = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
319 msleep_event_name (ident, name, MSLEEP_SEM));
320 if (!sem)
321 panic ("OpenSemaphore (%s) in wakeup failed: %E", name);
322 ReleaseSemaphore (sem, 1, &threads);
323 /* `threads' is the number of waiting threads. Now reset the semaphore
324 to 0 and wait for this number of threads to indicate that they have
325 called CloseHandle (evt). Then it's save to do the same here in
326 wakeup, which then means that the event object is destroyed and
327 can get safely recycled. */
328 for (int i = threads + 1; i > 0; --i)
329 WaitForSingleObject (sem, INFINITE);
330
331 if (!SetEvent (evt))
332 panic ("SetEvent (%s) in wakeup failed, %E", name);
333
334 /* Now wait for all threads which were waiting for this wakeup. */
335 while (threads-- > 0)
336 WaitForSingleObject (sem, INFINITE);
337
338 /* Now our handle is the last handle to this event object. */
339 CloseHandle (evt);
340 /* But paranoia rulez, so we check here again. */
341 evt = OpenEvent (EVENT_MODIFY_STATE, FALSE,
342 msleep_event_name (ident, name, MSLEEP_EVENT));
343 if (evt)
344 panic ("Event %s has not been destroyed. Obviously I can't count :-(",
345 name);
346
347 CloseHandle (sem);
348
349 /* Leave critical section (all of wakeup is critical). */
350 ReleaseMutex (mutex);
351 CloseHandle (mutex);
352
353 return 0;
354 }
355
356 /*
357 * Wakeup all sleeping threads. Only called in the context of cygserver
358 * shutdown.
359 */
360 void
361 wakeup_all (void)
362 {
363 SetEvent (msleep_glob_evt);
364 }
365 #endif /* __OUTSIDE_CYGWIN__ */
This page took 0.050458 seconds and 4 git commands to generate.