1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2008-2014 Travis Geiselbrecht
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7 
8 #pragma once
9 
10 #include <err.h>
11 #include <kernel/thread.h>
12 #include <kernel/timer.h>
13 #include <stdbool.h>
14 #include <stdint.h>
15 #include <sys/types.h>
16 #include <zircon/compiler.h>
17 #include <zircon/types.h>
18 
19 __BEGIN_CDECLS
20 
21 #define EVENT_MAGIC (0x65766E74) // "evnt"
22 
23 typedef struct event {
24     int magic;
25     bool signaled;
26     uint flags;
27     wait_queue_t wait;
28 } event_t;
29 
30 #define EVENT_FLAG_AUTOUNSIGNAL 1
31 
32 #define EVENT_INITIAL_VALUE(e, initial, _flags)     \
33     {                                               \
34         .magic = EVENT_MAGIC,                       \
35         .signaled = initial,                        \
36         .flags = _flags,                            \
37         .wait = WAIT_QUEUE_INITIAL_VALUE((e).wait), \
38     }
39 
40 // Rules for Events:
41 // - Events may be signaled from interrupt context *but* the reschedule
42 //   parameter must be false in that case.
43 // - Events may not be waited upon from interrupt context.
44 // - Events without FLAG_AUTOUNSIGNAL:
45 //   - Wake up any waiting threads when signaled.
46 //   - Continue to do so (no threads will wait) until unsignaled.
47 // - Events with FLAG_AUTOUNSIGNAL:
48 //   - If one or more threads are waiting when signaled, one thread will
49 //     be woken up and return.  The signaled state will not be set.
50 //   - If no threads are waiting when signaled, the Event will remain
51 //     in the signaled state until a thread attempts to wait (at which
52 //     time it will unsignal atomicly and return immediately) or
53 //     event_unsignal() is called.
54 
55 void event_init(event_t*, bool initial, uint flags);
56 void event_destroy(event_t*);
57 
58 // Wait until deadline
59 // Interruptable arg allows it to return early with ZX_ERR_INTERNAL_INTR_KILLED if thread
60 // is signaled for kill.
61 zx_status_t event_wait_deadline(event_t*, zx_time_t, bool interruptable);
62 
63 // Wait until the event occurs, the slack-adjusted deadline has elapsed, or the thread is
64 // interrupted.
65 zx_status_t event_wait_interruptable(event_t* e, zx_time_t deadline, TimerSlack slack);
66 
67 // no deadline, non interruptable version of the above.
event_wait(event_t * e)68 static inline zx_status_t event_wait(event_t* e) {
69     return event_wait_deadline(e, ZX_TIME_INFINITE, false);
70 }
71 
72 // Version of event_wait_deadline that ignores existing signals in
73 // |signal_mask|. There is no deadline, and the caller must be interruptable.
74 zx_status_t event_wait_with_mask(event_t*, uint signal_mask);
75 
76 int event_signal_etc(event_t*, bool reschedule, zx_status_t result);
77 int event_signal(event_t*, bool reschedule);
78 int event_signal_thread_locked(event_t*) TA_REQ(thread_lock);
79 zx_status_t event_unsignal(event_t*);
80 
event_initialized(const event_t * e)81 static inline bool event_initialized(const event_t* e) {
82     return e->magic == EVENT_MAGIC;
83 }
84 
event_signaled(const event_t * e)85 static inline bool event_signaled(const event_t* e) {
86     return e->signaled;
87 }
88 
89 __END_CDECLS
90 
91 #ifdef __cplusplus
92 
93 // C++ wrapper. This should be waited on from only a single thread, but may be
94 // signaled from many threads (Signal() is thread-safe).
95 class Event {
96 public:
97     Event(uint32_t opts = 0) {
98         event_init(&event_, false, opts);
99     }
~Event()100     ~Event() {
101         event_destroy(&event_);
102     }
103 
104     Event(const Event&) = delete;
105     Event& operator=(const Event&) = delete;
106 
107     // Returns:
108     // ZX_OK - signaled
109     // ZX_ERR_TIMED_OUT - time out expired
110     // ZX_ERR_INTERNAL_INTR_KILLED - thread killed
111     // Or the |status| which the caller specified in Event::Signal(status)
Wait(zx_time_t deadline,TimerSlack slack)112     zx_status_t Wait(zx_time_t deadline, TimerSlack slack) {
113         return event_wait_interruptable(&event_, deadline, slack);
114     }
115 
116     void Signal(zx_status_t status = ZX_OK) {
117         event_signal_etc(&event_, true, status);
118     }
119 
Unsignal()120     zx_status_t Unsignal() {
121         return event_unsignal(&event_);
122     }
123 
124 private:
125     event_t event_;
126 };
127 
128 #endif // __cplusplus
129