1 /*
2 * Copyright (C) 2017 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 #include "Callbacks.h"
18
19 #include <android-base/logging.h>
20
21 #include <limits>
22
23 namespace android::hardware::neuralnetworks::V1_2::implementation {
24
25 constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(),
26 .timeInDriver = std::numeric_limits<uint64_t>::max()};
27
28 // PreparedModelCallback methods begin here
29
notify(ErrorStatus errorStatus,const sp<V1_0::IPreparedModel> & preparedModel)30 Return<void> PreparedModelCallback::notify(ErrorStatus errorStatus,
31 const sp<V1_0::IPreparedModel>& preparedModel) {
32 {
33 std::lock_guard<std::mutex> hold(mMutex);
34
35 // quick-return if object has already been notified
36 if (mNotified) {
37 return Void();
38 }
39
40 // store results and mark as notified
41 mErrorStatus = errorStatus;
42 mPreparedModel = preparedModel;
43 mNotified = true;
44 }
45
46 mCondition.notify_all();
47 return Void();
48 }
49
notify_1_2(ErrorStatus errorStatus,const sp<V1_2::IPreparedModel> & preparedModel)50 Return<void> PreparedModelCallback::notify_1_2(ErrorStatus errorStatus,
51 const sp<V1_2::IPreparedModel>& preparedModel) {
52 return notify(errorStatus, preparedModel);
53 }
54
wait() const55 void PreparedModelCallback::wait() const {
56 std::unique_lock<std::mutex> lock(mMutex);
57 mCondition.wait(lock, [this] { return mNotified; });
58 }
59
getStatus() const60 ErrorStatus PreparedModelCallback::getStatus() const {
61 wait();
62 return mErrorStatus;
63 }
64
getPreparedModel() const65 sp<V1_0::IPreparedModel> PreparedModelCallback::getPreparedModel() const {
66 wait();
67 return mPreparedModel;
68 }
69
70 // ExecutionCallback methods begin here
71
notify(ErrorStatus errorStatus)72 Return<void> ExecutionCallback::notify(ErrorStatus errorStatus) {
73 notifyInternal(errorStatus, {}, kNoTiming);
74 return Void();
75 }
76
notify_1_2(ErrorStatus errorStatus,const hidl_vec<OutputShape> & outputShapes,const Timing & timing)77 Return<void> ExecutionCallback::notify_1_2(ErrorStatus errorStatus,
78 const hidl_vec<OutputShape>& outputShapes,
79 const Timing& timing) {
80 if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
81 // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE.
82 if (outputShapes.size() == 0) {
83 LOG(ERROR) << "Notified with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE";
84 notifyInternal(ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
85 return Void();
86 }
87 } else if (errorStatus != ErrorStatus::NONE) {
88 // outputShapes must be empty if errorStatus is neither NONE nor OUTPUT_INSUFFICIENT_SIZE.
89 if (outputShapes.size() != 0) {
90 LOG(ERROR) << "Notified with non-empty output shape vector when error status is "
91 "neither NONE nor OUTPUT_INSUFFICIENT_SIZE";
92 notifyInternal(ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
93 return Void();
94 }
95 }
96 notifyInternal(errorStatus, outputShapes, timing);
97 return Void();
98 }
99
wait() const100 void ExecutionCallback::wait() const {
101 std::unique_lock<std::mutex> lock(mMutex);
102 mCondition.wait(lock, [this] { return mNotified; });
103
104 /*
105 * Note that we cannot call std::thread::join from ExecutionCallback's
106 * destructor: ExecutionCallback is intended to be reference counted, and it
107 * is possible that the reference count drops to zero in the bound thread,
108 * causing the bound thread to call this destructor. If a thread tries to
109 * join itself, it throws an exception, producing a message like the
110 * following:
111 *
112 * terminating with uncaught exception of type std::__1::system_error:
113 * thread::join failed: Resource deadlock would occur
114 */
115 if (mThread.joinable()) {
116 mThread.join();
117 }
118 }
119
getStatus() const120 ErrorStatus ExecutionCallback::getStatus() const {
121 wait();
122 return mErrorStatus;
123 }
124
getOutputShapes() const125 const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
126 wait();
127 return mOutputShapes;
128 }
129
getTiming() const130 Timing ExecutionCallback::getTiming() const {
131 wait();
132 return mTiming;
133 }
134
bindThread(std::thread asyncThread)135 bool ExecutionCallback::bindThread(std::thread asyncThread) {
136 std::lock_guard<std::mutex> lock(mMutex);
137
138 // Ensure ExecutionCallback object does not already have a thread bound
139 if (mThread.joinable()) {
140 LOG(ERROR) << "ExecutionCallback::bindThread -- a thread has already been bound to this "
141 "callback object";
142 return false;
143 }
144
145 // Ensure the new thread is valid
146 if (!asyncThread.joinable()) {
147 LOG(ERROR) << "ExecutionCallback::bindThread -- the new thread is not joinable";
148 return false;
149 }
150
151 mThread = std::move(asyncThread);
152 return true;
153 }
154
setOnFinish(const ExecutionFinish & finish)155 void ExecutionCallback::setOnFinish(const ExecutionFinish& finish) {
156 std::lock_guard<std::mutex> hold(mMutex);
157
158 // Ensure ExecutionCallback object does not already have a "finish" callback
159 if (mOnFinish != nullptr) {
160 LOG(ERROR) << "ExecutionCallback::setOnFinish -- object already has a \"finish\" callback";
161 return;
162 }
163
164 // Ensure new "finish" callback is valid
165 if (finish == nullptr) {
166 LOG(ERROR) << "ExecutionCallback::setOnFinish -- \"finish\" callback is invalid";
167 return;
168 }
169
170 // Essure ExecutionCallback object has not already been notified
171 if (mNotified) {
172 LOG(ERROR) << "ExecutionCallback::setOnFinish -- ExecutionCallback has already been "
173 "notified with results";
174 return;
175 }
176
177 mOnFinish = finish;
178 }
179
notifyInternal(ErrorStatus errorStatus,const hidl_vec<OutputShape> & outputShapes,const Timing & timing)180 void ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
181 const hidl_vec<OutputShape>& outputShapes,
182 const Timing& timing) {
183 {
184 std::lock_guard<std::mutex> hold(mMutex);
185
186 // quick-return if object has already been notified
187 if (mNotified) {
188 return;
189 }
190
191 mErrorStatus = errorStatus;
192 mOutputShapes = outputShapes;
193 mTiming = timing;
194 mNotified = true;
195
196 if (mOnFinish != nullptr) {
197 ErrorStatus status = mOnFinish(mErrorStatus, mOutputShapes);
198 mOnFinish = nullptr;
199 if (status != ErrorStatus::NONE) {
200 mErrorStatus = status;
201 }
202 }
203 }
204 mCondition.notify_all();
205 }
206
207 } // namespace android::hardware::neuralnetworks::V1_2::implementation
208