• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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