]>
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 | 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. */ | |
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) | |
681bb2f7 | 37 | panic ("couldn't allocate %s mutex, %u\n", name, GetLastError ()); |
282113ba CV |
38 | } |
39 | ||
40 | void | |
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 | ||
53 | int | |
dafef5e2 | 54 | mtx_owned (mtx *m, DWORD winpid) |
282113ba | 55 | { |
dafef5e2 | 56 | return m->owner == winpid; |
282113ba CV |
57 | } |
58 | ||
59 | void | |
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 | ||
77 | void | |
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 | ||
96 | void | |
97 | mtx_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 |
109 | static int |
110 | win_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 | */ | |
138 | static int | |
139 | set_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 | */ | |
166 | static HANDLE msleep_glob_evt; | |
c4004194 CV |
167 | |
168 | class 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 | |
179 | public: | |
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 | ||
218 | static msleep_sync_array *msleep_sync; | |
f5133f95 | 219 | |
b80b2c01 CV |
220 | extern struct msginfo msginfo; |
221 | extern struct seminfo seminfo; | |
222 | extern struct shminfo shminfo; | |
223 | int32_t mni[3]; | |
224 | int32_t off[3]; | |
225 | ||
f5133f95 CV |
226 | void |
227 | msleep_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 | 249 | int |
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 | */ | |
323 | int | |
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 | */ | |
334 | void | |
335 | wakeup_all (void) | |
336 | { | |
c4004194 | 337 | SetEvent (msleep_glob_evt); |
282113ba CV |
338 | } |
339 | #endif /* __OUTSIDE_CYGWIN__ */ |