blob: 58dff8df5f6c3dc1d8fbd57188b0100d9e2faa63 [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/*
Travis Geiselbrecht9045c062009-06-28 11:20:09 -07002 * Copyright (c) 2008-2009 Travis Geiselbrecht
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07003 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
Travis Geiselbrecht12403e32010-05-06 13:36:57 -070023
24/**
25 * @file
26 * @brief Kernel threading
27 *
28 * This file is the core kernel threading interface.
29 *
30 * @defgroup thread Threads
31 * @{
32 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070033#include <debug.h>
34#include <list.h>
35#include <malloc.h>
36#include <string.h>
37#include <err.h>
38#include <kernel/thread.h>
39#include <kernel/timer.h>
40#include <kernel/dpc.h>
41#include <platform.h>
42
43#if DEBUGLEVEL > 1
44#define THREAD_CHECKS 1
45#endif
46
47#if THREAD_STATS
48struct thread_stats thread_stats;
49#endif
50
51/* global thread list */
52static struct list_node thread_list;
53
54/* the current thread */
55thread_t *current_thread;
56
57/* the global critical section count */
58int critical_section_count = 1;
59
60/* the run queue */
61static struct list_node run_queue[NUM_PRIORITIES];
62static uint32_t run_queue_bitmap;
63
64/* the bootstrap thread (statically allocated) */
65static thread_t bootstrap_thread;
66
67/* the idle thread */
68thread_t *idle_thread;
69
70/* local routines */
71static void thread_resched(void);
72static void idle_thread_routine(void) __NO_RETURN;
73
Travis Geiselbrecht9045c062009-06-28 11:20:09 -070074#if PLATFORM_HAS_DYNAMIC_TIMER
75/* preemption timer */
76static timer_t preempt_timer;
77#endif
78
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070079/* run queue manipulation */
80static void insert_in_run_queue_head(thread_t *t)
81{
82#if THREAD_CHECKS
83 ASSERT(t->magic == THREAD_MAGIC);
84 ASSERT(t->state == THREAD_READY);
85 ASSERT(!list_in_list(&t->queue_node));
86 ASSERT(in_critical_section());
87#endif
88
89 list_add_head(&run_queue[t->priority], &t->queue_node);
90 run_queue_bitmap |= (1<<t->priority);
91}
92
93static void insert_in_run_queue_tail(thread_t *t)
94{
95#if THREAD_CHECKS
96 ASSERT(t->magic == THREAD_MAGIC);
97 ASSERT(t->state == THREAD_READY);
98 ASSERT(!list_in_list(&t->queue_node));
99 ASSERT(in_critical_section());
100#endif
101
102 list_add_tail(&run_queue[t->priority], &t->queue_node);
103 run_queue_bitmap |= (1<<t->priority);
104}
105
106static void init_thread_struct(thread_t *t, const char *name)
107{
108 memset(t, 0, sizeof(thread_t));
109 t->magic = THREAD_MAGIC;
110 strlcpy(t->name, name, sizeof(t->name));
111}
112
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700113/**
114 * @brief Create a new thread
115 *
116 * This function creates a new thread. The thread is initially suspended, so you
117 * need to call thread_resume() to execute it.
118 *
119 * @param name Name of thread
120 * @param entry Entry point of thread
121 * @param arg Arbitrary argument passed to entry()
122 * @param priority Execution priority for the thread.
123 * @param stack_size Stack size for the thread.
124 *
125 * Thread priority is an integer from 0 (lowest) to 31 (highest). Some standard
126 * prioritys are defined in <kernel/thread.h>:
127 *
128 * HIGHEST_PRIORITY
129 * DPC_PRIORITY
130 * HIGH_PRIORITY
131 * DEFAULT_PRIORITY
132 * LOW_PRIORITY
133 * IDLE_PRIORITY
134 * LOWEST_PRIORITY
135 *
136 * Stack size is typically set to DEFAULT_STACK_SIZE
137 *
138 * @return Pointer to thread object, or NULL on failure.
139 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700140thread_t *thread_create(const char *name, thread_start_routine entry, void *arg, int priority, size_t stack_size)
141{
142 thread_t *t;
143
144 t = malloc(sizeof(thread_t));
145 if (!t)
146 return NULL;
147
148 init_thread_struct(t, name);
149
150 t->entry = entry;
151 t->arg = arg;
152 t->priority = priority;
153 t->saved_critical_section_count = 1; /* we always start inside a critical section */
154 t->state = THREAD_SUSPENDED;
155 t->blocking_wait_queue = NULL;
156 t->wait_queue_block_ret = NO_ERROR;
157
158 /* create the stack */
159 t->stack = malloc(stack_size);
160 if (!t->stack) {
161 free(t);
162 return NULL;
163 }
164
165 t->stack_size = stack_size;
166
travis geiselbrechtc60a2e62008-12-22 23:46:26 +0000167 /* inheirit thread local storage from the parent */
168 int i;
169 for (i=0; i < MAX_TLS_ENTRY; i++)
170 t->tls[i] = current_thread->tls[i];
171
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700172 /* set up the initial stack frame */
173 arch_thread_initialize(t);
174
175 /* add it to the global thread list */
176 enter_critical_section();
177 list_add_head(&thread_list, &t->thread_list_node);
178 exit_critical_section();
179
180 return t;
181}
182
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700183/**
184 * @brief Make a suspended thread executable.
185 *
186 * This function is typically called to start a thread which has just been
187 * created with thread_create()
188 *
189 * @param t Thread to resume
190 *
191 * @return NO_ERROR on success, ERR_NOT_SUSPENDED if thread was not suspended.
192 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700193status_t thread_resume(thread_t *t)
194{
195#if THREAD_CHECKS
196 ASSERT(t->magic == THREAD_MAGIC);
197 ASSERT(t->state != THREAD_DEATH);
198#endif
199
200 if (t->state == THREAD_READY || t->state == THREAD_RUNNING)
201 return ERR_NOT_SUSPENDED;
202
203 enter_critical_section();
204 t->state = THREAD_READY;
205 insert_in_run_queue_head(t);
206 thread_yield();
207 exit_critical_section();
208
209 return NO_ERROR;
210}
211
212static void thread_cleanup_dpc(void *thread)
213{
214 thread_t *t = (thread_t *)thread;
215
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700216// dprintf(SPEW, "thread_cleanup_dpc: thread %p (%s)\n", t, t->name);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700217
218#if THREAD_CHECKS
219 ASSERT(t->state == THREAD_DEATH);
220 ASSERT(t->blocking_wait_queue == NULL);
221 ASSERT(!list_in_list(&t->queue_node));
222#endif
223
224 /* remove it from the master thread list */
225 enter_critical_section();
226 list_delete(&t->thread_list_node);
227 exit_critical_section();
228
229 /* free its stack and the thread structure itself */
230 if (t->stack)
231 free(t->stack);
232
233 free(t);
234}
235
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700236/**
237 * @brief Terminate the current thread
238 *
239 * Current thread exits with the specified return code.
240 *
241 * This function does not return.
242 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700243void thread_exit(int retcode)
244{
245#if THREAD_CHECKS
246 ASSERT(current_thread->magic == THREAD_MAGIC);
247 ASSERT(current_thread->state == THREAD_RUNNING);
248#endif
249
250// dprintf("thread_exit: current %p\n", current_thread);
251
252 enter_critical_section();
253
254 /* enter the dead state */
255 current_thread->state = THREAD_DEATH;
256 current_thread->retcode = retcode;
257
258 /* schedule a dpc to clean ourselves up */
259 dpc_queue(thread_cleanup_dpc, (void *)current_thread, DPC_FLAG_NORESCHED);
260
261 /* reschedule */
262 thread_resched();
263
264 panic("somehow fell through thread_exit()\n");
265}
266
267static void idle_thread_routine(void)
268{
269 for(;;)
270 arch_idle();
271}
272
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700273/**
274 * @brief Cause another thread to be executed.
275 *
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700276 * Internal reschedule routine. The current thread needs to already be in whatever
277 * state and queues it needs to be in. This routine simply picks the next thread and
278 * switches to it.
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700279 *
280 * This is probably not the function you're looking for. See
281 * thread_yield() instead.
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700282 */
283void thread_resched(void)
284{
285 thread_t *oldthread;
286 thread_t *newthread;
287
288// dprintf("thread_resched: current %p: ", current_thread);
289// dump_thread(current_thread);
290
291#if THREAD_CHECKS
292 ASSERT(in_critical_section());
293#endif
294
295#if THREAD_STATS
296 thread_stats.reschedules++;
297#endif
298
299 oldthread = current_thread;
300
301 // at the moment, can't deal with more than 32 priority levels
302 ASSERT(NUM_PRIORITIES <= 32);
303
304 // should at least find the idle thread
305#if THREAD_CHECKS
306 ASSERT(run_queue_bitmap != 0);
307#endif
308
309 int next_queue = HIGHEST_PRIORITY - __builtin_clz(run_queue_bitmap) - (32 - NUM_PRIORITIES);
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700310 //dprintf(SPEW, "bitmap 0x%x, next %d\n", run_queue_bitmap, next_queue);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700311
312 newthread = list_remove_head_type(&run_queue[next_queue], thread_t, queue_node);
313
314#if THREAD_CHECKS
315 ASSERT(newthread);
316#endif
317
318 if (list_is_empty(&run_queue[next_queue]))
319 run_queue_bitmap &= ~(1<<next_queue);
320
321#if 0
322 // XXX make this more efficient
323 newthread = NULL;
324 for (i=HIGHEST_PRIORITY; i >= LOWEST_PRIORITY; i--) {
325 newthread = list_remove_head_type(&run_queue[i], thread_t, queue_node);
326 if (newthread)
327 break;
328 }
329#endif
330
331// dprintf("newthread: ");
332// dump_thread(newthread);
333
334 newthread->state = THREAD_RUNNING;
335
336 if (newthread == oldthread)
337 return;
338
339 /* set up quantum for the new thread if it was consumed */
340 if (newthread->remaining_quantum <= 0) {
341 newthread->remaining_quantum = 5; // XXX make this smarter
342 }
343
344#if THREAD_STATS
345 thread_stats.context_switches++;
346
347 if (oldthread == idle_thread) {
348 bigtime_t now = current_time_hires();
349 thread_stats.idle_time += now - thread_stats.last_idle_timestamp;
350 }
351 if (newthread == idle_thread) {
352 thread_stats.last_idle_timestamp = current_time_hires();
353 }
354#endif
355
356#if THREAD_CHECKS
357 ASSERT(critical_section_count > 0);
358 ASSERT(newthread->saved_critical_section_count > 0);
359#endif
360
Travis Geiselbrecht9045c062009-06-28 11:20:09 -0700361#if PLATFORM_HAS_DYNAMIC_TIMER
362 /* if we're switching from idle to a real thread, set up a periodic
363 * timer to run our preemption tick.
364 */
365 if (oldthread == idle_thread) {
366 timer_set_periodic(&preempt_timer, 10, (timer_callback)thread_timer_tick, NULL);
367 } else if (newthread == idle_thread) {
368 timer_cancel(&preempt_timer);
369 }
370#endif
371
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700372 /* do the switch */
373 oldthread->saved_critical_section_count = critical_section_count;
374 current_thread = newthread;
375 critical_section_count = newthread->saved_critical_section_count;
376 arch_context_switch(oldthread, newthread);
377}
378
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700379/**
380 * @brief Yield the cpu to another thread
381 *
382 * This function places the current thread at the end of the run queue
383 * and yields the cpu to another waiting thread (if any.)
384 *
385 * This function will return at some later time. Possibly immediately if
386 * no other threads are waiting to execute.
387 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700388void thread_yield(void)
389{
390#if THREAD_CHECKS
391 ASSERT(current_thread->magic == THREAD_MAGIC);
392 ASSERT(current_thread->state == THREAD_RUNNING);
393#endif
394
395 enter_critical_section();
396
397#if THREAD_STATS
398 thread_stats.yields++;
399#endif
400
401 /* we are yielding the cpu, so stick ourselves into the tail of the run queue and reschedule */
402 current_thread->state = THREAD_READY;
403 current_thread->remaining_quantum = 0;
404 insert_in_run_queue_tail(current_thread);
405 thread_resched();
406
407 exit_critical_section();
408}
409
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700410/**
411 * @brief Briefly yield cpu to another thread
412 *
413 * This function is similar to thread_yield(), except that it will
414 * restart more quickly.
415 *
416 * This function places the current thread at the head of the run
417 * queue and then yields the cpu to another thread.
418 *
419 * Exception: If the time slice for this thread has expired, then
420 * the thread goes to the end of the run queue.
421 *
422 * This function will return at some later time. Possibly immediately if
423 * no other threads are waiting to execute.
424 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700425void thread_preempt(void)
426{
427#if THREAD_CHECKS
428 ASSERT(current_thread->magic == THREAD_MAGIC);
429 ASSERT(current_thread->state == THREAD_RUNNING);
430#endif
431
432 enter_critical_section();
433
434#if THREAD_STATS
435 if (current_thread != idle_thread)
436 thread_stats.preempts++; /* only track when a meaningful preempt happens */
437#endif
438
439 /* we are being preempted, so we get to go back into the front of the run queue if we have quantum left */
440 current_thread->state = THREAD_READY;
441 if (current_thread->remaining_quantum > 0)
442 insert_in_run_queue_head(current_thread);
443 else
444 insert_in_run_queue_tail(current_thread); /* if we're out of quantum, go to the tail of the queue */
445 thread_resched();
446
447 exit_critical_section();
448}
449
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700450/**
451 * @brief Suspend thread until woken.
452 *
453 * This function schedules another thread to execute. This function does not
454 * return until the thread is made runable again by some other module.
455 *
456 * You probably don't want to call this function directly; it's meant to be called
457 * from other modules, such as mutex, which will presumably set the thread's
458 * state to blocked and add it to some queue or another.
459 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700460void thread_block(void)
461{
462#if THREAD_CHECKS
463 ASSERT(current_thread->magic == THREAD_MAGIC);
464 ASSERT(current_thread->state == THREAD_BLOCKED);
465#endif
466
467 enter_critical_section();
468
469 /* we are blocking on something. the blocking code should have already stuck us on a queue */
470 thread_resched();
471
472 exit_critical_section();
473}
474
475enum handler_return thread_timer_tick(void)
476{
477 if (current_thread == idle_thread)
478 return INT_NO_RESCHEDULE;
479
480 current_thread->remaining_quantum--;
481 if (current_thread->remaining_quantum <= 0)
482 return INT_RESCHEDULE;
483 else
484 return INT_NO_RESCHEDULE;
485}
486
487/* timer callback to wake up a sleeping thread */
488static enum handler_return thread_sleep_handler(timer_t *timer, time_t now, void *arg)
489{
490 thread_t *t = (thread_t *)arg;
491
492#if THREAD_CHECKS
493 ASSERT(t->magic == THREAD_MAGIC);
494 ASSERT(t->state == THREAD_SLEEPING);
495#endif
496
497 t->state = THREAD_READY;
498 insert_in_run_queue_head(t);
499
500 return INT_RESCHEDULE;
501}
502
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700503/**
504 * @brief Put thread to sleep; delay specified in ms
505 *
506 * This function puts the current thread to sleep until the specified
507 * delay in ms has expired.
508 *
509 * Note that this function could sleep for longer than the specified delay if
510 * other threads are running. When the timer expires, this thread will
511 * be placed at the head of the run queue.
512 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700513void thread_sleep(time_t delay)
514{
515 timer_t timer;
516
517#if THREAD_CHECKS
518 ASSERT(current_thread->magic == THREAD_MAGIC);
519 ASSERT(current_thread->state == THREAD_RUNNING);
520#endif
521
522 timer_initialize(&timer);
523
524 enter_critical_section();
525 timer_set_oneshot(&timer, delay, thread_sleep_handler, (void *)current_thread);
526 current_thread->state = THREAD_SLEEPING;
527 thread_resched();
528 exit_critical_section();
529}
530
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700531/**
532 * @brief Initialize threading system
533 *
534 * This function is called once, from kmain()
535 */
travis geiselbrecht858b1ad2008-12-23 21:55:41 +0000536void thread_init_early(void)
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700537{
538 int i;
539
540 /* initialize the run queues */
541 for (i=0; i < NUM_PRIORITIES; i++)
542 list_initialize(&run_queue[i]);
543
544 /* initialize the thread list */
545 list_initialize(&thread_list);
546
547 /* create a thread to cover the current running state */
548 thread_t *t = &bootstrap_thread;
549 init_thread_struct(t, "bootstrap");
550
551 /* half construct this thread, since we're already running */
552 t->priority = HIGHEST_PRIORITY;
553 t->state = THREAD_RUNNING;
554 t->saved_critical_section_count = 1;
555 list_add_head(&thread_list, &t->thread_list_node);
556 current_thread = t;
557}
558
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700559/**
560 * @brief Complete thread initialization
561 *
562 * This function is called once at boot time
563 */
travis geiselbrecht858b1ad2008-12-23 21:55:41 +0000564void thread_init(void)
565{
Travis Geiselbrecht9045c062009-06-28 11:20:09 -0700566#if PLATFORM_HAS_DYNAMIC_TIMER
567 timer_initialize(&preempt_timer);
568#endif
travis geiselbrecht858b1ad2008-12-23 21:55:41 +0000569}
570
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700571/**
572 * @brief Change name of current thread
573 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700574void thread_set_name(const char *name)
575{
576 strlcpy(current_thread->name, name, sizeof(current_thread->name));
577}
578
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700579/**
580 * @brief Change priority of current thread
581 *
582 * See thread_create() for a discussion of priority values.
583 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700584void thread_set_priority(int priority)
585{
586 if (priority < LOWEST_PRIORITY)
587 priority = LOWEST_PRIORITY;
588 if (priority > HIGHEST_PRIORITY)
589 priority = HIGHEST_PRIORITY;
590 current_thread->priority = priority;
591}
592
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700593/**
594 * @brief Become an idle thread
595 *
596 * This function marks the current thread as the idle thread -- the one which
597 * executes when there is nothing else to do. This function does not return.
598 * This function is called once at boot time.
599 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700600void thread_become_idle(void)
601{
602 thread_set_name("idle");
603 thread_set_priority(IDLE_PRIORITY);
604 idle_thread = current_thread;
605 idle_thread_routine();
606}
607
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700608/**
609 * @brief Dump debugging info about the specified thread.
610 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700611void dump_thread(thread_t *t)
612{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700613 dprintf(INFO, "dump_thread: t %p (%s)\n", t, t->name);
614 dprintf(INFO, "\tstate %d, priority %d, remaining quantum %d, critical section %d\n", t->state, t->priority, t->remaining_quantum, t->saved_critical_section_count);
615 dprintf(INFO, "\tstack %p, stack_size %zd\n", t->stack, t->stack_size);
616 dprintf(INFO, "\tentry %p, arg %p\n", t->entry, t->arg);
617 dprintf(INFO, "\twait queue %p, wait queue ret %d\n", t->blocking_wait_queue, t->wait_queue_block_ret);
travis geiselbrechtc60a2e62008-12-22 23:46:26 +0000618 dprintf(INFO, "\ttls:");
619 int i;
620 for (i=0; i < MAX_TLS_ENTRY; i++) {
621 dprintf(INFO, " 0x%x", t->tls[i]);
622 }
623 dprintf(INFO, "\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700624}
625
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700626/**
627 * @brief Dump debugging info about all threads
628 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700629void dump_all_threads(void)
630{
631 thread_t *t;
632
633 enter_critical_section();
634 list_for_every_entry(&thread_list, t, thread_t, thread_list_node) {
635 dump_thread(t);
636 }
637 exit_critical_section();
638}
639
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700640/** @} */
641
642
643/**
644 * @defgroup wait Wait Queue
645 * @{
646 */
647
648/**
649 * @brief Initialize a wait queue
650 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700651void wait_queue_init(wait_queue_t *wait)
652{
653 wait->magic = WAIT_QUEUE_MAGIC;
654 list_initialize(&wait->list);
655 wait->count = 0;
656}
657
658static enum handler_return wait_queue_timeout_handler(timer_t *timer, time_t now, void *arg)
659{
660 thread_t *thread = (thread_t *)arg;
661
662#if THREAD_CHECKS
663 ASSERT(thread->magic == THREAD_MAGIC);
664#endif
665
666 if (thread_unblock_from_wait_queue(thread, false, ERR_TIMED_OUT) >= NO_ERROR)
667 return INT_RESCHEDULE;
668
669 return INT_NO_RESCHEDULE;
670}
671
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700672/**
673 * @brief Block until a wait queue is notified.
674 *
675 * This function puts the current thread at the end of a wait
676 * queue and then blocks until some other thread wakes the queue
677 * up again.
678 *
679 * @param wait The wait queue to enter
680 * @param timeout The maximum time, in ms, to wait
681 *
682 * If the timeout is zero, this function returns immediately with
683 * ERR_TIMED_OUT. If the timeout is INFINITE_TIME, this function
684 * waits indefinitely. Otherwise, this function returns with
685 * ERR_TIMED_OUT at the end of the timeout period.
686 *
687 * @return ERR_TIMED_OUT on timeout, else returns the return
688 * value specified when the queue was woken by wait_queue_wake_one().
689 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700690status_t wait_queue_block(wait_queue_t *wait, time_t timeout)
691{
692 timer_t timer;
693
694#if THREAD_CHECKS
695 ASSERT(wait->magic == WAIT_QUEUE_MAGIC);
696 ASSERT(current_thread->state == THREAD_RUNNING);
697 ASSERT(in_critical_section());
698#endif
699
700 if (timeout == 0)
701 return ERR_TIMED_OUT;
702
703 list_add_tail(&wait->list, &current_thread->queue_node);
704 wait->count++;
705 current_thread->state = THREAD_BLOCKED;
706 current_thread->blocking_wait_queue = wait;
707 current_thread->wait_queue_block_ret = NO_ERROR;
708
709 /* if the timeout is nonzero or noninfinite, set a callback to yank us out of the queue */
710 if (timeout != INFINITE_TIME) {
711 timer_initialize(&timer);
712 timer_set_oneshot(&timer, timeout, wait_queue_timeout_handler, (void *)current_thread);
713 }
714
715 thread_block();
716
717 /* we don't really know if the timer fired or not, so it's better safe to try to cancel it */
718 if (timeout != INFINITE_TIME) {
719 timer_cancel(&timer);
720 }
721
722 return current_thread->wait_queue_block_ret;
723}
724
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700725/**
726 * @brief Wake up one thread sleeping on a wait queue
727 *
728 * This function removes one thread (if any) from the head of the wait queue and
729 * makes it executable. The new thread will be placed at the head of the
730 * run queue.
731 *
732 * @param wait The wait queue to wake
733 * @param reschedule If true, the newly-woken thread will run immediately.
734 * @param wait_queue_error The return value which the new thread will receive
735 * from wait_queue_block().
736 *
737 * @return The number of threads woken (zero or one)
738 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700739int wait_queue_wake_one(wait_queue_t *wait, bool reschedule, status_t wait_queue_error)
740{
741 thread_t *t;
742 int ret = 0;
743
744#if THREAD_CHECKS
745 ASSERT(wait->magic == WAIT_QUEUE_MAGIC);
746 ASSERT(in_critical_section());
747#endif
748
749 t = list_remove_head_type(&wait->list, thread_t, queue_node);
750 if (t) {
751 wait->count--;
752#if THREAD_CHECKS
753 ASSERT(t->state == THREAD_BLOCKED);
754#endif
755 t->state = THREAD_READY;
756 t->wait_queue_block_ret = wait_queue_error;
757 t->blocking_wait_queue = NULL;
758
759 /* if we're instructed to reschedule, stick the current thread on the head
760 * of the run queue first, so that the newly awakened thread gets a chance to run
761 * before the current one, but the current one doesn't get unnecessarilly punished.
762 */
763 if (reschedule) {
764 current_thread->state = THREAD_READY;
765 insert_in_run_queue_head(current_thread);
766 }
767 insert_in_run_queue_head(t);
768 if (reschedule)
769 thread_resched();
770 ret = 1;
771 }
772
773 return ret;
774}
775
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700776
777/**
778 * @brief Wake all threads sleeping on a wait queue
779 *
780 * This function removes all threads (if any) from the wait queue and
781 * makes them executable. The new threads will be placed at the head of the
782 * run queue.
783 *
784 * @param wait The wait queue to wake
785 * @param reschedule If true, the newly-woken threads will run immediately.
786 * @param wait_queue_error The return value which the new thread will receive
787 * from wait_queue_block().
788 *
789 * @return The number of threads woken (zero or one)
790 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700791int wait_queue_wake_all(wait_queue_t *wait, bool reschedule, status_t wait_queue_error)
792{
793 thread_t *t;
794 int ret = 0;
795
796#if THREAD_CHECKS
797 ASSERT(wait->magic == WAIT_QUEUE_MAGIC);
798 ASSERT(in_critical_section());
799#endif
800
801 if (reschedule && wait->count > 0) {
802 /* if we're instructed to reschedule, stick the current thread on the head
803 * of the run queue first, so that the newly awakened threads get a chance to run
804 * before the current one, but the current one doesn't get unnecessarilly punished.
805 */
806 current_thread->state = THREAD_READY;
807 insert_in_run_queue_head(current_thread);
808 }
809
810 /* pop all the threads off the wait queue into the run queue */
811 while ((t = list_remove_head_type(&wait->list, thread_t, queue_node))) {
812 wait->count--;
813#if THREAD_CHECKS
814 ASSERT(t->state == THREAD_BLOCKED);
815#endif
816 t->state = THREAD_READY;
817 t->wait_queue_block_ret = wait_queue_error;
818 t->blocking_wait_queue = NULL;
819
820 insert_in_run_queue_head(t);
821 ret++;
822 }
823
824#if THREAD_CHECKS
825 ASSERT(wait->count == 0);
826#endif
827
828 if (reschedule && ret > 0)
829 thread_resched();
830
831 return ret;
832}
833
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700834/**
835 * @brief Free all resources allocated in wait_queue_init()
836 *
837 * If any threads were waiting on this queue, they are all woken.
838 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700839void wait_queue_destroy(wait_queue_t *wait, bool reschedule)
840{
841#if THREAD_CHECKS
842 ASSERT(wait->magic == WAIT_QUEUE_MAGIC);
843 ASSERT(in_critical_section());
844#endif
845 wait_queue_wake_all(wait, reschedule, ERR_OBJECT_DESTROYED);
846 wait->magic = 0;
847}
848
Travis Geiselbrecht12403e32010-05-06 13:36:57 -0700849/**
850 * @brief Wake a specific thread in a wait queue
851 *
852 * This function extracts a specific thread from a wait queue, wakes it, and
853 * puts it at the head of the run queue.
854 *
855 * @param t The thread to wake
856 * @param reschedule If true, the newly-woken threads will run immediately.
857 * @param wait_queue_error The return value which the new thread will receive
858 * from wait_queue_block().
859 *
860 * @return ERR_NOT_BLOCKED if thread was not in any wait queue.
861 */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700862status_t thread_unblock_from_wait_queue(thread_t *t, bool reschedule, status_t wait_queue_error)
863{
864 enter_critical_section();
865
866#if THREAD_CHECKS
867 ASSERT(t->magic == THREAD_MAGIC);
868#endif
869
870 if (t->state != THREAD_BLOCKED)
871 return ERR_NOT_BLOCKED;
872
873#if THREAD_CHECKS
874 ASSERT(t->blocking_wait_queue != NULL);
875 ASSERT(t->blocking_wait_queue->magic == WAIT_QUEUE_MAGIC);
876 ASSERT(list_in_list(&t->queue_node));
877#endif
878
879 list_delete(&t->queue_node);
880 t->blocking_wait_queue->count--;
881 t->blocking_wait_queue = NULL;
882 t->state = THREAD_READY;
883 t->wait_queue_block_ret = wait_queue_error;
884 insert_in_run_queue_head(t);
885
886 if (reschedule)
887 thread_resched();
888
889 exit_critical_section();
890
891 return NO_ERROR;
892}
893
894