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 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 50 Return<void> PreparedModelCallback::notify_1_2(ErrorStatus errorStatus, 51 const sp<V1_2::IPreparedModel>& preparedModel) { 52 return notify(errorStatus, preparedModel); 53 } 54 55 void PreparedModelCallback::wait() const { 56 std::unique_lock<std::mutex> lock(mMutex); 57 mCondition.wait(lock, [this] { return mNotified; }); 58 } 59 60 ErrorStatus PreparedModelCallback::getStatus() const { 61 wait(); 62 return mErrorStatus; 63 } 64 65 sp<V1_0::IPreparedModel> PreparedModelCallback::getPreparedModel() const { 66 wait(); 67 return mPreparedModel; 68 } 69 70 // ExecutionCallback methods begin here 71 72 Return<void> ExecutionCallback::notify(ErrorStatus errorStatus) { 73 notifyInternal(errorStatus, {}, kNoTiming); 74 return Void(); 75 } 76 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 100 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 120 ErrorStatus ExecutionCallback::getStatus() const { 121 wait(); 122 return mErrorStatus; 123 } 124 125 const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const { 126 wait(); 127 return mOutputShapes; 128 } 129 130 Timing ExecutionCallback::getTiming() const { 131 wait(); 132 return mTiming; 133 } 134 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 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 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