blob: 4f7f2f603e369b7106f0706e2eea23ccf3e53575 [file] [log] [blame]
Michael Butler0e2ac1b2017-09-01 10:59:38 -07001#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_0_EVENT_H
2#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_EVENT_H
3
4#include <android/hardware/neuralnetworks/1.0/IEvent.h>
5#include <chrono>
6#include <condition_variable>
7#include <functional>
8#include <hidl/MQDescriptor.h>
9#include <hidl/Status.h>
10#include <mutex>
11#include <thread>
12
13namespace android {
14namespace hardware {
15namespace neuralnetworks {
16namespace V1_0 {
17namespace implementation {
18
19using ::android::hardware::hidl_array;
20using ::android::hardware::hidl_memory;
21using ::android::hardware::hidl_string;
22using ::android::hardware::hidl_vec;
23using ::android::hardware::Return;
24using ::android::hardware::Void;
25using ::android::sp;
26
27using ReturnedStatus = ::android::hardware::neuralnetworks::V1_0::Status;
28
29/**
30 * The Event class is used internally by the Neuralnetworks runtime to
31 * synchronize between different threads. An asynchronous task is launched
32 * paired with an event object. When a client thread requires the output being
33 * processed by the asynchronous task, the client thread can wait for the result
34 * and be blocked until it has completed or a timeout condition has been
35 * reached, or poll the result periodically. Both poll and wait* may safely be
36 * called concurrently, even on the same event. When the server thread has
37 * completed, it should immediately call "notify" to indicate the corresponding
38 * output has been produced and awaken any client threads waiting on the event.
39 *
40 * This class exists to enable synchronization across HIDL. When synchronization
41 * is only required in the same process, consider using std::future, std::mutex,
42 * std::condition_variable, or std::experimental::latch instead.
43 */
44struct Event : public IEvent {
45 Event();
46 ~Event() override;
47
48 /**
49 * Event::Status::WAITING -- The corresponding asynchronous execution has
50 * not yet finished.
51 * Event::Status::SUCCESS -- The corresponding asynchronous execution has
52 * succeeded and the output is ready to be
53 * consumed.
54 * Event::Status::TIMEOUT -- The calling thread has waited longer than the
55 * user has specified. This only applies to the
56 * methods Event::wait_for and Event::wait_until.
57 * Event::Status::ERROR -- The corresponding asynchronous execution has
58 * failed to properly execute.
59 */
60 enum class Status : uint32_t {
61 WAITING,
62 SUCCESS,
63 TIMEOUT,
64 ERROR,
65 };
66
67 /**
68 * IEvent::notify marks the event with the return status of the
69 * asynchronous call the event is paired with and enables all
70 * prior and future wait calls on the Event object to proceed. The
71 * call to IEvent::notify happens before any wait* calls on
72 * this event return (except in the case of TIMEOUT) and before
73 * any poll calls that see the resulting status. The asynchronous
74 * call the event is paired with must ensure that any update to
75 * state that should be visible to the caller of wait* or poll
76 * happens before the call to IEvent::notify.
77 *
78 * IEvent::notify can be called at most once on a given event.
79 *
80 * @param neuralnetworks::V1_0::Status SUCCESS or ERROR
81 */
82 Return<void> notify(ReturnedStatus status) override;
83
84 /**
85 * Event::poll returns the current status of the event.
86 *
87 * @return Status SUCCESS, ERROR, or WAITING
88 */
89 Event::Status poll();
90
91 /**
92 * Event::wait blocks until the event has been signaled.
93 *
94 * @return Status SUCCESS or ERROR
95 */
96 Event::Status wait();
97
98 /**
99 * Event::wait_for blocks until the event has been signaled or the time
100 * duration from the time the wait_for function was called has expired,
101 * whichever comes first.
102 *
103 * @return Status SUCCESS, ERROR, or TIMEOUT
104 */
105 template<class Rep, class Period>
106 Event::Status wait_for(const std::chrono::duration<Rep,Period>& timeout_duration);
107
108 /**
109 * Event::wait_until blocks until the event has been signaled or a certain
110 * time has been reached, whichever comes first.
111 *
112 * @return Status SUCCESS, ERROR, or TIMEOUT
113 */
114 template<class Clock, class Duration>
115 Event::Status wait_until(const std::chrono::time_point<Clock,Duration>& timeout_duration);
116
117 /**
118 * Event::on_finish binds a callback function to the event. The
119 * callback will be executed when IEvent::notify is called, before
120 * any calls to wait* return. (Note that wait_for or wait_until
121 * can return TIMEOUT before IEvent::notify is called for the
122 * first time, and hence before the callback is executed.)
123 *
124 * The callback function must not synchronize with or otherwise
125 * access the event object it is bound to.
126 *
127 * Event::on_finish can be called at most once on a given event.
128 *
129 * @param callback Function to be invoked the first time IEvent::notify is
130 * called. Must have a target -- i.e., must not compare equal
131 * to nullptr. Callback returns true if it successfully
132 * completes, false if it fails.
133 * @return bool True if the callback was successfully bound, false if
134 * unsuccessful.
135 *
136 * TODO: What if notify has already been called before on_finish?
137 * TODO: Why does the return value of the callback matter?
138 */
David Gross0380d7f2017-09-11 16:23:17 -0700139 bool on_finish(std::function<bool(void)> callback);
Michael Butler0e2ac1b2017-09-01 10:59:38 -0700140
141 /**
David Gross0380d7f2017-09-11 16:23:17 -0700142 * Event::bind_thread binds a thread to the event for later use by
143 * Event::join_thread.
144 *
145 * The thread must be passed using std::move.
146 *
147 * Once a thread is bound with Event::bind_thread, the client code
148 * should ensure that one of the following occurs before the event is
149 * destroyed:
150 * - Event::join_thread has been called.
151 * - Event::wait has been called.
152 * - Event::wait_for has been called and returned other than TIMEOUT.
153 * - Event::wait_until has been called and returned other than TIMEOUT.
Michael Butler0e2ac1b2017-09-01 10:59:38 -0700154 *
155 * The bound thread shall not call any Event method with the exception of
156 * IEvent::notify, which it will call when the thread has finished its
157 * computation.
158 *
159 * Event::bind_thread can be called at most once on a given event.
160 *
161 * @param asyncThread Thread to be bound to the event. The thread object
162 * must represent a thread of execution -- i.e.,
163 * asyncThread.joinable() must be true.
164 * @return bool True if successful, false if thread was not properly bound.
165 */
David Gross0380d7f2017-09-11 16:23:17 -0700166 bool bind_thread(std::thread&& asyncThread);
167
168 /**
169 * Event::join_thread ensures that the thread (if any) bound to
170 * this event with Event::bind_thread has fully finished and
171 * cleaned its resources. It is legal to call this function
172 * multiple times, concurrently or sequentially.
173 */
174 void join_thread();
Michael Butler0e2ac1b2017-09-01 10:59:38 -0700175
176 private:
David Gross0380d7f2017-09-11 16:23:17 -0700177 // Same as Event::join_thread but assumes we already hold a lock on mMutex.
178 void join_thread_locked();
179
Michael Butler0e2ac1b2017-09-01 10:59:38 -0700180 Status mStatus;
181 std::mutex mMutex;
182 std::condition_variable mCondition;
183 std::function<bool(void)> mCallback;
184 std::thread mThread;
185};
186
187
188// template function implementations
189
190template<class Rep, class Period>
191Event::Status Event::wait_for(const std::chrono::duration<Rep,Period>& timeout_duration) {
192 std::unique_lock<std::mutex> lock(mMutex);
193 std::cv_status status = mCondition.wait_for(lock, timeout_duration,
194 [this]{return mStatus != Status::WAITING;});
David Gross0380d7f2017-09-11 16:23:17 -0700195 if (status != std::cv_status::timeout) {
196 join_thread_locked();
197 }
Michael Butler0e2ac1b2017-09-01 10:59:38 -0700198 return status != std::cv_status::timeout ? mStatus : Status::TIMEOUT;
199}
200
201template<class Clock, class Duration>
202Event::Status Event::wait_until(const std::chrono::time_point<Clock,Duration>& timeout_time) {
203 std::unique_lock<std::mutex> lock(mMutex);
204 std::cv_status status = mCondition.wait_until(lock, timeout_time,
205 [this]{return mStatus != Status::WAITING;});
David Gross0380d7f2017-09-11 16:23:17 -0700206 if (status != std::cv_status::timeout) {
207 join_thread_locked();
208 }
Michael Butler0e2ac1b2017-09-01 10:59:38 -0700209 return status != std::cv_status::timeout ? mStatus : Status::TIMEOUT;
210}
211
212} // namespace implementation
213} // namespace V1_0
214} // namespace neuralnetworks
215} // namespace hardware
216} // namespace android
217
218#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_EVENT_H