1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_0_CALLBACKS_H
18 #define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_CALLBACKS_H
19 
20 #include <android-base/thread_annotations.h>
21 #include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
22 #include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
23 #include <hidl/Status.h>
24 #include <condition_variable>
25 #include <mutex>
26 
27 /*
28  * The Callback classes are used internally by the NeuralNetworks runtime to
29  * synchronize between different threads. An asynchronous task is launched
30  * paired with a callback object. When a client thread requires the output being
31  * generated by the asynchronous task, the client thread can wait for the result
32  * and be blocked until it has completed. Any wait may safely be called
33  * concurrently, even on the same callback object. When the asynchronous task
34  * has finished its workload, it must immediately call "notify". If the
35  * asynchronous task has failed to launch, the function that tried to launch the
36  * asynchronous task must immediately call "notify". This "notify" call
37  * awakens any client threads waiting on the callback object.
38  *
39  * These classes exist to enable synchronization across HIDL. When
40  * synchronization is only required in the same process, consider using
41  * std::future, std::mutex, std::condition_variable, or std::experimental::latch
42  * instead.
43  */
44 
45 namespace android::hardware::neuralnetworks::V1_0::implementation {
46 
47 /**
48  * The PreparedModelCallback class is used to receive the error status of
49  * preparing a model as well as the prepared model from a task executing
50  * asynchronously with respect to the runtime. If a calling thread calls wait
51  * or get* on a PreparedModelCallback object and the corresponding asynchronous
52  * task has not finished preparing the model, the calling thread will block
53  * until the asynchronous task has called notify.
54  *
55  * If the callback object is notified more than once, only the results of the
56  * first call to notify are used, and the results from subsequent calls are
57  * discarded.
58  *
59  * This callback object is passed as an argument to IDevice::prepareModel*.
60  */
61 class PreparedModelCallback : public IPreparedModelCallback {
62   public:
63     /**
64      * IPreparedModelCallback::notify marks the callback object with the return
65      * status of the asynchronous model preparation along with the prepared
66      * model, and allows all prior and future wait calls on the
67      * PreparedModelCallback object to proceed.
68      *
69      * IPreparedModelCallback::notify must be called on a given
70      * PreparedModelCallback object.
71      *
72      * If the callback object is notified more than once, only the results of
73      * the first call to notify are used, and the results from subsequent calls
74      * are discarded.
75      *
76      * @param status Error status returned from asynchronously preparing the
77      *     model; will be:
78      *     - NONE if the asynchronous preparation was successful
79      *     - DEVICE_UNAVAILABLE if driver is offline or busy
80      *     - GENERAL_FAILURE if there is an unspecified error
81      *     - INVALID_ARGUMENT if the input model is invalid
82      * @param preparedModel Returned model that has been prepared for execution,
83      *     nullptr if the model was unable to be prepared.
84      */
85     Return<void> notify(ErrorStatus status, const sp<IPreparedModel>& preparedModel) override;
86 
87     /**
88      * PreparedModelCallback::wait blocks until notify has been called on the
89      * callback object.
90      */
91     void wait() const;
92 
93     /**
94      * Retrieves the error status returned from the asynchronous task launched
95      * by IDevice::prepareModel*. If IDevice::prepareModel* has not finished
96      * asynchronously preparing the model, this call will block until the
97      * asynchronous task notifies the object.
98      *
99      * @return status Error status returned from asynchronously preparing the
100      *     model; will be:
101      *     - NONE if the asynchronous preparation was successful
102      *     - DEVICE_UNAVAILABLE if driver is offline or busy
103      *     - GENERAL_FAILURE if there is an unspecified error
104      *     - INVALID_ARGUMENT if the input model is invalid
105      */
106     ErrorStatus getStatus() const;
107 
108     /**
109      * Retrieves the model that has been prepared for execution from the
110      * asynchronous task launched by IDevice::prepareModel*. If
111      * IDevice::prepareModel* has not finished asynchronously preparing the
112      * model, this call will block until the asynchronous task notifies the
113      * object.
114      *
115      * @return preparedModel Returned model that has been prepared for
116      *     execution, nullptr if the model was unable to be prepared.
117      */
118     sp<IPreparedModel> getPreparedModel() const;
119 
120   private:
121     mutable std::mutex mMutex;
122     mutable std::condition_variable mCondition;
123     bool mNotified GUARDED_BY(mMutex) = false;
124     ErrorStatus mErrorStatus = ErrorStatus::GENERAL_FAILURE;
125     sp<IPreparedModel> mPreparedModel;
126 };
127 
128 /**
129  * The ExecutionCallback class is used to receive the results of the execution
130  * from a task executing asynchronously with respect to the runtime. If a
131  * calling thread calls wait or get* on a ExecutionCallback object and the
132  * corresponding asynchronous task has not finished the execution, the calling
133  * thread will block until the asynchronous task has called notify.
134  *
135  * If the callback object is notified more than once, only the results of the
136  * first call to notify are used, and the results from subsequent calls are
137  * discarded.
138  *
139  * This callback object is passed as an argument to IPreparedModel::execute*.
140  */
141 class ExecutionCallback : public IExecutionCallback {
142   public:
143     /**
144      * IExecutionCallback::notify marks the callback object with the return
145      * status of the asynchronous execution that held this callback and enables
146      * all prior and future wait calls on the ExecutionCallback object to
147      * proceed.
148      *
149      * IExecutionCallback::notify must be called on a given ExecutionCallback
150      * object.
151      *
152      * If the callback object is notified more than once, only the results of
153      * the first call to notify are used, and the results from subsequent calls
154      * are discarded.
155      *
156      * @param status Error status returned from launching the asynchronous task
157      *     (if the launch fails) or from the asynchronous task itself (if the
158      *     launch succeeds). Must be:
159      *     - NONE if the asynchronous execution was successful
160      *     - DEVICE_UNAVAILABLE if driver is offline or busy
161      *     - GENERAL_FAILURE if there is an unspecified error
162      *     - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is not large
163      *         enough to store the resultant values
164      *     - INVALID_ARGUMENT if the input request is invalid
165      */
166     Return<void> notify(ErrorStatus status) override;
167 
168     /**
169      * ExecutionCallback::wait blocks until notify has been called on the
170      * callback object.
171      */
172     void wait() const;
173 
174     /**
175      * Retrieves the error status returned from the asynchronous task launched
176      * by IPreparedModel::execute. If IPreparedModel::execute has not finished
177      * asynchronously executing, this call will block until the asynchronous
178      * task notifies the object.
179      *
180      * @return status Error status returned from launching the asynchronous task
181      *     (if the launch fails) or from the asynchronous task itself (if the
182      *     launch succeeds). Must be:
183      *     - NONE if the asynchronous execution was successful
184      *     - DEVICE_UNAVAILABLE if driver is offline or busy
185      *     - GENERAL_FAILURE if the asynchronous task resulted in an unspecified
186      *         error
187      *     - OUTPUT_INSUFFICIENT_SIZE if at least one output operand buffer is
188      *         not large enough to store the corresponding output
189      *     - INVALID_ARGUMENT if one of the input arguments to prepareModel is
190      *         invalid
191      */
192     ErrorStatus getStatus() const;
193 
194   private:
195     mutable std::mutex mMutex;
196     mutable std::condition_variable mCondition;
197     bool mNotified GUARDED_BY(mMutex) = false;
198     ErrorStatus mErrorStatus = ErrorStatus::GENERAL_FAILURE;
199 };
200 
201 }  // namespace android::hardware::neuralnetworks::V1_0::implementation
202 
203 #endif  // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_CALLBACKS_H
204