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