1 /* Copyright (c) 2013 The Regents of the University of California
2 * Barret Rhoden <brho@cs.berkeley.edu>
3 * See LICENSE for details.
5 * Plan9 style Rendezvous (http://plan9.bell-labs.com/sys/doc/sleep.html)
7 * We implement it with CVs, and it can handle multiple sleepers/wakers. */
16 void rendez_init(struct rendez *rv)
18 cv_init_irqsave(&rv->cv);
21 void rendez_sleep(struct rendez *rv, int (*cond)(void*), void *arg)
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);
31 cv_unlock_irqsave(&rv->cv, &irq_state);
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. */
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 error(EINTR, "syscall aborted");
48 cv_unlock_irqsave(&rv->cv, &irq_state);
49 dereg_abortable_cv(&cle);
52 /* Force a wakeup of all waiters on the rv, including non-timeout users. For
53 * those, they will just wake up, see the condition is still false (probably)
54 * and go back to sleep. */
55 static void rendez_alarm_handler(struct alarm_waiter *awaiter,
56 struct hw_trapframe *hw_tf)
58 struct rendez *rv = (struct rendez*)awaiter->data;
63 /* Like sleep, but it will timeout in 'usec' microseconds. */
64 void rendez_sleep_timeout(struct rendez *rv, int (*cond)(void*), void *arg,
68 struct alarm_waiter awaiter;
69 struct cv_lookup_elm cle;
70 struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;
74 /* Doing this cond check early, but then unlocking again. Mostly just to
75 * avoid weird issues with the CV lock and the alarm tchain lock. */
76 cv_lock_irqsave(&rv->cv, &irq_state);
78 cv_unlock_irqsave(&rv->cv, &irq_state);
81 cv_unlock_irqsave(&rv->cv, &irq_state);
82 /* The handler will call rendez_wake, but won't mess with the condition
83 * state. It's enough to break us out of cv_wait() to see .on_tchain.
84 * Since all we're doing is poking a rendez, we might as well just do it
85 * from IRQ ctx instead of mucking with an extra RKM. It also avoids issues
86 * with unset_alarm blocking. */
87 init_awaiter_irq(&awaiter, rendez_alarm_handler);
89 set_awaiter_rel(&awaiter, usec);
90 /* Set our alarm on this cpu's tchain. Note that when we sleep in cv_wait,
91 * we could be migrated, and later on we could be unsetting the alarm
93 set_alarm(pcpui_tchain, &awaiter);
94 cv_lock_irqsave(&rv->cv, &irq_state);
95 __reg_abortable_cv(&cle, &rv->cv);
96 /* We could wake early for a few reasons. Legit wakeups after a changed
97 * condition (and we should exit), other alarms with different timeouts (and
98 * we should go back to sleep), etc. Note it is possible for our alarm to
99 * fire immediately upon setting it: before we even cv_lock. */
100 while (!cond(arg) && awaiter.on_tchain) {
101 if (should_abort(&cle)) {
102 cv_unlock_irqsave(&rv->cv, &irq_state);
103 unset_alarm(pcpui_tchain, &awaiter);
104 dereg_abortable_cv(&cle);
105 error(EINTR, "syscall aborted");
110 cv_unlock_irqsave(&rv->cv, &irq_state);
111 dereg_abortable_cv(&cle);
112 /* Turn off our alarm. If it already fired, this is a no-op. Note this
113 * could be cross-core. */
114 unset_alarm(pcpui_tchain, &awaiter);
117 /* plan9 rendez returned a pointer to the proc woken up. we return "true" if we
118 * woke someone up. */
119 bool rendez_wakeup(struct rendez *rv)
121 int8_t irq_state = 0;
123 /* The plan9 style "one sleeper, one waker" could get by with a signal here.
124 * But we want to make sure all potential waiters are woken up. */
125 cv_lock_irqsave(&rv->cv, &irq_state);
126 ret = rv->cv.nr_waiters ? TRUE : FALSE;
127 __cv_broadcast(&rv->cv);
128 cv_unlock_irqsave(&rv->cv, &irq_state);