]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 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. | |
8f6c56a5 | 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. | |
17 | * | |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b | 27 | */ |
1c79356b | 28 | |
91447636 | 29 | #ifdef KERNEL_PRIVATE |
9bccf70c | 30 | |
91447636 A |
31 | #ifndef _KERN_WAIT_QUEUE_H_ |
32 | #define _KERN_WAIT_QUEUE_H_ | |
9bccf70c | 33 | |
91447636 | 34 | #include <mach/mach_types.h> |
1c79356b | 35 | #include <mach/sync_policy.h> |
0b4e3aa0 | 36 | #include <mach/kern_return.h> /* for kern_return_t */ |
1c79356b | 37 | |
9bccf70c | 38 | #include <kern/kern_types.h> /* for wait_queue_t */ |
316670eb | 39 | #include <kern/queue.h> |
9bccf70c | 40 | |
91447636 A |
41 | #include <sys/cdefs.h> |
42 | ||
43 | #ifdef MACH_KERNEL_PRIVATE | |
1c79356b A |
44 | |
45 | #include <kern/lock.h> | |
6d2010ae | 46 | #include <mach/branch_predicates.h> |
9bccf70c | 47 | |
060df5ea A |
48 | #include <machine/cpu_number.h> |
49 | #include <machine/machine_routines.h> /* machine_timeout_suspended() */ | |
1c79356b A |
50 | /* |
51 | * wait_queue_t | |
52 | * This is the definition of the common event wait queue | |
53 | * that the scheduler APIs understand. It is used | |
54 | * internally by the gerneralized event waiting mechanism | |
55 | * (assert_wait), and also for items that maintain their | |
56 | * own wait queues (such as ports and semaphores). | |
57 | * | |
58 | * It is not published to other kernel components. They | |
59 | * can create wait queues by calling wait_queue_alloc. | |
60 | * | |
61 | * NOTE: Hardware locks are used to protect event wait | |
62 | * queues since interrupt code is free to post events to | |
63 | * them. | |
64 | */ | |
65 | typedef struct wait_queue { | |
9bccf70c A |
66 | unsigned int /* flags */ |
67 | /* boolean_t */ wq_type:16, /* only public field */ | |
68 | wq_fifo:1, /* fifo wakeup policy? */ | |
b0d623f7 | 69 | wq_prepost:1, /* waitq supports prepost? set only */ |
9bccf70c | 70 | :0; /* force to long boundary */ |
1c79356b | 71 | hw_lock_data_t wq_interlock; /* interlock */ |
9bccf70c | 72 | queue_head_t wq_queue; /* queue of elements */ |
1c79356b A |
73 | } WaitQueue; |
74 | ||
75 | /* | |
9bccf70c A |
76 | * wait_queue_set_t |
77 | * This is the common definition for a set wait queue. | |
1c79356b A |
78 | * These can be linked as members/elements of multiple regular |
79 | * wait queues. They have an additional set of linkages to | |
80 | * identify the linkage structures that point to them. | |
81 | */ | |
9bccf70c A |
82 | typedef struct wait_queue_set { |
83 | WaitQueue wqs_wait_queue; /* our wait queue */ | |
84 | queue_head_t wqs_setlinks; /* links from set perspective */ | |
b0d623f7 | 85 | queue_head_t wqs_preposts; /* preposted links */ |
9bccf70c | 86 | } WaitQueueSet; |
1c79356b | 87 | |
9bccf70c A |
88 | #define wqs_type wqs_wait_queue.wq_type |
89 | #define wqs_fifo wqs_wait_queue.wq_fifo | |
b0d623f7 | 90 | #define wqs_prepost wqs_wait_queue.wq_prepost |
9bccf70c | 91 | #define wqs_queue wqs_wait_queue.wq_queue |
1c79356b A |
92 | |
93 | /* | |
94 | * wait_queue_element_t | |
95 | * This structure describes the elements on an event wait | |
96 | * queue. It is the common first fields in a thread shuttle | |
97 | * and wait_queue_link_t. In that way, a wait queue can | |
98 | * consist of both thread shuttle elements and links off of | |
9bccf70c | 99 | * to other (set) wait queues. |
1c79356b | 100 | * |
9bccf70c A |
101 | * WARNING: These fields correspond to fields in the thread |
102 | * shuttle (run queue links and run queue pointer). Any change in | |
1c79356b A |
103 | * the layout here will have to be matched with a change there. |
104 | */ | |
105 | typedef struct wait_queue_element { | |
106 | queue_chain_t wqe_links; /* link of elements on this queue */ | |
9bccf70c | 107 | void * wqe_type; /* Identifies link vs. thread */ |
1c79356b | 108 | wait_queue_t wqe_queue; /* queue this element is on */ |
9bccf70c | 109 | } WaitQueueElement; |
1c79356b | 110 | |
9bccf70c | 111 | typedef WaitQueueElement *wait_queue_element_t; |
0b4e3aa0 | 112 | |
1c79356b A |
113 | /* |
114 | * wait_queue_link_t | |
9bccf70c | 115 | * Specialized wait queue element type for linking set |
1c79356b A |
116 | * event waits queues onto a wait queue. In this way, an event |
117 | * can be constructed so that any thread waiting on any number | |
118 | * of associated wait queues can handle the event, while letting | |
119 | * the thread only be linked on the single wait queue it blocked on. | |
120 | * | |
121 | * One use: ports in multiple portsets. Each thread is queued up | |
122 | * on the portset that it specifically blocked on during a receive | |
123 | * operation. Each port's event queue links in all the portset | |
124 | * event queues of which it is a member. An IPC event post associated | |
125 | * with that port may wake up any thread from any of those portsets, | |
126 | * or one that was waiting locally on the port itself. | |
127 | */ | |
2d21ac55 | 128 | typedef struct _wait_queue_link { |
9bccf70c A |
129 | WaitQueueElement wql_element; /* element on master */ |
130 | queue_chain_t wql_setlinks; /* element on set */ | |
b0d623f7 | 131 | queue_chain_t wql_preposts; /* element on set prepost list */ |
9bccf70c | 132 | wait_queue_set_t wql_setqueue; /* set queue */ |
0b4e3aa0 A |
133 | } WaitQueueLink; |
134 | ||
1c79356b | 135 | #define wql_links wql_element.wqe_links |
9bccf70c | 136 | #define wql_type wql_element.wqe_type |
1c79356b | 137 | #define wql_queue wql_element.wqe_queue |
0b4e3aa0 | 138 | |
9bccf70c A |
139 | #define _WAIT_QUEUE_inited 0xf1d0 |
140 | #define _WAIT_QUEUE_SET_inited 0xf1d1 | |
0b4e3aa0 | 141 | |
9bccf70c A |
142 | #define wait_queue_is_queue(wq) \ |
143 | ((wq)->wq_type == _WAIT_QUEUE_inited) | |
1c79356b | 144 | |
9bccf70c A |
145 | #define wait_queue_is_set(wqs) \ |
146 | ((wqs)->wqs_type == _WAIT_QUEUE_SET_inited) | |
1c79356b | 147 | |
9bccf70c A |
148 | #define wait_queue_is_valid(wq) \ |
149 | (((wq)->wq_type & ~1) == _WAIT_QUEUE_inited) | |
1c79356b | 150 | |
9bccf70c | 151 | #define wait_queue_empty(wq) (queue_empty(&(wq)->wq_queue)) |
2d21ac55 | 152 | |
9bccf70c A |
153 | #define wait_queue_held(wq) (hw_lock_held(&(wq)->wq_interlock)) |
154 | #define wait_queue_lock_try(wq) (hw_lock_try(&(wq)->wq_interlock)) | |
1c79356b | 155 | |
2d21ac55 | 156 | /* For x86, the hardware timeout is in TSC units. */ |
6d2010ae | 157 | #if defined(i386) || defined(x86_64) |
2d21ac55 A |
158 | #define hwLockTimeOut LockTimeOutTSC |
159 | #else | |
160 | #define hwLockTimeOut LockTimeOut | |
161 | #endif | |
9bccf70c A |
162 | /* |
163 | * Double the standard lock timeout, because wait queues tend | |
164 | * to iterate over a number of threads - locking each. If there is | |
165 | * a problem with a thread lock, it normally times out at the wait | |
166 | * queue level first, hiding the real problem. | |
167 | */ | |
1c79356b | 168 | |
0c530ab8 | 169 | static inline void wait_queue_lock(wait_queue_t wq) { |
6d2010ae | 170 | if (__improbable(hw_lock_to(&(wq)->wq_interlock, hwLockTimeOut * 2) == 0)) { |
060df5ea | 171 | boolean_t wql_acquired = FALSE; |
6d2010ae | 172 | |
060df5ea A |
173 | while (machine_timeout_suspended()) { |
174 | #if defined(__i386__) || defined(__x86_64__) | |
175 | /* | |
176 | * i386/x86_64 return with preemption disabled on a timeout for | |
177 | * diagnostic purposes. | |
178 | */ | |
179 | mp_enable_preemption(); | |
180 | #endif | |
181 | if ((wql_acquired = hw_lock_to(&(wq)->wq_interlock, hwLockTimeOut * 2))) | |
182 | break; | |
183 | } | |
060df5ea A |
184 | if (wql_acquired == FALSE) |
185 | panic("wait queue deadlock - wq=%p, cpu=%d\n", wq, cpu_number()); | |
186 | } | |
0c530ab8 | 187 | } |
060df5ea | 188 | |
0c530ab8 A |
189 | static inline void wait_queue_unlock(wait_queue_t wq) { |
190 | assert(wait_queue_held(wq)); | |
2d21ac55 | 191 | hw_lock_unlock(&(wq)->wq_interlock); |
0c530ab8 | 192 | } |
1c79356b | 193 | |
9bccf70c A |
194 | #define wqs_lock(wqs) wait_queue_lock(&(wqs)->wqs_wait_queue) |
195 | #define wqs_unlock(wqs) wait_queue_unlock(&(wqs)->wqs_wait_queue) | |
196 | #define wqs_lock_try(wqs) wait_queue__try_lock(&(wqs)->wqs_wait_queue) | |
b0d623f7 A |
197 | #define wqs_is_preposted(wqs) ((wqs)->wqs_prepost && !queue_empty(&(wqs)->wqs_preposts)) |
198 | ||
199 | #define wql_is_preposted(wql) ((wql)->wql_preposts.next != NULL) | |
200 | #define wql_clear_prepost(wql) ((wql)->wql_preposts.next = (wql)->wql_preposts.prev = NULL) | |
1c79356b A |
201 | |
202 | #define wait_queue_assert_possible(thread) \ | |
203 | ((thread)->wait_queue == WAIT_QUEUE_NULL) | |
204 | ||
b0d623f7 A |
205 | /* bootstrap interface - can allocate/link wait_queues and sets after calling this */ |
206 | __private_extern__ void wait_queue_bootstrap(void); | |
207 | ||
1c79356b A |
208 | /******** Decomposed interfaces (to build higher level constructs) ***********/ |
209 | ||
1c79356b | 210 | /* assert intent to wait on a locked wait queue */ |
9bccf70c | 211 | __private_extern__ wait_result_t wait_queue_assert_wait64_locked( |
1c79356b | 212 | wait_queue_t wait_queue, |
9bccf70c A |
213 | event64_t wait_event, |
214 | wait_interrupt_t interruptible, | |
91447636 | 215 | uint64_t deadline, |
55e303ae | 216 | thread_t thread); |
1c79356b | 217 | |
2d21ac55 | 218 | /* pull a thread from its wait queue */ |
9bccf70c | 219 | __private_extern__ void wait_queue_pull_thread_locked( |
1c79356b A |
220 | wait_queue_t wait_queue, |
221 | thread_t thread, | |
222 | boolean_t unlock); | |
223 | ||
224 | /* wakeup all threads waiting for a particular event on locked queue */ | |
9bccf70c | 225 | __private_extern__ kern_return_t wait_queue_wakeup64_all_locked( |
1c79356b | 226 | wait_queue_t wait_queue, |
9bccf70c A |
227 | event64_t wake_event, |
228 | wait_result_t result, | |
1c79356b A |
229 | boolean_t unlock); |
230 | ||
231 | /* wakeup one thread waiting for a particular event on locked queue */ | |
9bccf70c | 232 | __private_extern__ kern_return_t wait_queue_wakeup64_one_locked( |
1c79356b | 233 | wait_queue_t wait_queue, |
9bccf70c A |
234 | event64_t wake_event, |
235 | wait_result_t result, | |
1c79356b A |
236 | boolean_t unlock); |
237 | ||
1c79356b | 238 | /* return identity of a thread awakened for a particular <wait_queue,event> */ |
9bccf70c | 239 | __private_extern__ thread_t wait_queue_wakeup64_identity_locked( |
1c79356b | 240 | wait_queue_t wait_queue, |
9bccf70c A |
241 | event64_t wake_event, |
242 | wait_result_t result, | |
1c79356b A |
243 | boolean_t unlock); |
244 | ||
245 | /* wakeup thread iff its still waiting for a particular event on locked queue */ | |
9bccf70c | 246 | __private_extern__ kern_return_t wait_queue_wakeup64_thread_locked( |
1c79356b | 247 | wait_queue_t wait_queue, |
9bccf70c | 248 | event64_t wake_event, |
1c79356b | 249 | thread_t thread, |
9bccf70c | 250 | wait_result_t result, |
1c79356b A |
251 | boolean_t unlock); |
252 | ||
b0d623f7 A |
253 | __private_extern__ uint32_t num_wait_queues; |
254 | __private_extern__ struct wait_queue *wait_queues; | |
255 | /* The Jenkins "one at a time" hash. | |
256 | * TBD: There may be some value to unrolling here, | |
257 | * depending on the architecture. | |
258 | */ | |
259 | static inline uint32_t wq_hash(char *key) | |
260 | { | |
261 | uint32_t hash = 0; | |
262 | size_t i, length = sizeof(char *); | |
263 | ||
264 | for (i = 0; i < length; i++) { | |
265 | hash += key[i]; | |
266 | hash += (hash << 10); | |
267 | hash ^= (hash >> 6); | |
268 | } | |
269 | ||
270 | hash += (hash << 3); | |
271 | hash ^= (hash >> 11); | |
272 | hash += (hash << 15); | |
273 | ||
316670eb | 274 | hash &= (num_wait_queues - 1); |
b0d623f7 A |
275 | return hash; |
276 | } | |
277 | ||
316670eb | 278 | #define wait_hash(event) wq_hash((char *)&event) |
b0d623f7 | 279 | |
91447636 A |
280 | #endif /* MACH_KERNEL_PRIVATE */ |
281 | ||
282 | __BEGIN_DECLS | |
1c79356b | 283 | |
9bccf70c A |
284 | /******** Semi-Public interfaces (not a part of a higher construct) ************/ |
285 | ||
91447636 A |
286 | extern unsigned int wait_queue_set_size(void); |
287 | extern unsigned int wait_queue_link_size(void); | |
288 | ||
9bccf70c A |
289 | extern kern_return_t wait_queue_init( |
290 | wait_queue_t wait_queue, | |
291 | int policy); | |
292 | ||
293 | extern wait_queue_set_t wait_queue_set_alloc( | |
294 | int policy); | |
295 | ||
91447636 A |
296 | extern kern_return_t wait_queue_set_init( |
297 | wait_queue_set_t set_queue, | |
298 | int policy); | |
299 | ||
9bccf70c A |
300 | extern kern_return_t wait_queue_set_free( |
301 | wait_queue_set_t set_queue); | |
302 | ||
303 | extern wait_queue_link_t wait_queue_link_alloc( | |
304 | int policy); | |
305 | ||
306 | extern kern_return_t wait_queue_link_free( | |
307 | wait_queue_link_t link_element); | |
308 | ||
91447636 A |
309 | extern kern_return_t wait_queue_link( |
310 | wait_queue_t wait_queue, | |
311 | wait_queue_set_t set_queue); | |
9bccf70c | 312 | |
91447636 A |
313 | extern kern_return_t wait_queue_link_noalloc( |
314 | wait_queue_t wait_queue, | |
315 | wait_queue_set_t set_queue, | |
316 | wait_queue_link_t link); | |
1c79356b | 317 | |
91447636 | 318 | extern boolean_t wait_queue_member( |
9bccf70c A |
319 | wait_queue_t wait_queue, |
320 | wait_queue_set_t set_queue); | |
321 | ||
322 | extern kern_return_t wait_queue_unlink( | |
323 | wait_queue_t wait_queue, | |
324 | wait_queue_set_t set_queue); | |
325 | ||
326 | extern kern_return_t wait_queue_unlink_all( | |
1c79356b A |
327 | wait_queue_t wait_queue); |
328 | ||
9bccf70c A |
329 | extern kern_return_t wait_queue_set_unlink_all( |
330 | wait_queue_set_t set_queue); | |
331 | ||
6d2010ae A |
332 | #ifdef XNU_KERNEL_PRIVATE |
333 | extern kern_return_t wait_queue_set_unlink_one( | |
334 | wait_queue_set_t set_queue, | |
335 | wait_queue_link_t link); | |
336 | ||
316670eb A |
337 | extern kern_return_t wait_queue_unlink_nofree( |
338 | wait_queue_t wait_queue, | |
339 | wait_queue_set_t set_queue, | |
340 | wait_queue_link_t *wqlp); | |
341 | ||
342 | extern kern_return_t wait_queue_unlink_all_nofree( | |
343 | wait_queue_t wait_queue, | |
344 | queue_t links); | |
345 | ||
346 | extern kern_return_t wait_queue_set_unlink_all_nofree( | |
347 | wait_queue_set_t set_queue, | |
348 | queue_t links); | |
349 | ||
6d2010ae A |
350 | extern wait_queue_link_t wait_queue_link_allocate(void); |
351 | ||
352 | #endif /* XNU_KERNEL_PRIVATE */ | |
353 | ||
91447636 A |
354 | /* legacy API */ |
355 | kern_return_t wait_queue_sub_init( | |
356 | wait_queue_set_t set_queue, | |
357 | int policy); | |
358 | ||
359 | kern_return_t wait_queue_sub_clearrefs( | |
360 | wait_queue_set_t wq_set); | |
361 | ||
362 | extern kern_return_t wait_subqueue_unlink_all( | |
363 | wait_queue_set_t set_queue); | |
364 | ||
365 | extern wait_queue_t wait_queue_alloc( | |
366 | int policy); | |
367 | ||
368 | extern kern_return_t wait_queue_free( | |
369 | wait_queue_t wait_queue); | |
370 | ||
9bccf70c A |
371 | /* assert intent to wait on <wait_queue,event64> pair */ |
372 | extern wait_result_t wait_queue_assert_wait64( | |
373 | wait_queue_t wait_queue, | |
374 | event64_t wait_event, | |
91447636 A |
375 | wait_interrupt_t interruptible, |
376 | uint64_t deadline); | |
9bccf70c A |
377 | |
378 | /* wakeup the most appropriate thread waiting on <wait_queue,event64> pair */ | |
379 | extern kern_return_t wait_queue_wakeup64_one( | |
380 | wait_queue_t wait_queue, | |
381 | event64_t wake_event, | |
382 | wait_result_t result); | |
383 | ||
384 | /* wakeup all the threads waiting on <wait_queue,event64> pair */ | |
385 | extern kern_return_t wait_queue_wakeup64_all( | |
386 | wait_queue_t wait_queue, | |
387 | event64_t wake_event, | |
388 | wait_result_t result); | |
389 | ||
390 | /* wakeup a specified thread waiting iff waiting on <wait_queue,event64> pair */ | |
391 | extern kern_return_t wait_queue_wakeup64_thread( | |
392 | wait_queue_t wait_queue, | |
393 | event64_t wake_event, | |
394 | thread_t thread, | |
395 | wait_result_t result); | |
396 | ||
9bccf70c A |
397 | /* |
398 | * Compatibility Wait Queue APIs based on pointer events instead of 64bit | |
399 | * integer events. | |
400 | */ | |
1c79356b A |
401 | |
402 | /* assert intent to wait on <wait_queue,event> pair */ | |
9bccf70c | 403 | extern wait_result_t wait_queue_assert_wait( |
1c79356b A |
404 | wait_queue_t wait_queue, |
405 | event_t wait_event, | |
91447636 A |
406 | wait_interrupt_t interruptible, |
407 | uint64_t deadline); | |
1c79356b A |
408 | |
409 | /* wakeup the most appropriate thread waiting on <wait_queue,event> pair */ | |
9bccf70c | 410 | extern kern_return_t wait_queue_wakeup_one( |
1c79356b A |
411 | wait_queue_t wait_queue, |
412 | event_t wake_event, | |
6d2010ae A |
413 | wait_result_t result, |
414 | int priority); | |
1c79356b A |
415 | |
416 | /* wakeup all the threads waiting on <wait_queue,event> pair */ | |
9bccf70c | 417 | extern kern_return_t wait_queue_wakeup_all( |
1c79356b A |
418 | wait_queue_t wait_queue, |
419 | event_t wake_event, | |
9bccf70c | 420 | wait_result_t result); |
1c79356b A |
421 | |
422 | /* wakeup a specified thread waiting iff waiting on <wait_queue,event> pair */ | |
9bccf70c | 423 | extern kern_return_t wait_queue_wakeup_thread( |
1c79356b A |
424 | wait_queue_t wait_queue, |
425 | event_t wake_event, | |
426 | thread_t thread, | |
9bccf70c A |
427 | wait_result_t result); |
428 | ||
91447636 A |
429 | __END_DECLS |
430 | ||
431 | #endif /* _KERN_WAIT_QUEUE_H_ */ | |
1c79356b | 432 | |
91447636 | 433 | #endif /* KERNEL_PRIVATE */ |