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