source: mainline/kernel/generic/src/synch/waitq.c@ bf2042f9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bf2042f9 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 16.9 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup sync
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Wait queue.
36 *
37 * Wait queue is the basic synchronization primitive upon which all
38 * other synchronization primitives build.
39 *
40 * It allows threads to wait for an event in first-come, first-served
41 * fashion. Conditional operation as well as timeouts and interruptions
42 * are supported.
43 *
44 */
45
46#include <assert.h>
47#include <errno.h>
48#include <synch/waitq.h>
49#include <synch/spinlock.h>
50#include <proc/thread.h>
51#include <proc/scheduler.h>
52#include <arch/asm.h>
53#include <typedefs.h>
54#include <time/timeout.h>
55#include <arch.h>
56#include <context.h>
57#include <adt/list.h>
58#include <arch/cycle.h>
59
60static void waitq_sleep_timed_out(void *);
61static void waitq_complete_wakeup(waitq_t *);
62
63
64/** Initialize wait queue
65 *
66 * Initialize wait queue.
67 *
68 * @param wq Pointer to wait queue to be initialized.
69 *
70 */
71void waitq_initialize(waitq_t *wq)
72{
73 irq_spinlock_initialize(&wq->lock, "wq.lock");
74 list_initialize(&wq->sleepers);
75 wq->missed_wakeups = 0;
76}
77
78/** Handle timeout during waitq_sleep_timeout() call
79 *
80 * This routine is called when waitq_sleep_timeout() times out.
81 * Interrupts are disabled.
82 *
83 * It is supposed to try to remove 'its' thread from the wait queue;
84 * it can eventually fail to achieve this goal when these two events
85 * overlap. In that case it behaves just as though there was no
86 * timeout at all.
87 *
88 * @param data Pointer to the thread that called waitq_sleep_timeout().
89 *
90 */
91void waitq_sleep_timed_out(void *data)
92{
93 thread_t *thread = (thread_t *) data;
94 bool do_wakeup = false;
95 DEADLOCK_PROBE_INIT(p_wqlock);
96
97 irq_spinlock_lock(&threads_lock, false);
98 if (!thread_exists(thread))
99 goto out;
100
101grab_locks:
102 irq_spinlock_lock(&thread->lock, false);
103
104 waitq_t *wq;
105 if ((wq = thread->sleep_queue)) { /* Assignment */
106 if (!irq_spinlock_trylock(&wq->lock)) {
107 irq_spinlock_unlock(&thread->lock, false);
108 DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
109 /* Avoid deadlock */
110 goto grab_locks;
111 }
112
113 list_remove(&thread->wq_link);
114 thread->saved_context = thread->sleep_timeout_context;
115 do_wakeup = true;
116 thread->sleep_queue = NULL;
117 irq_spinlock_unlock(&wq->lock, false);
118 }
119
120 thread->timeout_pending = false;
121 irq_spinlock_unlock(&thread->lock, false);
122
123 if (do_wakeup)
124 thread_ready(thread);
125
126out:
127 irq_spinlock_unlock(&threads_lock, false);
128}
129
130/** Interrupt sleeping thread.
131 *
132 * This routine attempts to interrupt a thread from its sleep in
133 * a waitqueue. If the thread is not found sleeping, no action
134 * is taken.
135 *
136 * The threads_lock must be already held and interrupts must be
137 * disabled upon calling this function.
138 *
139 * @param thread Thread to be interrupted.
140 *
141 */
142void waitq_interrupt_sleep(thread_t *thread)
143{
144 bool do_wakeup = false;
145 DEADLOCK_PROBE_INIT(p_wqlock);
146
147 /*
148 * The thread is quaranteed to exist because
149 * threads_lock is held.
150 */
151
152grab_locks:
153 irq_spinlock_lock(&thread->lock, false);
154
155 waitq_t *wq;
156 if ((wq = thread->sleep_queue)) { /* Assignment */
157 if (!(thread->sleep_interruptible)) {
158 /*
159 * The sleep cannot be interrupted.
160 */
161 irq_spinlock_unlock(&thread->lock, false);
162 return;
163 }
164
165 if (!irq_spinlock_trylock(&wq->lock)) {
166 /* Avoid deadlock */
167 irq_spinlock_unlock(&thread->lock, false);
168 DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
169 goto grab_locks;
170 }
171
172 if ((thread->timeout_pending) &&
173 (timeout_unregister(&thread->sleep_timeout)))
174 thread->timeout_pending = false;
175
176 list_remove(&thread->wq_link);
177 thread->saved_context = thread->sleep_interruption_context;
178 do_wakeup = true;
179 thread->sleep_queue = NULL;
180 irq_spinlock_unlock(&wq->lock, false);
181 }
182
183 irq_spinlock_unlock(&thread->lock, false);
184
185 if (do_wakeup)
186 thread_ready(thread);
187}
188
189/** Interrupt the first thread sleeping in the wait queue.
190 *
191 * Note that the caller somehow needs to know that the thread to be interrupted
192 * is sleeping interruptibly.
193 *
194 * @param wq Pointer to wait queue.
195 *
196 */
197void waitq_unsleep(waitq_t *wq)
198{
199 irq_spinlock_lock(&wq->lock, true);
200
201 if (!list_empty(&wq->sleepers)) {
202 thread_t *thread = list_get_instance(list_first(&wq->sleepers),
203 thread_t, wq_link);
204
205 irq_spinlock_lock(&thread->lock, false);
206
207 assert(thread->sleep_interruptible);
208
209 if ((thread->timeout_pending) &&
210 (timeout_unregister(&thread->sleep_timeout)))
211 thread->timeout_pending = false;
212
213 list_remove(&thread->wq_link);
214 thread->saved_context = thread->sleep_interruption_context;
215 thread->sleep_queue = NULL;
216
217 irq_spinlock_unlock(&thread->lock, false);
218 thread_ready(thread);
219 }
220
221 irq_spinlock_unlock(&wq->lock, true);
222}
223
224#define PARAM_NON_BLOCKING(flags, usec) \
225 (((flags) & SYNCH_FLAGS_NON_BLOCKING) && ((usec) == 0))
226
227/** Sleep until either wakeup, timeout or interruption occurs
228 *
229 * This is a sleep implementation which allows itself to time out or to be
230 * interrupted from the sleep, restoring a failover context.
231 *
232 * Sleepers are organised in a FIFO fashion in a structure called wait queue.
233 *
234 * This function is really basic in that other functions as waitq_sleep()
235 * and all the *_timeout() functions use it.
236 *
237 * @param wq Pointer to wait queue.
238 * @param usec Timeout in microseconds.
239 * @param flags Specify mode of the sleep.
240 *
241 * @param[out] blocked On return, regardless of the return code,
242 * `*blocked` is set to `true` iff the thread went to
243 * sleep.
244 *
245 * The sleep can be interrupted only if the
246 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
247 *
248 * If usec is greater than zero, regardless of the value of the
249 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either
250 * timeout, interruption or wakeup comes.
251 *
252 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags,
253 * the call will not return until wakeup or interruption comes.
254 *
255 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is set in flags, the
256 * call will immediately return, reporting either success or failure.
257 *
258 * @return EAGAIN, meaning that the sleep failed because it was requested
259 * as SYNCH_FLAGS_NON_BLOCKING, but there was no pending wakeup.
260 * @return ETIMEOUT, meaning that the sleep timed out.
261 * @return EINTR, meaning that somebody interrupted the sleeping
262 * thread. Check the value of `*blocked` to see if the thread slept,
263 * or if a pending interrupt forced it to return immediately.
264 * @return EOK, meaning that none of the above conditions occured, and the
265 * thread was woken up successfuly by `waitq_wakeup()`. Check
266 * the value of `*blocked` to see if the thread slept or if
267 * the wakeup was already pending.
268 *
269 */
270errno_t waitq_sleep_timeout(waitq_t *wq, uint32_t usec, unsigned int flags, bool *blocked)
271{
272 assert((!PREEMPTION_DISABLED) || (PARAM_NON_BLOCKING(flags, usec)));
273
274 ipl_t ipl = waitq_sleep_prepare(wq);
275 bool nblocked;
276 errno_t rc = waitq_sleep_timeout_unsafe(wq, usec, flags, &nblocked);
277 waitq_sleep_finish(wq, nblocked, ipl);
278
279 if (blocked != NULL) {
280 *blocked = nblocked;
281 }
282 return rc;
283}
284
285/** Prepare to sleep in a waitq.
286 *
287 * This function will return holding the lock of the wait queue
288 * and interrupts disabled.
289 *
290 * @param wq Wait queue.
291 *
292 * @return Interrupt level as it existed on entry to this function.
293 *
294 */
295ipl_t waitq_sleep_prepare(waitq_t *wq)
296{
297 ipl_t ipl;
298
299restart:
300 ipl = interrupts_disable();
301
302 if (THREAD) { /* Needed during system initiailzation */
303 /*
304 * Busy waiting for a delayed timeout.
305 * This is an important fix for the race condition between
306 * a delayed timeout and a next call to waitq_sleep_timeout().
307 * Simply, the thread is not allowed to go to sleep if
308 * there are timeouts in progress.
309 *
310 */
311 irq_spinlock_lock(&THREAD->lock, false);
312
313 if (THREAD->timeout_pending) {
314 irq_spinlock_unlock(&THREAD->lock, false);
315 interrupts_restore(ipl);
316 goto restart;
317 }
318
319 irq_spinlock_unlock(&THREAD->lock, false);
320 }
321
322 irq_spinlock_lock(&wq->lock, false);
323 return ipl;
324}
325
326/** Finish waiting in a wait queue.
327 *
328 * This function restores interrupts to the state that existed prior
329 * to the call to waitq_sleep_prepare(). If necessary, the wait queue
330 * lock is released.
331 *
332 * @param wq Wait queue.
333 * @param blocked Out parameter of waitq_sleep_timeout_unsafe().
334 * @param ipl Interrupt level returned by waitq_sleep_prepare().
335 *
336 */
337void waitq_sleep_finish(waitq_t *wq, bool blocked, ipl_t ipl)
338{
339 if (blocked) {
340 /*
341 * Wait for a waitq_wakeup() or waitq_unsleep() to complete
342 * before returning from waitq_sleep() to the caller. Otherwise
343 * the caller might expect that the wait queue is no longer used
344 * and deallocate it (although the wakeup on a another cpu has
345 * not yet completed and is using the wait queue).
346 *
347 * Note that we have to do this for EOK and EINTR, but not
348 * necessarily for ETIMEOUT where the timeout handler stops
349 * using the waitq before waking us up. To be on the safe side,
350 * ensure the waitq is not in use anymore in this case as well.
351 */
352 waitq_complete_wakeup(wq);
353 } else {
354 irq_spinlock_unlock(&wq->lock, false);
355 }
356
357 interrupts_restore(ipl);
358}
359
360/** Internal implementation of waitq_sleep_timeout().
361 *
362 * This function implements logic of sleeping in a wait queue.
363 * This call must be preceded by a call to waitq_sleep_prepare()
364 * and followed by a call to waitq_sleep_finish().
365 *
366 * @param wq See waitq_sleep_timeout().
367 * @param usec See waitq_sleep_timeout().
368 * @param flags See waitq_sleep_timeout().
369 *
370 * @param[out] blocked See waitq_sleep_timeout().
371 *
372 * @return See waitq_sleep_timeout().
373 *
374 */
375errno_t waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags, bool *blocked)
376{
377 *blocked = false;
378
379 /* Checks whether to go to sleep at all */
380 if (wq->missed_wakeups) {
381 wq->missed_wakeups--;
382 return EOK;
383 } else {
384 if (PARAM_NON_BLOCKING(flags, usec)) {
385 /* Return immediately instead of going to sleep */
386 return EAGAIN;
387 }
388 }
389
390 /*
391 * Now we are firmly decided to go to sleep.
392 *
393 */
394 irq_spinlock_lock(&THREAD->lock, false);
395
396 if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
397 /*
398 * If the thread was already interrupted,
399 * don't go to sleep at all.
400 */
401 if (THREAD->interrupted) {
402 irq_spinlock_unlock(&THREAD->lock, false);
403 return EINTR;
404 }
405
406 /*
407 * Set context that will be restored if the sleep
408 * of this thread is ever interrupted.
409 */
410 THREAD->sleep_interruptible = true;
411 if (!context_save(&THREAD->sleep_interruption_context)) {
412 /* Short emulation of scheduler() return code. */
413 THREAD->last_cycle = get_cycle();
414 irq_spinlock_unlock(&THREAD->lock, false);
415 return EINTR;
416 }
417 } else
418 THREAD->sleep_interruptible = false;
419
420 if (usec) {
421 /* We use the timeout variant. */
422 if (!context_save(&THREAD->sleep_timeout_context)) {
423 /* Short emulation of scheduler() return code. */
424 THREAD->last_cycle = get_cycle();
425 irq_spinlock_unlock(&THREAD->lock, false);
426 return ETIMEOUT;
427 }
428
429 THREAD->timeout_pending = true;
430 timeout_register(&THREAD->sleep_timeout, (uint64_t) usec,
431 waitq_sleep_timed_out, THREAD);
432 }
433
434 list_append(&THREAD->wq_link, &wq->sleepers);
435
436 /*
437 * Suspend execution.
438 *
439 */
440 THREAD->state = Sleeping;
441 THREAD->sleep_queue = wq;
442
443 /* Must be before entry to scheduler, because there are multiple
444 * return vectors.
445 */
446 *blocked = true;
447
448 irq_spinlock_unlock(&THREAD->lock, false);
449
450 /* wq->lock is released in scheduler_separated_stack() */
451 scheduler();
452
453 return EOK;
454}
455
456/** Wake up first thread sleeping in a wait queue
457 *
458 * Wake up first thread sleeping in a wait queue. This is the SMP- and IRQ-safe
459 * wrapper meant for general use.
460 *
461 * Besides its 'normal' wakeup operation, it attempts to unregister possible
462 * timeout.
463 *
464 * @param wq Pointer to wait queue.
465 * @param mode Wakeup mode.
466 *
467 */
468void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
469{
470 irq_spinlock_lock(&wq->lock, true);
471 _waitq_wakeup_unsafe(wq, mode);
472 irq_spinlock_unlock(&wq->lock, true);
473}
474
475/** If there is a wakeup in progress actively waits for it to complete.
476 *
477 * The function returns once the concurrently running waitq_wakeup()
478 * exits. It returns immediately if there are no concurrent wakeups
479 * at the time.
480 *
481 * Interrupts must be disabled.
482 *
483 * Example usage:
484 * @code
485 * void callback(waitq *wq)
486 * {
487 * // Do something and notify wait_for_completion() that we're done.
488 * waitq_wakeup(wq);
489 * }
490 * void wait_for_completion(void)
491 * {
492 * waitq wg;
493 * waitq_initialize(&wq);
494 * // Run callback() in the background, pass it wq.
495 * do_asynchronously(callback, &wq);
496 * // Wait for callback() to complete its work.
497 * waitq_sleep(&wq);
498 * // callback() completed its work, but it may still be accessing
499 * // wq in waitq_wakeup(). Therefore it is not yet safe to return
500 * // from waitq_sleep() or it would clobber up our stack (where wq
501 * // is stored). waitq_sleep() ensures the wait queue is no longer
502 * // in use by invoking waitq_complete_wakeup() internally.
503 *
504 * // waitq_sleep() returned, it is safe to free wq.
505 * }
506 * @endcode
507 *
508 * @param wq Pointer to a wait queue.
509 */
510static void waitq_complete_wakeup(waitq_t *wq)
511{
512 assert(interrupts_disabled());
513
514 irq_spinlock_lock(&wq->lock, false);
515 irq_spinlock_unlock(&wq->lock, false);
516}
517
518
519/** Internal SMP- and IRQ-unsafe version of waitq_wakeup()
520 *
521 * This is the internal SMP- and IRQ-unsafe version of waitq_wakeup(). It
522 * assumes wq->lock is already locked and interrupts are already disabled.
523 *
524 * @param wq Pointer to wait queue.
525 * @param mode If mode is WAKEUP_FIRST, then the longest waiting
526 * thread, if any, is woken up. If mode is WAKEUP_ALL, then
527 * all waiting threads, if any, are woken up. If there are
528 * no waiting threads to be woken up, the missed wakeup is
529 * recorded in the wait queue.
530 *
531 */
532void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
533{
534 size_t count = 0;
535
536 assert(interrupts_disabled());
537 assert(irq_spinlock_locked(&wq->lock));
538
539loop:
540 if (list_empty(&wq->sleepers)) {
541 wq->missed_wakeups++;
542 if ((count) && (mode == WAKEUP_ALL))
543 wq->missed_wakeups--;
544
545 return;
546 }
547
548 count++;
549 thread_t *thread = list_get_instance(list_first(&wq->sleepers),
550 thread_t, wq_link);
551
552 /*
553 * Lock the thread prior to removing it from the wq.
554 * This is not necessary because of mutual exclusion
555 * (the link belongs to the wait queue), but because
556 * of synchronization with waitq_sleep_timed_out()
557 * and thread_interrupt_sleep().
558 *
559 * In order for these two functions to work, the following
560 * invariant must hold:
561 *
562 * thread->sleep_queue != NULL <=> thread sleeps in a wait queue
563 *
564 * For an observer who locks the thread, the invariant
565 * holds only when the lock is held prior to removing
566 * it from the wait queue.
567 *
568 */
569 irq_spinlock_lock(&thread->lock, false);
570 list_remove(&thread->wq_link);
571
572 if ((thread->timeout_pending) &&
573 (timeout_unregister(&thread->sleep_timeout)))
574 thread->timeout_pending = false;
575
576 thread->sleep_queue = NULL;
577 irq_spinlock_unlock(&thread->lock, false);
578
579 thread_ready(thread);
580
581 if (mode == WAKEUP_ALL)
582 goto loop;
583}
584
585/** Get the missed wakeups count.
586 *
587 * @param wq Pointer to wait queue.
588 * @return The wait queue's missed_wakeups count.
589 */
590int waitq_count_get(waitq_t *wq)
591{
592 int cnt;
593
594 irq_spinlock_lock(&wq->lock, true);
595 cnt = wq->missed_wakeups;
596 irq_spinlock_unlock(&wq->lock, true);
597
598 return cnt;
599}
600
601/** Set the missed wakeups count.
602 *
603 * @param wq Pointer to wait queue.
604 * @param val New value of the missed_wakeups count.
605 */
606void waitq_count_set(waitq_t *wq, int val)
607{
608 irq_spinlock_lock(&wq->lock, true);
609 wq->missed_wakeups = val;
610 irq_spinlock_unlock(&wq->lock, true);
611}
612
613/** @}
614 */
Note: See TracBrowser for help on using the repository browser.