]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
6d2010ae | 2 | * Copyright (c) 2000-2009 Apple Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * @OSF_COPYRIGHT@ | |
0a7de745 | 30 | * |
1c79356b A |
31 | */ |
32 | /* | |
33 | * File: kern/sync_sema.c | |
34 | * Author: Joseph CaraDonna | |
35 | * | |
36 | * Contains RT distributed semaphore synchronization services. | |
37 | */ | |
38 | ||
39 | #include <mach/mach_types.h> | |
91447636 | 40 | #include <mach/mach_traps.h> |
1c79356b A |
41 | #include <mach/kern_return.h> |
42 | #include <mach/semaphore.h> | |
43 | #include <mach/sync_policy.h> | |
91447636 | 44 | #include <mach/task.h> |
1c79356b A |
45 | |
46 | #include <kern/misc_protos.h> | |
47 | #include <kern/sync_sema.h> | |
48 | #include <kern/spl.h> | |
49 | #include <kern/ipc_kobject.h> | |
50 | #include <kern/ipc_sync.h> | |
51 | #include <kern/ipc_tt.h> | |
52 | #include <kern/thread.h> | |
53 | #include <kern/clock.h> | |
54 | #include <ipc/ipc_port.h> | |
55 | #include <ipc/ipc_space.h> | |
56 | #include <kern/host.h> | |
3e170ce0 | 57 | #include <kern/waitq.h> |
1c79356b A |
58 | #include <kern/zalloc.h> |
59 | #include <kern/mach_param.h> | |
60 | ||
316670eb A |
61 | #include <libkern/OSAtomic.h> |
62 | ||
9bccf70c | 63 | static unsigned int semaphore_event; |
cf7d32b8 | 64 | #define SEMAPHORE_EVENT CAST_EVENT64_T(&semaphore_event) |
1c79356b A |
65 | |
66 | zone_t semaphore_zone; | |
b0d623f7 | 67 | unsigned int semaphore_max; |
1c79356b | 68 | |
d9a64523 A |
69 | os_refgrp_decl(static, sema_refgrp, "semaphore", NULL); |
70 | ||
91447636 A |
71 | /* Forward declarations */ |
72 | ||
73 | ||
0a7de745 | 74 | kern_return_t |
91447636 | 75 | semaphore_wait_trap_internal( |
0a7de745 A |
76 | mach_port_name_t name, |
77 | void (*caller_cont)(kern_return_t)); | |
91447636 | 78 | |
0a7de745 | 79 | kern_return_t |
91447636 | 80 | semaphore_wait_signal_trap_internal( |
0a7de745 A |
81 | mach_port_name_t wait_name, |
82 | mach_port_name_t signal_name, | |
83 | void (*caller_cont)(kern_return_t)); | |
91447636 | 84 | |
0a7de745 | 85 | kern_return_t |
91447636 | 86 | semaphore_timedwait_trap_internal( |
0a7de745 A |
87 | mach_port_name_t name, |
88 | unsigned int sec, | |
89 | clock_res_t nsec, | |
90 | void (*caller_cont)(kern_return_t)); | |
91447636 | 91 | |
0a7de745 | 92 | kern_return_t |
91447636 | 93 | semaphore_timedwait_signal_trap_internal( |
0a7de745 A |
94 | mach_port_name_t wait_name, |
95 | mach_port_name_t signal_name, | |
96 | unsigned int sec, | |
97 | clock_res_t nsec, | |
98 | void (*caller_cont)(kern_return_t)); | |
91447636 | 99 | |
2d21ac55 A |
100 | kern_return_t |
101 | semaphore_signal_internal_trap(mach_port_name_t sema_name); | |
91447636 A |
102 | |
103 | kern_return_t | |
104 | semaphore_signal_internal( | |
0a7de745 A |
105 | semaphore_t semaphore, |
106 | thread_t thread, | |
107 | int options); | |
91447636 A |
108 | |
109 | kern_return_t | |
110 | semaphore_convert_wait_result( | |
0a7de745 | 111 | int wait_result); |
91447636 A |
112 | |
113 | void | |
114 | semaphore_wait_continue(void); | |
115 | ||
b0d623f7 | 116 | static kern_return_t |
91447636 | 117 | semaphore_wait_internal( |
0a7de745 A |
118 | semaphore_t wait_semaphore, |
119 | semaphore_t signal_semaphore, | |
120 | uint64_t deadline, | |
121 | int option, | |
122 | void (*caller_cont)(kern_return_t)); | |
91447636 | 123 | |
b0d623f7 A |
124 | static __inline__ uint64_t |
125 | semaphore_deadline( | |
0a7de745 A |
126 | unsigned int sec, |
127 | clock_res_t nsec) | |
b0d623f7 | 128 | { |
0a7de745 | 129 | uint64_t abstime; |
b0d623f7 | 130 | |
0a7de745 | 131 | nanoseconds_to_absolutetime((uint64_t)sec * NSEC_PER_SEC + nsec, &abstime); |
b0d623f7 A |
132 | clock_absolutetime_interval_to_deadline(abstime, &abstime); |
133 | ||
0a7de745 | 134 | return abstime; |
b0d623f7 A |
135 | } |
136 | ||
1c79356b A |
137 | /* |
138 | * ROUTINE: semaphore_init [private] | |
139 | * | |
140 | * Initialize the semaphore mechanisms. | |
141 | * Right now, we only need to initialize the semaphore zone. | |
0a7de745 | 142 | */ |
1c79356b A |
143 | void |
144 | semaphore_init(void) | |
145 | { | |
0a7de745 A |
146 | semaphore_zone = zinit(sizeof(struct semaphore), |
147 | semaphore_max * sizeof(struct semaphore), | |
148 | sizeof(struct semaphore), | |
149 | "semaphores"); | |
150 | zone_change(semaphore_zone, Z_NOENCRYPT, TRUE); | |
1c79356b A |
151 | } |
152 | ||
153 | /* | |
154 | * Routine: semaphore_create | |
155 | * | |
156 | * Creates a semaphore. | |
157 | * The port representing the semaphore is returned as a parameter. | |
158 | */ | |
159 | kern_return_t | |
160 | semaphore_create( | |
0a7de745 A |
161 | task_t task, |
162 | semaphore_t *new_semaphore, | |
163 | int policy, | |
164 | int value) | |
1c79356b | 165 | { |
0a7de745 A |
166 | semaphore_t s = SEMAPHORE_NULL; |
167 | kern_return_t kret; | |
1c79356b A |
168 | |
169 | ||
b0d623f7 | 170 | *new_semaphore = SEMAPHORE_NULL; |
0a7de745 | 171 | if (task == TASK_NULL || value < 0 || policy > SYNC_POLICY_MAX) { |
1c79356b | 172 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 173 | } |
1c79356b | 174 | |
0a7de745 | 175 | s = (semaphore_t) zalloc(semaphore_zone); |
1c79356b | 176 | |
0a7de745 A |
177 | if (s == SEMAPHORE_NULL) { |
178 | return KERN_RESOURCE_SHORTAGE; | |
179 | } | |
b0d623f7 | 180 | |
3e170ce0 | 181 | kret = waitq_init(&s->waitq, policy | SYNC_POLICY_DISABLE_IRQ); /* also inits lock */ |
b0d623f7 A |
182 | if (kret != KERN_SUCCESS) { |
183 | zfree(semaphore_zone, s); | |
184 | return kret; | |
1c79356b A |
185 | } |
186 | ||
1c79356b | 187 | /* |
3e170ce0 | 188 | * Initialize the semaphore values. |
1c79356b | 189 | */ |
0a7de745 | 190 | s->port = IP_NULL; |
d9a64523 | 191 | os_ref_init(&s->ref_count, &sema_refgrp); |
3e170ce0 A |
192 | s->count = value; |
193 | s->active = TRUE; | |
194 | s->owner = task; | |
1c79356b A |
195 | |
196 | /* | |
197 | * Associate the new semaphore with the task by adding | |
198 | * the new semaphore to the task's semaphore list. | |
1c79356b A |
199 | */ |
200 | task_lock(task); | |
201 | enqueue_head(&task->semaphore_list, (queue_entry_t) s); | |
202 | task->semaphores_owned++; | |
1c79356b A |
203 | task_unlock(task); |
204 | ||
205 | *new_semaphore = s; | |
206 | ||
207 | return KERN_SUCCESS; | |
0a7de745 | 208 | } |
1c79356b A |
209 | |
210 | /* | |
3e170ce0 | 211 | * Routine: semaphore_destroy_internal |
1c79356b | 212 | * |
4bd07ac2 A |
213 | * Disassociate a semaphore from its owning task, mark it inactive, |
214 | * and set any waiting threads running with THREAD_RESTART. | |
1c79356b | 215 | * |
4bd07ac2 A |
216 | * Conditions: |
217 | * task is locked | |
218 | * semaphore is locked | |
219 | * semaphore is owned by the specified task | |
220 | * Returns: | |
221 | * with semaphore unlocked | |
1c79356b | 222 | */ |
4bd07ac2 | 223 | static void |
3e170ce0 | 224 | semaphore_destroy_internal( |
0a7de745 A |
225 | task_t task, |
226 | semaphore_t semaphore) | |
1c79356b | 227 | { |
0a7de745 | 228 | int old_count; |
3e170ce0 | 229 | |
4bd07ac2 A |
230 | /* unlink semaphore from owning task */ |
231 | assert(semaphore->owner == task); | |
6d2010ae | 232 | remqueue((queue_entry_t) semaphore); |
1c79356b A |
233 | semaphore->owner = TASK_NULL; |
234 | task->semaphores_owned--; | |
1c79356b | 235 | |
1c79356b A |
236 | /* |
237 | * Deactivate semaphore | |
238 | */ | |
239 | assert(semaphore->active); | |
240 | semaphore->active = FALSE; | |
241 | ||
242 | /* | |
0a7de745 | 243 | * Wakeup blocked threads |
1c79356b A |
244 | */ |
245 | old_count = semaphore->count; | |
246 | semaphore->count = 0; | |
247 | ||
248 | if (old_count < 0) { | |
3e170ce0 | 249 | waitq_wakeup64_all_locked(&semaphore->waitq, |
0a7de745 A |
250 | SEMAPHORE_EVENT, |
251 | THREAD_RESTART, NULL, | |
252 | WAITQ_ALL_PRIORITIES, | |
253 | WAITQ_UNLOCK); | |
3e170ce0 | 254 | /* waitq/semaphore is unlocked */ |
1c79356b A |
255 | } else { |
256 | semaphore_unlock(semaphore); | |
257 | } | |
1c79356b A |
258 | } |
259 | ||
3e170ce0 A |
260 | /* |
261 | * Routine: semaphore_destroy | |
262 | * | |
263 | * Destroys a semaphore and consume the caller's reference on the | |
264 | * semaphore. | |
265 | */ | |
266 | kern_return_t | |
267 | semaphore_destroy( | |
0a7de745 A |
268 | task_t task, |
269 | semaphore_t semaphore) | |
3e170ce0 | 270 | { |
4bd07ac2 | 271 | spl_t spl_level; |
3e170ce0 | 272 | |
0a7de745 | 273 | if (semaphore == SEMAPHORE_NULL) { |
3e170ce0 | 274 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 275 | } |
3e170ce0 A |
276 | |
277 | if (task == TASK_NULL) { | |
4bd07ac2 A |
278 | semaphore_dereference(semaphore); |
279 | return KERN_INVALID_ARGUMENT; | |
280 | } | |
281 | ||
282 | task_lock(task); | |
283 | spl_level = splsched(); | |
284 | semaphore_lock(semaphore); | |
285 | ||
286 | if (semaphore->owner != task) { | |
287 | semaphore_unlock(semaphore); | |
d9a64523 | 288 | semaphore_dereference(semaphore); |
4bd07ac2 A |
289 | splx(spl_level); |
290 | task_unlock(task); | |
291 | return KERN_INVALID_ARGUMENT; | |
3e170ce0 | 292 | } |
d9a64523 | 293 | |
4bd07ac2 A |
294 | semaphore_destroy_internal(task, semaphore); |
295 | /* semaphore unlocked */ | |
296 | ||
297 | splx(spl_level); | |
298 | task_unlock(task); | |
299 | ||
3e170ce0 | 300 | semaphore_dereference(semaphore); |
4bd07ac2 A |
301 | return KERN_SUCCESS; |
302 | } | |
303 | ||
304 | /* | |
305 | * Routine: semaphore_destroy_all | |
306 | * | |
307 | * Destroy all the semaphores associated with a given task. | |
308 | */ | |
309 | #define SEMASPERSPL 20 /* max number of semaphores to destroy per spl hold */ | |
310 | ||
311 | void | |
312 | semaphore_destroy_all( | |
0a7de745 | 313 | task_t task) |
4bd07ac2 A |
314 | { |
315 | uint32_t count; | |
316 | spl_t spl_level; | |
317 | ||
318 | count = 0; | |
319 | task_lock(task); | |
320 | while (!queue_empty(&task->semaphore_list)) { | |
321 | semaphore_t semaphore; | |
322 | ||
323 | semaphore = (semaphore_t) queue_first(&task->semaphore_list); | |
324 | ||
0a7de745 | 325 | if (count == 0) { |
4bd07ac2 | 326 | spl_level = splsched(); |
0a7de745 | 327 | } |
4bd07ac2 A |
328 | semaphore_lock(semaphore); |
329 | ||
330 | semaphore_destroy_internal(task, semaphore); | |
331 | /* semaphore unlocked */ | |
332 | ||
333 | /* throttle number of semaphores per interrupt disablement */ | |
334 | if (++count == SEMASPERSPL) { | |
335 | count = 0; | |
336 | splx(spl_level); | |
337 | } | |
338 | } | |
0a7de745 | 339 | if (count != 0) { |
4bd07ac2 | 340 | splx(spl_level); |
0a7de745 | 341 | } |
4bd07ac2 A |
342 | |
343 | task_unlock(task); | |
3e170ce0 A |
344 | } |
345 | ||
1c79356b A |
346 | /* |
347 | * Routine: semaphore_signal_internal | |
348 | * | |
0a7de745 | 349 | * Signals the semaphore as direct. |
1c79356b A |
350 | * Assumptions: |
351 | * Semaphore is locked. | |
352 | */ | |
353 | kern_return_t | |
354 | semaphore_signal_internal( | |
0a7de745 A |
355 | semaphore_t semaphore, |
356 | thread_t thread, | |
357 | int options) | |
1c79356b A |
358 | { |
359 | kern_return_t kr; | |
360 | spl_t spl_level; | |
361 | ||
362 | spl_level = splsched(); | |
363 | semaphore_lock(semaphore); | |
364 | ||
365 | if (!semaphore->active) { | |
366 | semaphore_unlock(semaphore); | |
367 | splx(spl_level); | |
368 | return KERN_TERMINATED; | |
369 | } | |
370 | ||
91447636 | 371 | if (thread != THREAD_NULL) { |
1c79356b | 372 | if (semaphore->count < 0) { |
3e170ce0 | 373 | kr = waitq_wakeup64_thread_locked( |
0a7de745 A |
374 | &semaphore->waitq, |
375 | SEMAPHORE_EVENT, | |
376 | thread, | |
377 | THREAD_AWAKENED, | |
378 | WAITQ_UNLOCK); | |
3e170ce0 | 379 | /* waitq/semaphore is unlocked */ |
1c79356b | 380 | } else { |
1c79356b | 381 | kr = KERN_NOT_WAITING; |
3e170ce0 | 382 | semaphore_unlock(semaphore); |
1c79356b A |
383 | } |
384 | splx(spl_level); | |
385 | return kr; | |
0a7de745 | 386 | } |
1c79356b A |
387 | |
388 | if (options & SEMAPHORE_SIGNAL_ALL) { | |
389 | int old_count = semaphore->count; | |
390 | ||
3e170ce0 | 391 | kr = KERN_NOT_WAITING; |
1c79356b A |
392 | if (old_count < 0) { |
393 | semaphore->count = 0; /* always reset */ | |
3e170ce0 | 394 | kr = waitq_wakeup64_all_locked( |
0a7de745 A |
395 | &semaphore->waitq, |
396 | SEMAPHORE_EVENT, | |
397 | THREAD_AWAKENED, NULL, | |
398 | WAITQ_ALL_PRIORITIES, | |
399 | WAITQ_UNLOCK); | |
3e170ce0 | 400 | /* waitq / semaphore is unlocked */ |
1c79356b | 401 | } else { |
0a7de745 | 402 | if (options & SEMAPHORE_SIGNAL_PREPOST) { |
1c79356b | 403 | semaphore->count++; |
0a7de745 | 404 | } |
1c79356b | 405 | kr = KERN_SUCCESS; |
3e170ce0 | 406 | semaphore_unlock(semaphore); |
1c79356b A |
407 | } |
408 | splx(spl_level); | |
409 | return kr; | |
410 | } | |
0a7de745 | 411 | |
1c79356b | 412 | if (semaphore->count < 0) { |
3e170ce0 | 413 | kr = waitq_wakeup64_one_locked( |
0a7de745 A |
414 | &semaphore->waitq, |
415 | SEMAPHORE_EVENT, | |
416 | THREAD_AWAKENED, NULL, | |
417 | WAITQ_ALL_PRIORITIES, | |
418 | WAITQ_KEEP_LOCKED); | |
3e170ce0 | 419 | if (kr == KERN_SUCCESS) { |
1c79356b A |
420 | semaphore_unlock(semaphore); |
421 | splx(spl_level); | |
422 | return KERN_SUCCESS; | |
3e170ce0 | 423 | } else { |
1c79356b | 424 | semaphore->count = 0; /* all waiters gone */ |
3e170ce0 | 425 | } |
1c79356b A |
426 | } |
427 | ||
428 | if (options & SEMAPHORE_SIGNAL_PREPOST) { | |
429 | semaphore->count++; | |
430 | } | |
431 | ||
432 | semaphore_unlock(semaphore); | |
433 | splx(spl_level); | |
434 | return KERN_NOT_WAITING; | |
435 | } | |
436 | ||
437 | /* | |
438 | * Routine: semaphore_signal_thread | |
439 | * | |
91447636 A |
440 | * If the specified thread is blocked on the semaphore, it is |
441 | * woken up. If a NULL thread was supplied, then any one | |
1c79356b A |
442 | * thread is woken up. Otherwise the caller gets KERN_NOT_WAITING |
443 | * and the semaphore is unchanged. | |
444 | */ | |
445 | kern_return_t | |
446 | semaphore_signal_thread( | |
0a7de745 A |
447 | semaphore_t semaphore, |
448 | thread_t thread) | |
1c79356b | 449 | { |
0a7de745 | 450 | kern_return_t ret; |
1c79356b | 451 | |
0a7de745 | 452 | if (semaphore == SEMAPHORE_NULL) { |
1c79356b | 453 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 454 | } |
1c79356b A |
455 | |
456 | ret = semaphore_signal_internal(semaphore, | |
0a7de745 A |
457 | thread, |
458 | SEMAPHORE_OPTION_NONE); | |
1c79356b | 459 | return ret; |
0a7de745 | 460 | } |
1c79356b A |
461 | |
462 | /* | |
463 | * Routine: semaphore_signal_thread_trap | |
464 | * | |
465 | * Trap interface to the semaphore_signal_thread function. | |
466 | */ | |
467 | kern_return_t | |
468 | semaphore_signal_thread_trap( | |
91447636 | 469 | struct semaphore_signal_thread_trap_args *args) |
1c79356b | 470 | { |
91447636 A |
471 | mach_port_name_t sema_name = args->signal_name; |
472 | mach_port_name_t thread_name = args->thread_name; | |
0a7de745 A |
473 | semaphore_t semaphore; |
474 | thread_t thread; | |
475 | kern_return_t kr; | |
1c79356b | 476 | |
0a7de745 | 477 | /* |
1c79356b A |
478 | * MACH_PORT_NULL is not an error. It means that we want to |
479 | * select any one thread that is already waiting, but not to | |
480 | * pre-post the semaphore. | |
481 | */ | |
482 | if (thread_name != MACH_PORT_NULL) { | |
91447636 | 483 | thread = port_name_to_thread(thread_name); |
0a7de745 | 484 | if (thread == THREAD_NULL) { |
1c79356b | 485 | return KERN_INVALID_ARGUMENT; |
0a7de745 A |
486 | } |
487 | } else { | |
91447636 | 488 | thread = THREAD_NULL; |
0a7de745 | 489 | } |
1c79356b A |
490 | |
491 | kr = port_name_to_semaphore(sema_name, &semaphore); | |
91447636 A |
492 | if (kr == KERN_SUCCESS) { |
493 | kr = semaphore_signal_internal(semaphore, | |
0a7de745 A |
494 | thread, |
495 | SEMAPHORE_OPTION_NONE); | |
91447636 A |
496 | semaphore_dereference(semaphore); |
497 | } | |
498 | if (thread != THREAD_NULL) { | |
499 | thread_deallocate(thread); | |
1c79356b | 500 | } |
1c79356b A |
501 | return kr; |
502 | } | |
503 | ||
504 | ||
505 | ||
506 | /* | |
507 | * Routine: semaphore_signal | |
508 | * | |
509 | * Traditional (in-kernel client and MIG interface) semaphore | |
510 | * signal routine. Most users will access the trap version. | |
511 | * | |
512 | * This interface in not defined to return info about whether | |
513 | * this call found a thread waiting or not. The internal | |
514 | * routines (and future external routines) do. We have to | |
515 | * convert those into plain KERN_SUCCESS returns. | |
516 | */ | |
517 | kern_return_t | |
518 | semaphore_signal( | |
0a7de745 | 519 | semaphore_t semaphore) |
1c79356b | 520 | { |
0a7de745 | 521 | kern_return_t kr; |
1c79356b | 522 | |
0a7de745 | 523 | if (semaphore == SEMAPHORE_NULL) { |
1c79356b | 524 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 525 | } |
1c79356b A |
526 | |
527 | kr = semaphore_signal_internal(semaphore, | |
0a7de745 A |
528 | THREAD_NULL, |
529 | SEMAPHORE_SIGNAL_PREPOST); | |
530 | if (kr == KERN_NOT_WAITING) { | |
1c79356b | 531 | return KERN_SUCCESS; |
0a7de745 | 532 | } |
1c79356b A |
533 | return kr; |
534 | } | |
535 | ||
536 | /* | |
537 | * Routine: semaphore_signal_trap | |
538 | * | |
539 | * Trap interface to the semaphore_signal function. | |
540 | */ | |
541 | kern_return_t | |
542 | semaphore_signal_trap( | |
91447636 | 543 | struct semaphore_signal_trap_args *args) |
1c79356b | 544 | { |
91447636 | 545 | mach_port_name_t sema_name = args->signal_name; |
2d21ac55 | 546 | |
0a7de745 | 547 | return semaphore_signal_internal_trap(sema_name); |
2d21ac55 A |
548 | } |
549 | ||
550 | kern_return_t | |
551 | semaphore_signal_internal_trap(mach_port_name_t sema_name) | |
552 | { | |
0a7de745 | 553 | semaphore_t semaphore; |
1c79356b A |
554 | kern_return_t kr; |
555 | ||
556 | kr = port_name_to_semaphore(sema_name, &semaphore); | |
91447636 | 557 | if (kr == KERN_SUCCESS) { |
0a7de745 A |
558 | kr = semaphore_signal_internal(semaphore, |
559 | THREAD_NULL, | |
560 | SEMAPHORE_SIGNAL_PREPOST); | |
91447636 | 561 | semaphore_dereference(semaphore); |
0a7de745 | 562 | if (kr == KERN_NOT_WAITING) { |
91447636 | 563 | kr = KERN_SUCCESS; |
0a7de745 | 564 | } |
1c79356b | 565 | } |
1c79356b A |
566 | return kr; |
567 | } | |
568 | ||
569 | /* | |
570 | * Routine: semaphore_signal_all | |
571 | * | |
572 | * Awakens ALL threads currently blocked on the semaphore. | |
573 | * The semaphore count returns to zero. | |
574 | */ | |
575 | kern_return_t | |
576 | semaphore_signal_all( | |
0a7de745 | 577 | semaphore_t semaphore) |
1c79356b A |
578 | { |
579 | kern_return_t kr; | |
580 | ||
0a7de745 | 581 | if (semaphore == SEMAPHORE_NULL) { |
1c79356b | 582 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 583 | } |
1c79356b A |
584 | |
585 | kr = semaphore_signal_internal(semaphore, | |
0a7de745 A |
586 | THREAD_NULL, |
587 | SEMAPHORE_SIGNAL_ALL); | |
588 | if (kr == KERN_NOT_WAITING) { | |
1c79356b | 589 | return KERN_SUCCESS; |
0a7de745 | 590 | } |
1c79356b A |
591 | return kr; |
592 | } | |
593 | ||
594 | /* | |
595 | * Routine: semaphore_signal_all_trap | |
596 | * | |
597 | * Trap interface to the semaphore_signal_all function. | |
598 | */ | |
599 | kern_return_t | |
600 | semaphore_signal_all_trap( | |
91447636 | 601 | struct semaphore_signal_all_trap_args *args) |
1c79356b | 602 | { |
91447636 | 603 | mach_port_name_t sema_name = args->signal_name; |
0a7de745 | 604 | semaphore_t semaphore; |
1c79356b A |
605 | kern_return_t kr; |
606 | ||
607 | kr = port_name_to_semaphore(sema_name, &semaphore); | |
91447636 A |
608 | if (kr == KERN_SUCCESS) { |
609 | kr = semaphore_signal_internal(semaphore, | |
0a7de745 A |
610 | THREAD_NULL, |
611 | SEMAPHORE_SIGNAL_ALL); | |
91447636 | 612 | semaphore_dereference(semaphore); |
0a7de745 | 613 | if (kr == KERN_NOT_WAITING) { |
91447636 | 614 | kr = KERN_SUCCESS; |
0a7de745 | 615 | } |
1c79356b | 616 | } |
1c79356b A |
617 | return kr; |
618 | } | |
619 | ||
620 | /* | |
621 | * Routine: semaphore_convert_wait_result | |
622 | * | |
623 | * Generate the return code after a semaphore wait/block. It | |
624 | * takes the wait result as an input and coverts that to an | |
625 | * appropriate result. | |
626 | */ | |
627 | kern_return_t | |
628 | semaphore_convert_wait_result(int wait_result) | |
629 | { | |
630 | switch (wait_result) { | |
631 | case THREAD_AWAKENED: | |
632 | return KERN_SUCCESS; | |
633 | ||
634 | case THREAD_TIMED_OUT: | |
635 | return KERN_OPERATION_TIMED_OUT; | |
0a7de745 | 636 | |
1c79356b A |
637 | case THREAD_INTERRUPTED: |
638 | return KERN_ABORTED; | |
639 | ||
640 | case THREAD_RESTART: | |
641 | return KERN_TERMINATED; | |
642 | ||
643 | default: | |
644 | panic("semaphore_block\n"); | |
645 | return KERN_FAILURE; | |
646 | } | |
647 | } | |
648 | ||
649 | /* | |
650 | * Routine: semaphore_wait_continue | |
651 | * | |
652 | * Common continuation routine after waiting on a semphore. | |
653 | * It returns directly to user space. | |
654 | */ | |
655 | void | |
656 | semaphore_wait_continue(void) | |
657 | { | |
658 | thread_t self = current_thread(); | |
659 | int wait_result = self->wait_result; | |
660 | void (*caller_cont)(kern_return_t) = self->sth_continuation; | |
661 | ||
662 | assert(self->sth_waitsemaphore != SEMAPHORE_NULL); | |
663 | semaphore_dereference(self->sth_waitsemaphore); | |
0a7de745 | 664 | if (self->sth_signalsemaphore != SEMAPHORE_NULL) { |
1c79356b | 665 | semaphore_dereference(self->sth_signalsemaphore); |
0a7de745 | 666 | } |
1c79356b A |
667 | |
668 | assert(caller_cont != (void (*)(kern_return_t))0); | |
669 | (*caller_cont)(semaphore_convert_wait_result(wait_result)); | |
670 | } | |
671 | ||
1c79356b A |
672 | /* |
673 | * Routine: semaphore_wait_internal | |
674 | * | |
675 | * Decrements the semaphore count by one. If the count is | |
676 | * negative after the decrement, the calling thread blocks | |
677 | * (possibly at a continuation and/or with a timeout). | |
678 | * | |
679 | * Assumptions: | |
680 | * The reference | |
681 | * A reference is held on the signal semaphore. | |
682 | */ | |
b0d623f7 | 683 | static kern_return_t |
1c79356b | 684 | semaphore_wait_internal( |
0a7de745 A |
685 | semaphore_t wait_semaphore, |
686 | semaphore_t signal_semaphore, | |
687 | uint64_t deadline, | |
688 | int option, | |
689 | void (*caller_cont)(kern_return_t)) | |
1c79356b | 690 | { |
0a7de745 A |
691 | int wait_result; |
692 | spl_t spl_level; | |
693 | kern_return_t kr = KERN_ALREADY_WAITING; | |
1c79356b A |
694 | |
695 | spl_level = splsched(); | |
696 | semaphore_lock(wait_semaphore); | |
697 | ||
1c79356b A |
698 | if (!wait_semaphore->active) { |
699 | kr = KERN_TERMINATED; | |
700 | } else if (wait_semaphore->count > 0) { | |
701 | wait_semaphore->count--; | |
702 | kr = KERN_SUCCESS; | |
b0d623f7 | 703 | } else if (option & SEMAPHORE_TIMEOUT_NOBLOCK) { |
1c79356b | 704 | kr = KERN_OPERATION_TIMED_OUT; |
55e303ae | 705 | } else { |
0a7de745 | 706 | thread_t self = current_thread(); |
55e303ae | 707 | |
1c79356b | 708 | wait_semaphore->count = -1; /* we don't keep an actual count */ |
813fb2f6 A |
709 | |
710 | thread_set_pending_block_hint(self, kThreadWaitSemaphore); | |
3e170ce0 | 711 | (void)waitq_assert_wait64_locked( |
0a7de745 A |
712 | &wait_semaphore->waitq, |
713 | SEMAPHORE_EVENT, | |
714 | THREAD_ABORTSAFE, | |
715 | TIMEOUT_URGENCY_USER_NORMAL, | |
716 | deadline, TIMEOUT_NO_LEEWAY, | |
717 | self); | |
1c79356b A |
718 | } |
719 | semaphore_unlock(wait_semaphore); | |
720 | splx(spl_level); | |
721 | ||
722 | /* | |
723 | * wait_semaphore is unlocked so we are free to go ahead and | |
724 | * signal the signal_semaphore (if one was provided). | |
725 | */ | |
726 | if (signal_semaphore != SEMAPHORE_NULL) { | |
727 | kern_return_t signal_kr; | |
728 | ||
729 | /* | |
730 | * lock the signal semaphore reference we got and signal it. | |
731 | * This will NOT block (we cannot block after having asserted | |
732 | * our intention to wait above). | |
733 | */ | |
734 | signal_kr = semaphore_signal_internal(signal_semaphore, | |
0a7de745 A |
735 | THREAD_NULL, |
736 | SEMAPHORE_SIGNAL_PREPOST); | |
1c79356b | 737 | |
0a7de745 | 738 | if (signal_kr == KERN_NOT_WAITING) { |
1c79356b | 739 | signal_kr = KERN_SUCCESS; |
0a7de745 A |
740 | } else if (signal_kr == KERN_TERMINATED) { |
741 | /* | |
1c79356b A |
742 | * Uh!Oh! The semaphore we were to signal died. |
743 | * We have to get ourselves out of the wait in | |
744 | * case we get stuck here forever (it is assumed | |
745 | * that the semaphore we were posting is gating | |
746 | * the decision by someone else to post the | |
747 | * semaphore we are waiting on). People will | |
748 | * discover the other dead semaphore soon enough. | |
749 | * If we got out of the wait cleanly (someone | |
750 | * already posted a wakeup to us) then return that | |
751 | * (most important) result. Otherwise, | |
752 | * return the KERN_TERMINATED status. | |
753 | */ | |
754 | thread_t self = current_thread(); | |
755 | ||
756 | clear_wait(self, THREAD_INTERRUPTED); | |
757 | kr = semaphore_convert_wait_result(self->wait_result); | |
0a7de745 | 758 | if (kr == KERN_ABORTED) { |
1c79356b | 759 | kr = KERN_TERMINATED; |
0a7de745 | 760 | } |
1c79356b A |
761 | } |
762 | } | |
0a7de745 | 763 | |
1c79356b A |
764 | /* |
765 | * If we had an error, or we didn't really need to wait we can | |
766 | * return now that we have signalled the signal semaphore. | |
767 | */ | |
0a7de745 | 768 | if (kr != KERN_ALREADY_WAITING) { |
1c79356b | 769 | return kr; |
0a7de745 | 770 | } |
1c79356b A |
771 | |
772 | /* | |
773 | * Now, we can block. If the caller supplied a continuation | |
774 | * pointer of his own for after the block, block with the | |
775 | * appropriate semaphore continuation. Thiswill gather the | |
776 | * semaphore results, release references on the semaphore(s), | |
777 | * and then call the caller's continuation. | |
778 | */ | |
779 | if (caller_cont) { | |
780 | thread_t self = current_thread(); | |
781 | ||
782 | self->sth_continuation = caller_cont; | |
783 | self->sth_waitsemaphore = wait_semaphore; | |
784 | self->sth_signalsemaphore = signal_semaphore; | |
91447636 | 785 | wait_result = thread_block((thread_continue_t)semaphore_wait_continue); |
0a7de745 | 786 | } else { |
9bccf70c | 787 | wait_result = thread_block(THREAD_CONTINUE_NULL); |
1c79356b A |
788 | } |
789 | ||
0a7de745 | 790 | return semaphore_convert_wait_result(wait_result); |
1c79356b A |
791 | } |
792 | ||
793 | ||
794 | /* | |
795 | * Routine: semaphore_wait | |
796 | * | |
797 | * Traditional (non-continuation) interface presented to | |
0a7de745 | 798 | * in-kernel clients to wait on a semaphore. |
1c79356b A |
799 | */ |
800 | kern_return_t | |
801 | semaphore_wait( | |
0a7de745 A |
802 | semaphore_t semaphore) |
803 | { | |
804 | if (semaphore == SEMAPHORE_NULL) { | |
1c79356b | 805 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 806 | } |
1c79356b | 807 | |
0a7de745 A |
808 | return semaphore_wait_internal(semaphore, |
809 | SEMAPHORE_NULL, | |
810 | 0ULL, SEMAPHORE_OPTION_NONE, | |
811 | (void (*)(kern_return_t))0); | |
b0d623f7 A |
812 | } |
813 | ||
814 | kern_return_t | |
815 | semaphore_wait_noblock( | |
0a7de745 A |
816 | semaphore_t semaphore) |
817 | { | |
818 | if (semaphore == SEMAPHORE_NULL) { | |
b0d623f7 | 819 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 820 | } |
b0d623f7 | 821 | |
0a7de745 A |
822 | return semaphore_wait_internal(semaphore, |
823 | SEMAPHORE_NULL, | |
824 | 0ULL, SEMAPHORE_TIMEOUT_NOBLOCK, | |
825 | (void (*)(kern_return_t))0); | |
b0d623f7 A |
826 | } |
827 | ||
828 | kern_return_t | |
829 | semaphore_wait_deadline( | |
0a7de745 A |
830 | semaphore_t semaphore, |
831 | uint64_t deadline) | |
832 | { | |
833 | if (semaphore == SEMAPHORE_NULL) { | |
b0d623f7 | 834 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 835 | } |
b0d623f7 | 836 | |
0a7de745 A |
837 | return semaphore_wait_internal(semaphore, |
838 | SEMAPHORE_NULL, | |
839 | deadline, SEMAPHORE_OPTION_NONE, | |
840 | (void (*)(kern_return_t))0); | |
1c79356b A |
841 | } |
842 | ||
843 | /* | |
844 | * Trap: semaphore_wait_trap | |
845 | * | |
846 | * Trap version of semaphore wait. Called on behalf of user-level | |
847 | * clients. | |
848 | */ | |
91447636 | 849 | |
1c79356b A |
850 | kern_return_t |
851 | semaphore_wait_trap( | |
91447636 A |
852 | struct semaphore_wait_trap_args *args) |
853 | { | |
0a7de745 | 854 | return semaphore_wait_trap_internal(args->wait_name, thread_syscall_return); |
91447636 A |
855 | } |
856 | ||
857 | ||
858 | ||
859 | kern_return_t | |
860 | semaphore_wait_trap_internal( | |
0a7de745 | 861 | mach_port_name_t name, |
91447636 | 862 | void (*caller_cont)(kern_return_t)) |
0a7de745 A |
863 | { |
864 | semaphore_t semaphore; | |
1c79356b A |
865 | kern_return_t kr; |
866 | ||
867 | kr = port_name_to_semaphore(name, &semaphore); | |
91447636 A |
868 | if (kr == KERN_SUCCESS) { |
869 | kr = semaphore_wait_internal(semaphore, | |
0a7de745 A |
870 | SEMAPHORE_NULL, |
871 | 0ULL, SEMAPHORE_OPTION_NONE, | |
872 | caller_cont); | |
91447636 A |
873 | semaphore_dereference(semaphore); |
874 | } | |
1c79356b A |
875 | return kr; |
876 | } | |
877 | ||
878 | /* | |
879 | * Routine: semaphore_timedwait | |
880 | * | |
881 | * Traditional (non-continuation) interface presented to | |
0a7de745 | 882 | * in-kernel clients to wait on a semaphore with a timeout. |
1c79356b A |
883 | * |
884 | * A timeout of {0,0} is considered non-blocking. | |
885 | */ | |
886 | kern_return_t | |
887 | semaphore_timedwait( | |
0a7de745 A |
888 | semaphore_t semaphore, |
889 | mach_timespec_t wait_time) | |
b0d623f7 | 890 | { |
0a7de745 A |
891 | int option = SEMAPHORE_OPTION_NONE; |
892 | uint64_t deadline = 0; | |
b0d623f7 | 893 | |
0a7de745 | 894 | if (semaphore == SEMAPHORE_NULL) { |
1c79356b | 895 | return KERN_INVALID_ARGUMENT; |
0a7de745 A |
896 | } |
897 | ||
898 | if (BAD_MACH_TIMESPEC(&wait_time)) { | |
1c79356b | 899 | return KERN_INVALID_VALUE; |
0a7de745 | 900 | } |
b0d623f7 | 901 | |
0a7de745 | 902 | if (wait_time.tv_sec == 0 && wait_time.tv_nsec == 0) { |
b0d623f7 | 903 | option = SEMAPHORE_TIMEOUT_NOBLOCK; |
0a7de745 | 904 | } else { |
b0d623f7 | 905 | deadline = semaphore_deadline(wait_time.tv_sec, wait_time.tv_nsec); |
0a7de745 A |
906 | } |
907 | ||
908 | return semaphore_wait_internal(semaphore, | |
909 | SEMAPHORE_NULL, | |
910 | deadline, option, | |
911 | (void (*)(kern_return_t))0); | |
1c79356b A |
912 | } |
913 | ||
914 | /* | |
915 | * Trap: semaphore_timedwait_trap | |
916 | * | |
917 | * Trap version of a semaphore_timedwait. The timeout parameter | |
918 | * is passed in two distinct parts and re-assembled on this side | |
919 | * of the trap interface (to accomodate calling conventions that | |
920 | * pass structures as pointers instead of inline in registers without | |
921 | * having to add a copyin). | |
922 | * | |
923 | * A timeout of {0,0} is considered non-blocking. | |
924 | */ | |
925 | kern_return_t | |
926 | semaphore_timedwait_trap( | |
91447636 | 927 | struct semaphore_timedwait_trap_args *args) |
0a7de745 A |
928 | { |
929 | return semaphore_timedwait_trap_internal(args->wait_name, args->sec, args->nsec, thread_syscall_return); | |
91447636 A |
930 | } |
931 | ||
932 | ||
933 | kern_return_t | |
934 | semaphore_timedwait_trap_internal( | |
935 | mach_port_name_t name, | |
936 | unsigned int sec, | |
937 | clock_res_t nsec, | |
938 | void (*caller_cont)(kern_return_t)) | |
939 | { | |
1c79356b A |
940 | semaphore_t semaphore; |
941 | mach_timespec_t wait_time; | |
942 | kern_return_t kr; | |
943 | ||
944 | wait_time.tv_sec = sec; | |
945 | wait_time.tv_nsec = nsec; | |
0a7de745 | 946 | if (BAD_MACH_TIMESPEC(&wait_time)) { |
1c79356b | 947 | return KERN_INVALID_VALUE; |
0a7de745 A |
948 | } |
949 | ||
1c79356b | 950 | kr = port_name_to_semaphore(name, &semaphore); |
91447636 | 951 | if (kr == KERN_SUCCESS) { |
0a7de745 A |
952 | int option = SEMAPHORE_OPTION_NONE; |
953 | uint64_t deadline = 0; | |
b0d623f7 | 954 | |
0a7de745 | 955 | if (sec == 0 && nsec == 0) { |
b0d623f7 | 956 | option = SEMAPHORE_TIMEOUT_NOBLOCK; |
0a7de745 | 957 | } else { |
b0d623f7 | 958 | deadline = semaphore_deadline(sec, nsec); |
0a7de745 | 959 | } |
b0d623f7 | 960 | |
91447636 | 961 | kr = semaphore_wait_internal(semaphore, |
0a7de745 A |
962 | SEMAPHORE_NULL, |
963 | deadline, option, | |
964 | caller_cont); | |
91447636 A |
965 | semaphore_dereference(semaphore); |
966 | } | |
1c79356b A |
967 | return kr; |
968 | } | |
969 | ||
970 | /* | |
971 | * Routine: semaphore_wait_signal | |
972 | * | |
973 | * Atomically register a wait on a semaphore and THEN signal | |
974 | * another. This is the in-kernel entry point that does not | |
975 | * block at a continuation and does not free a signal_semaphore | |
976 | * reference. | |
977 | */ | |
978 | kern_return_t | |
979 | semaphore_wait_signal( | |
0a7de745 A |
980 | semaphore_t wait_semaphore, |
981 | semaphore_t signal_semaphore) | |
1c79356b | 982 | { |
0a7de745 | 983 | if (wait_semaphore == SEMAPHORE_NULL) { |
1c79356b | 984 | return KERN_INVALID_ARGUMENT; |
0a7de745 A |
985 | } |
986 | ||
987 | return semaphore_wait_internal(wait_semaphore, | |
988 | signal_semaphore, | |
989 | 0ULL, SEMAPHORE_OPTION_NONE, | |
990 | (void (*)(kern_return_t))0); | |
1c79356b A |
991 | } |
992 | ||
993 | /* | |
994 | * Trap: semaphore_wait_signal_trap | |
995 | * | |
996 | * Atomically register a wait on a semaphore and THEN signal | |
0a7de745 | 997 | * another. This is the trap version from user space. |
1c79356b A |
998 | */ |
999 | kern_return_t | |
1000 | semaphore_wait_signal_trap( | |
91447636 A |
1001 | struct semaphore_wait_signal_trap_args *args) |
1002 | { | |
0a7de745 | 1003 | return semaphore_wait_signal_trap_internal(args->wait_name, args->signal_name, thread_syscall_return); |
91447636 A |
1004 | } |
1005 | ||
1006 | kern_return_t | |
1007 | semaphore_wait_signal_trap_internal( | |
1008 | mach_port_name_t wait_name, | |
1009 | mach_port_name_t signal_name, | |
1010 | void (*caller_cont)(kern_return_t)) | |
1c79356b A |
1011 | { |
1012 | semaphore_t wait_semaphore; | |
1013 | semaphore_t signal_semaphore; | |
1014 | kern_return_t kr; | |
1015 | ||
1016 | kr = port_name_to_semaphore(signal_name, &signal_semaphore); | |
91447636 A |
1017 | if (kr == KERN_SUCCESS) { |
1018 | kr = port_name_to_semaphore(wait_name, &wait_semaphore); | |
1019 | if (kr == KERN_SUCCESS) { | |
1020 | kr = semaphore_wait_internal(wait_semaphore, | |
0a7de745 A |
1021 | signal_semaphore, |
1022 | 0ULL, SEMAPHORE_OPTION_NONE, | |
1023 | caller_cont); | |
91447636 A |
1024 | semaphore_dereference(wait_semaphore); |
1025 | } | |
1c79356b | 1026 | semaphore_dereference(signal_semaphore); |
1c79356b | 1027 | } |
1c79356b A |
1028 | return kr; |
1029 | } | |
1030 | ||
1031 | ||
1032 | /* | |
1033 | * Routine: semaphore_timedwait_signal | |
1034 | * | |
1035 | * Atomically register a wait on a semaphore and THEN signal | |
1036 | * another. This is the in-kernel entry point that does not | |
1037 | * block at a continuation. | |
1038 | * | |
1039 | * A timeout of {0,0} is considered non-blocking. | |
1040 | */ | |
1041 | kern_return_t | |
1042 | semaphore_timedwait_signal( | |
0a7de745 A |
1043 | semaphore_t wait_semaphore, |
1044 | semaphore_t signal_semaphore, | |
1045 | mach_timespec_t wait_time) | |
1c79356b | 1046 | { |
0a7de745 A |
1047 | int option = SEMAPHORE_OPTION_NONE; |
1048 | uint64_t deadline = 0; | |
b0d623f7 | 1049 | |
0a7de745 | 1050 | if (wait_semaphore == SEMAPHORE_NULL) { |
1c79356b | 1051 | return KERN_INVALID_ARGUMENT; |
0a7de745 A |
1052 | } |
1053 | ||
1054 | if (BAD_MACH_TIMESPEC(&wait_time)) { | |
1c79356b | 1055 | return KERN_INVALID_VALUE; |
0a7de745 | 1056 | } |
b0d623f7 | 1057 | |
0a7de745 | 1058 | if (wait_time.tv_sec == 0 && wait_time.tv_nsec == 0) { |
b0d623f7 | 1059 | option = SEMAPHORE_TIMEOUT_NOBLOCK; |
0a7de745 | 1060 | } else { |
b0d623f7 | 1061 | deadline = semaphore_deadline(wait_time.tv_sec, wait_time.tv_nsec); |
0a7de745 A |
1062 | } |
1063 | ||
1064 | return semaphore_wait_internal(wait_semaphore, | |
1065 | signal_semaphore, | |
1066 | deadline, option, | |
1067 | (void (*)(kern_return_t))0); | |
1c79356b A |
1068 | } |
1069 | ||
1070 | /* | |
1071 | * Trap: semaphore_timedwait_signal_trap | |
1072 | * | |
1073 | * Atomically register a timed wait on a semaphore and THEN signal | |
0a7de745 | 1074 | * another. This is the trap version from user space. |
1c79356b A |
1075 | */ |
1076 | kern_return_t | |
1077 | semaphore_timedwait_signal_trap( | |
91447636 A |
1078 | struct semaphore_timedwait_signal_trap_args *args) |
1079 | { | |
0a7de745 | 1080 | return semaphore_timedwait_signal_trap_internal(args->wait_name, args->signal_name, args->sec, args->nsec, thread_syscall_return); |
91447636 A |
1081 | } |
1082 | ||
1083 | kern_return_t | |
1084 | semaphore_timedwait_signal_trap_internal( | |
1085 | mach_port_name_t wait_name, | |
1086 | mach_port_name_t signal_name, | |
1087 | unsigned int sec, | |
1088 | clock_res_t nsec, | |
1089 | void (*caller_cont)(kern_return_t)) | |
1c79356b A |
1090 | { |
1091 | semaphore_t wait_semaphore; | |
1092 | semaphore_t signal_semaphore; | |
1093 | mach_timespec_t wait_time; | |
1094 | kern_return_t kr; | |
1095 | ||
1096 | wait_time.tv_sec = sec; | |
1097 | wait_time.tv_nsec = nsec; | |
0a7de745 | 1098 | if (BAD_MACH_TIMESPEC(&wait_time)) { |
1c79356b | 1099 | return KERN_INVALID_VALUE; |
0a7de745 A |
1100 | } |
1101 | ||
1c79356b | 1102 | kr = port_name_to_semaphore(signal_name, &signal_semaphore); |
91447636 A |
1103 | if (kr == KERN_SUCCESS) { |
1104 | kr = port_name_to_semaphore(wait_name, &wait_semaphore); | |
1105 | if (kr == KERN_SUCCESS) { | |
0a7de745 A |
1106 | int option = SEMAPHORE_OPTION_NONE; |
1107 | uint64_t deadline = 0; | |
b0d623f7 | 1108 | |
0a7de745 | 1109 | if (sec == 0 && nsec == 0) { |
b0d623f7 | 1110 | option = SEMAPHORE_TIMEOUT_NOBLOCK; |
0a7de745 | 1111 | } else { |
b0d623f7 | 1112 | deadline = semaphore_deadline(sec, nsec); |
0a7de745 | 1113 | } |
b0d623f7 | 1114 | |
91447636 | 1115 | kr = semaphore_wait_internal(wait_semaphore, |
0a7de745 A |
1116 | signal_semaphore, |
1117 | deadline, option, | |
1118 | caller_cont); | |
91447636 A |
1119 | semaphore_dereference(wait_semaphore); |
1120 | } | |
1c79356b | 1121 | semaphore_dereference(signal_semaphore); |
1c79356b | 1122 | } |
1c79356b A |
1123 | return kr; |
1124 | } | |
1125 | ||
1126 | ||
1127 | /* | |
1128 | * Routine: semaphore_reference | |
1129 | * | |
1130 | * Take out a reference on a semaphore. This keeps the data structure | |
1131 | * in existence (but the semaphore may be deactivated). | |
1132 | */ | |
1133 | void | |
1134 | semaphore_reference( | |
0a7de745 | 1135 | semaphore_t semaphore) |
1c79356b | 1136 | { |
d9a64523 | 1137 | os_ref_retain(&semaphore->ref_count); |
1c79356b A |
1138 | } |
1139 | ||
1140 | /* | |
1141 | * Routine: semaphore_dereference | |
1142 | * | |
1143 | * Release a reference on a semaphore. If this is the last reference, | |
1144 | * the semaphore data structure is deallocated. | |
1145 | */ | |
1146 | void | |
1147 | semaphore_dereference( | |
0a7de745 | 1148 | semaphore_t semaphore) |
1c79356b | 1149 | { |
4bd07ac2 A |
1150 | uint32_t collisions; |
1151 | spl_t spl_level; | |
1152 | ||
0a7de745 | 1153 | if (semaphore == NULL) { |
3e170ce0 | 1154 | return; |
0a7de745 | 1155 | } |
3e170ce0 | 1156 | |
d9a64523 | 1157 | if (os_ref_release(&semaphore->ref_count) > 0) { |
3e170ce0 | 1158 | return; |
d9a64523 | 1159 | } |
3e170ce0 A |
1160 | |
1161 | /* | |
1162 | * Last ref, clean up the port [if any] | |
1163 | * associated with the semaphore, destroy | |
1164 | * it (if still active) and then free | |
1165 | * the semaphore. | |
1166 | */ | |
1167 | ipc_port_t port = semaphore->port; | |
1168 | ||
1169 | if (IP_VALID(port)) { | |
1170 | assert(!port->ip_srights); | |
1171 | ipc_port_dealloc_kernel(port); | |
1172 | } | |
4bd07ac2 A |
1173 | |
1174 | /* | |
1175 | * Lock the semaphore to lock in the owner task reference. | |
1176 | * Then continue to try to lock the task (inverse order). | |
1177 | */ | |
1178 | spl_level = splsched(); | |
1179 | semaphore_lock(semaphore); | |
1180 | for (collisions = 0; semaphore->active; collisions++) { | |
1181 | task_t task = semaphore->owner; | |
1182 | ||
1183 | assert(task != TASK_NULL); | |
0a7de745 | 1184 | |
4bd07ac2 A |
1185 | if (task_lock_try(task)) { |
1186 | semaphore_destroy_internal(task, semaphore); | |
1187 | /* semaphore unlocked */ | |
1188 | splx(spl_level); | |
1189 | task_unlock(task); | |
1190 | goto out; | |
1191 | } | |
0a7de745 | 1192 | |
4bd07ac2 A |
1193 | /* failed to get out-of-order locks */ |
1194 | semaphore_unlock(semaphore); | |
1195 | splx(spl_level); | |
1196 | mutex_pause(collisions); | |
1197 | spl_level = splsched(); | |
1198 | semaphore_lock(semaphore); | |
1c79356b | 1199 | } |
4bd07ac2 A |
1200 | semaphore_unlock(semaphore); |
1201 | splx(spl_level); | |
1202 | ||
0a7de745 | 1203 | out: |
3e170ce0 | 1204 | zfree(semaphore_zone, semaphore); |
1c79356b | 1205 | } |
3e170ce0 | 1206 | |
813fb2f6 A |
1207 | #define WAITQ_TO_SEMA(wq) ((semaphore_t) ((uintptr_t)(wq) - offsetof(struct semaphore, waitq))) |
1208 | void | |
1209 | kdp_sema_find_owner(struct waitq * waitq, __assert_only event64_t event, thread_waitinfo_t * waitinfo) | |
1210 | { | |
1211 | semaphore_t sem = WAITQ_TO_SEMA(waitq); | |
1212 | assert(event == SEMAPHORE_EVENT); | |
1213 | assert(kdp_is_in_zone(sem, "semaphores")); | |
3e170ce0 | 1214 | |
813fb2f6 | 1215 | waitinfo->context = VM_KERNEL_UNSLIDE_OR_PERM(sem->port); |
0a7de745 | 1216 | if (sem->owner) { |
813fb2f6 | 1217 | waitinfo->owner = pid_from_task(sem->owner); |
0a7de745 | 1218 | } |
813fb2f6 | 1219 | } |