52e6a1ce1217fb557114fd295950fd5fe61cc588
[akaros.git] / kern / src / rendez.c
1 /* Copyright (c) 2013 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Plan9 style Rendezvous (http://plan9.bell-labs.com/sys/doc/sleep.html)
6  *
7  * We implement it with CVs, and it can handle multiple sleepers/wakers. */
8
9 #include <rendez.h>
10 #include <kthread.h>
11 #include <alarm.h>
12 #include <assert.h>
13 #include <smp.h>
14
15 void rendez_init(struct rendez *rv)
16 {
17         cv_init_irqsave(&rv->cv);
18 }
19
20 void rendez_sleep(struct rendez *rv, int (*cond)(void*), void *arg)
21 {
22         int8_t irq_state = 0;
23         cv_lock_irqsave(&rv->cv, &irq_state);
24         /* Mesa-style semantics, which is definitely what you want.  See the
25          * discussion at the end of the URL above. */
26         while (!cond(arg)) {
27                 cv_wait(&rv->cv);
28                 cpu_relax();
29         }
30         cv_unlock_irqsave(&rv->cv, &irq_state);
31 }
32
33 /* Force a wakeup of all waiters on the rv, including non-timeout users.  For
34  * those, they will just wake up, see the condition is still false (probably)
35  * and go back to sleep. */
36 static void rendez_alarm_handler(struct alarm_waiter *awaiter)
37 {
38         struct rendez *rv = (struct rendez*)awaiter->data;
39         rendez_wakeup(rv);
40 }
41
42 /* Like sleep, but it will timeout in 'msec' milliseconds. */
43 void rendez_sleep_timeout(struct rendez *rv, int (*cond)(void*), void *arg,
44                           unsigned int msec)
45 {
46         int8_t irq_state = 0;
47         struct alarm_waiter awaiter;
48         struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
49
50         assert((int)msec > 0);
51         /* The handler will call rendez_wake, but won't mess with the condition
52          * state.  It's enough to break us out of cv_wait() to see .has_fired. */
53         init_awaiter(&awaiter, rendez_alarm_handler);
54         awaiter.data = rv;
55         set_awaiter_rel(&awaiter, msec);
56         /* Set our alarm on this cpu's tchain.  Note that when we sleep in cv_wait,
57          * we could be migrated, and later on we could be unsetting the alarm
58          * remotely. */
59         set_alarm(pcpui_tchain, &awaiter);
60         cv_lock_irqsave(&rv->cv, &irq_state);
61         /* We could wake early for a few reasons.  Legit wakeups after a changed
62          * condition (and we should exit), other alarms with different timeouts (and
63          * we should go back to sleep), etc.  Note it is possible for our alarm to
64          * fire immediately upon setting it: before we even cv_lock. */
65         while (!cond(arg) && !awaiter.has_fired) {
66                 cv_wait(&rv->cv);
67                 cpu_relax();
68         }
69         cv_unlock_irqsave(&rv->cv, &irq_state);
70         /* Turn off our alarm.  If it already fired, this is a no-op.  Note this
71          * could be cross-core. */
72         unset_alarm(pcpui_tchain, &awaiter);
73 }
74
75 void rendez_wakeup(struct rendez *rv)
76 {
77         int8_t irq_state = 0;
78         /* The plan9 style "one sleeper, one waker" could get by with a signal here.
79          * But we want to make sure all potential waiters are woken up. */
80         cv_broadcast_irqsave(&rv->cv, &irq_state);
81 }