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