blob: 2e19585ef21199d82d9f34f96957b0009162992f [file] [log] [blame]
#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_0_EVENT_H
#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_EVENT_H
#include <android/hardware/neuralnetworks/1.0/IEvent.h>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <mutex>
#include <thread>
namespace android {
namespace hardware {
namespace neuralnetworks {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
using ReturnedStatus = ::android::hardware::neuralnetworks::V1_0::Status;
/**
* The Event class is used internally by the Neuralnetworks runtime to
* synchronize between different threads. An asynchronous task is launched
* paired with an event object. When a client thread requires the output being
* processed by the asynchronous task, the client thread can wait for the result
* and be blocked until it has completed or a timeout condition has been
* reached, or poll the result periodically. Both poll and wait* may safely be
* called concurrently, even on the same event. When the server thread has
* completed, it should immediately call "notify" to indicate the corresponding
* output has been produced and awaken any client threads waiting on the event.
*
* This class exists to enable synchronization across HIDL. When synchronization
* is only required in the same process, consider using std::future, std::mutex,
* std::condition_variable, or std::experimental::latch instead.
*/
struct Event : public IEvent {
Event();
~Event() override;
/**
* Event::Status::WAITING -- The corresponding asynchronous execution has
* not yet finished.
* Event::Status::SUCCESS -- The corresponding asynchronous execution has
* succeeded and the output is ready to be
* consumed.
* Event::Status::TIMEOUT -- The calling thread has waited longer than the
* user has specified. This only applies to the
* methods Event::wait_for and Event::wait_until.
* Event::Status::ERROR -- The corresponding asynchronous execution has
* failed to properly execute.
*/
enum class Status : uint32_t {
WAITING,
SUCCESS,
TIMEOUT,
ERROR,
};
/**
* IEvent::notify marks the event with the return status of the
* asynchronous call the event is paired with and enables all
* prior and future wait calls on the Event object to proceed. The
* call to IEvent::notify happens before any wait* calls on
* this event return (except in the case of TIMEOUT) and before
* any poll calls that see the resulting status. The asynchronous
* call the event is paired with must ensure that any update to
* state that should be visible to the caller of wait* or poll
* happens before the call to IEvent::notify.
*
* IEvent::notify can be called at most once on a given event.
*
* @param neuralnetworks::V1_0::Status SUCCESS or ERROR
*/
Return<void> notify(ReturnedStatus status) override;
/**
* Event::poll returns the current status of the event.
*
* @return Status SUCCESS, ERROR, or WAITING
*/
Event::Status poll();
/**
* Event::wait blocks until the event has been signaled.
*
* @return Status SUCCESS or ERROR
*/
Event::Status wait();
/**
* Event::wait_for blocks until the event has been signaled or the time
* duration from the time the wait_for function was called has expired,
* whichever comes first.
*
* @return Status SUCCESS, ERROR, or TIMEOUT
*/
template<class Rep, class Period>
Event::Status wait_for(const std::chrono::duration<Rep,Period>& timeout_duration);
/**
* Event::wait_until blocks until the event has been signaled or a certain
* time has been reached, whichever comes first.
*
* @return Status SUCCESS, ERROR, or TIMEOUT
*/
template<class Clock, class Duration>
Event::Status wait_until(const std::chrono::time_point<Clock,Duration>& timeout_duration);
/**
* Event::on_finish binds a callback function to the event. The
* callback will be executed when IEvent::notify is called, before
* any calls to wait* return. (Note that wait_for or wait_until
* can return TIMEOUT before IEvent::notify is called for the
* first time, and hence before the callback is executed.)
*
* The callback function must not synchronize with or otherwise
* access the event object it is bound to.
*
* Event::on_finish can be called at most once on a given event.
*
* @param callback Function to be invoked the first time IEvent::notify is
* called. Must have a target -- i.e., must not compare equal
* to nullptr. Callback returns true if it successfully
* completes, false if it fails.
* @return bool True if the callback was successfully bound, false if
* unsuccessful.
*
* TODO: What if notify has already been called before on_finish?
* TODO: Why does the return value of the callback matter?
*/
bool on_finish(std::function<bool(void)> callback);
/**
* Event::bind_thread binds a thread to the event ensuring that the thread
* has fully finished and cleaned its resources before the event is
* destroyed. The thread should be bound using std::move.
*
* The bound thread shall not call any Event method with the exception of
* IEvent::notify, which it will call when the thread has finished its
* computation.
*
* Event::bind_thread can be called at most once on a given event.
*
* @param asyncThread Thread to be bound to the event. The thread object
* must represent a thread of execution -- i.e.,
* asyncThread.joinable() must be true.
* @return bool True if successful, false if thread was not properly bound.
*/
bool bind_thread(std::thread&& asyncThread);
private:
Status mStatus;
std::mutex mMutex;
std::condition_variable mCondition;
std::function<bool(void)> mCallback;
std::thread mThread;
};
// template function implementations
template<class Rep, class Period>
Event::Status Event::wait_for(const std::chrono::duration<Rep,Period>& timeout_duration) {
std::unique_lock<std::mutex> lock(mMutex);
std::cv_status status = mCondition.wait_for(lock, timeout_duration,
[this]{return mStatus != Status::WAITING;});
return status != std::cv_status::timeout ? mStatus : Status::TIMEOUT;
}
template<class Clock, class Duration>
Event::Status Event::wait_until(const std::chrono::time_point<Clock,Duration>& timeout_time) {
std::unique_lock<std::mutex> lock(mMutex);
std::cv_status status = mCondition.wait_until(lock, timeout_time,
[this]{return mStatus != Status::WAITING;});
return status != std::cv_status::timeout ? mStatus : Status::TIMEOUT;
}
} // namespace implementation
} // namespace V1_0
} // namespace neuralnetworks
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_EVENT_H