Remove historical file.
[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 #include <err.h>
15
16 void rendez_init(struct rendez *rv)
17 {
18         cv_init_irqsave(&rv->cv);
19 }
20
21 void rendez_sleep(struct rendez *rv, int (*cond)(void*), void *arg)
22 {
23         int8_t irq_state = 0;
24         struct cv_lookup_elm cle;
25         /* Do a quick check before registering and sleeping.  this is the 'check,
26          * signal, check again' pattern, where the first check is an optimization.
27          * Many rendezes will already be satisfied, so we want to avoid excessive
28          * locking associated with reg/dereg. */
29         cv_lock_irqsave(&rv->cv, &irq_state);
30         if (cond(arg)) {
31                 cv_unlock_irqsave(&rv->cv, &irq_state);
32                 return;
33         }
34         __reg_abortable_cv(&cle, &rv->cv);
35         /* Mesa-style semantics, which is definitely what you want.  See the
36          * discussion at the end of the URL above. */
37         while (!cond(arg)) {
38                 /* it's okay if we miss the ABORT flag; we hold the cv lock, so an
39                  * aborter's broadcast is waiting until we unlock. */
40                 if (should_abort(&cle)) {
41                         cv_unlock_irqsave(&rv->cv, &irq_state);
42                         dereg_abortable_cv(&cle);
43                         set_errno(EINTR);
44                         error("syscall aborted");
45                 }
46                 cv_wait(&rv->cv);
47                 cpu_relax();
48         }
49         cv_unlock_irqsave(&rv->cv, &irq_state);
50         dereg_abortable_cv(&cle);
51 }
52
53 /* Force a wakeup of all waiters on the rv, including non-timeout users.  For
54  * those, they will just wake up, see the condition is still false (probably)
55  * and go back to sleep. */
56 static void rendez_alarm_handler(struct alarm_waiter *awaiter)
57 {
58         struct rendez *rv = (struct rendez*)awaiter->data;
59         rendez_wakeup(rv);
60 }
61
62 /* Like sleep, but it will timeout in 'msec' milliseconds. */
63 void rendez_sleep_timeout(struct rendez *rv, int (*cond)(void*), void *arg,
64                           unsigned int msec)
65 {
66         int8_t irq_state = 0;
67         struct alarm_waiter awaiter;
68         struct cv_lookup_elm cle;
69         struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
70
71         assert((int)msec > 0);
72         /* Doing this cond check early, but then unlocking again.  Mostly just to
73          * avoid weird issues with the CV lock and the alarm tchain lock. */
74         cv_lock_irqsave(&rv->cv, &irq_state);
75         if (cond(arg)) {
76                 cv_unlock_irqsave(&rv->cv, &irq_state);
77                 return;
78         }
79         cv_unlock_irqsave(&rv->cv, &irq_state);
80         /* The handler will call rendez_wake, but won't mess with the condition
81          * state.  It's enough to break us out of cv_wait() to see .on_tchain. */
82         init_awaiter(&awaiter, rendez_alarm_handler);
83         awaiter.data = rv;
84         set_awaiter_rel(&awaiter, msec * 1000);
85         /* Set our alarm on this cpu's tchain.  Note that when we sleep in cv_wait,
86          * we could be migrated, and later on we could be unsetting the alarm
87          * remotely. */
88         set_alarm(pcpui_tchain, &awaiter);
89         cv_lock_irqsave(&rv->cv, &irq_state);
90         __reg_abortable_cv(&cle, &rv->cv);
91         /* We could wake early for a few reasons.  Legit wakeups after a changed
92          * condition (and we should exit), other alarms with different timeouts (and
93          * we should go back to sleep), etc.  Note it is possible for our alarm to
94          * fire immediately upon setting it: before we even cv_lock. */
95         while (!cond(arg) && awaiter.on_tchain) {
96                 if (should_abort(&cle)) {
97                         cv_unlock_irqsave(&rv->cv, &irq_state);
98                         unset_alarm(pcpui_tchain, &awaiter);
99                         dereg_abortable_cv(&cle);
100                         set_errno(EINTR);
101                         error("syscall aborted");
102                 }
103                 cv_wait(&rv->cv);
104                 cpu_relax();
105         }
106         cv_unlock_irqsave(&rv->cv, &irq_state);
107         dereg_abortable_cv(&cle);
108         /* Turn off our alarm.  If it already fired, this is a no-op.  Note this
109          * could be cross-core. */
110         unset_alarm(pcpui_tchain, &awaiter);
111 }
112
113 /* plan9 rendez returned a pointer to the proc woken up.  we return "true" if we
114  * woke someone up. */
115 bool rendez_wakeup(struct rendez *rv)
116 {
117         int8_t irq_state = 0;
118         bool ret;
119         /* The plan9 style "one sleeper, one waker" could get by with a signal here.
120          * But we want to make sure all potential waiters are woken up. */
121         cv_lock_irqsave(&rv->cv, &irq_state);
122         ret = rv->cv.nr_waiters ? TRUE : FALSE;
123         __cv_broadcast(&rv->cv);
124         cv_unlock_irqsave(&rv->cv, &irq_state);
125         return ret;
126 }