Michael Butler | 8aafe68 | 2017-09-22 18:21:59 -0700 | [diff] [blame] | 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 | |
Slava Shklyaev | a181287 | 2020-11-11 15:41:18 +0000 | [diff] [blame] | 17 | #define LOG_TAG "ExecutionCallback" |
Stefano Galarraga | 6c3a8cc | 2019-07-02 16:28:53 +0100 | [diff] [blame] | 18 | |
Slava Shklyaev | a181287 | 2020-11-11 15:41:18 +0000 | [diff] [blame] | 19 | #include "ExecutionCallback.h" |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 20 | |
Michael Butler | 8aafe68 | 2017-09-22 18:21:59 -0700 | [diff] [blame] | 21 | #include <android-base/logging.h> |
Slava Shklyaev | 9f29f43 | 2020-08-13 13:16:03 +0100 | [diff] [blame] | 22 | |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 23 | #include <limits> |
Xusong Wang | 36f3cad | 2019-10-23 10:35:51 -0700 | [diff] [blame] | 24 | #include <utility> |
| 25 | #include <vector> |
Michael Butler | 8aafe68 | 2017-09-22 18:21:59 -0700 | [diff] [blame] | 26 | |
Michael Butler | 6bf05b2 | 2019-07-11 11:45:01 -0700 | [diff] [blame] | 27 | namespace android::nn { |
| 28 | |
Slava Shklyaev | a6d95b1 | 2020-11-27 17:29:10 +0000 | [diff] [blame] | 29 | void ExecutionCallback::notify(ErrorStatus status, const std::vector<OutputShape>& outputShapes, |
| 30 | const Timing& timing) { |
| 31 | notifyInternal(status, outputShapes, timing); |
Xusong Wang | c73a89b | 2018-11-05 09:59:30 -0800 | [diff] [blame] | 32 | } |
| 33 | |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 34 | void ExecutionCallback::wait() const { |
| 35 | std::unique_lock<std::mutex> lock(mMutex); |
| 36 | mCondition.wait(lock, [this] { return mNotified; }); |
| 37 | |
| 38 | /* |
| 39 | * Note that we cannot call std::thread::join from ExecutionCallback's |
| 40 | * destructor: ExecutionCallback is intended to be reference counted, and it |
| 41 | * is possible that the reference count drops to zero in the bound thread, |
| 42 | * causing the bound thread to call this destructor. If a thread tries to |
| 43 | * join itself, it throws an exception, producing a message like the |
| 44 | * following: |
| 45 | * |
| 46 | * terminating with uncaught exception of type std::__1::system_error: |
| 47 | * thread::join failed: Resource deadlock would occur |
| 48 | */ |
| 49 | if (mThread.joinable()) { |
| 50 | mThread.join(); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | ErrorStatus ExecutionCallback::getStatus() const { |
Michael Butler | 8aafe68 | 2017-09-22 18:21:59 -0700 | [diff] [blame] | 55 | wait(); |
| 56 | return mErrorStatus; |
| 57 | } |
| 58 | |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 59 | const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const { |
Xusong Wang | 0c0cbb5 | 2018-11-06 18:48:20 -0800 | [diff] [blame] | 60 | wait(); |
| 61 | return mOutputShapes; |
| 62 | } |
| 63 | |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 64 | Timing ExecutionCallback::getTiming() const { |
David Gross | 257ee7a | 2019-01-23 14:59:10 -0800 | [diff] [blame] | 65 | wait(); |
| 66 | return mTiming; |
| 67 | } |
| 68 | |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 69 | bool ExecutionCallback::bindThread(std::thread asyncThread) { |
| 70 | std::lock_guard<std::mutex> lock(mMutex); |
| 71 | |
| 72 | // Ensure ExecutionCallback object does not already have a thread bound |
| 73 | if (mThread.joinable()) { |
| 74 | LOG(ERROR) << "ExecutionCallback::bindThread -- a thread has already been bound to this " |
| 75 | "callback object"; |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | // Ensure the new thread is valid |
| 80 | if (!asyncThread.joinable()) { |
| 81 | LOG(ERROR) << "ExecutionCallback::bindThread -- the new thread is not joinable"; |
| 82 | return false; |
| 83 | } |
| 84 | |
| 85 | mThread = std::move(asyncThread); |
| 86 | return true; |
| 87 | } |
| 88 | |
| 89 | void ExecutionCallback::setOnFinish(const ExecutionFinish& finish) { |
| 90 | std::lock_guard<std::mutex> hold(mMutex); |
| 91 | |
| 92 | // Ensure ExecutionCallback object does not already have a "finish" callback |
| 93 | if (mOnFinish != nullptr) { |
| 94 | LOG(ERROR) << "ExecutionCallback::setOnFinish -- object already has a \"finish\" callback"; |
| 95 | return; |
| 96 | } |
| 97 | |
| 98 | // Ensure new "finish" callback is valid |
| 99 | if (finish == nullptr) { |
| 100 | LOG(ERROR) << "ExecutionCallback::setOnFinish -- \"finish\" callback is invalid"; |
| 101 | return; |
| 102 | } |
| 103 | |
| 104 | // Essure ExecutionCallback object has not already been notified |
| 105 | if (mNotified) { |
| 106 | LOG(ERROR) << "ExecutionCallback::setOnFinish -- ExecutionCallback has already been " |
| 107 | "notified with results"; |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | mOnFinish = finish; |
| 112 | } |
| 113 | |
Slava Shklyaev | a6d95b1 | 2020-11-27 17:29:10 +0000 | [diff] [blame] | 114 | void ExecutionCallback::notifyInternal(ErrorStatus errorStatus, |
| 115 | std::vector<OutputShape> outputShapes, Timing timing) { |
Michael Butler | e6785e4 | 2019-12-12 16:25:03 -0800 | [diff] [blame] | 116 | // check results |
Slava Shklyaev | a6d95b1 | 2020-11-27 17:29:10 +0000 | [diff] [blame] | 117 | { |
Michael Butler | bf25823 | 2019-12-16 18:32:45 -0800 | [diff] [blame] | 118 | if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) { |
| 119 | // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE. |
| 120 | if (outputShapes.size() == 0) { |
| 121 | LOG(ERROR) |
| 122 | << "Notified with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE"; |
| 123 | errorStatus = ErrorStatus::GENERAL_FAILURE; |
| 124 | outputShapes = {}; |
Slava Shklyaev | 9f29f43 | 2020-08-13 13:16:03 +0100 | [diff] [blame] | 125 | timing = {}; |
Michael Butler | bf25823 | 2019-12-16 18:32:45 -0800 | [diff] [blame] | 126 | } |
| 127 | } else if (errorStatus != ErrorStatus::NONE) { |
| 128 | // outputShapes must be empty if errorStatus is neither NONE nor |
| 129 | // OUTPUT_INSUFFICIENT_SIZE. |
| 130 | if (outputShapes.size() != 0) { |
| 131 | LOG(ERROR) << "Notified with non-empty output shape vector when error status is " |
| 132 | "neither NONE nor OUTPUT_INSUFFICIENT_SIZE"; |
| 133 | errorStatus = ErrorStatus::GENERAL_FAILURE; |
| 134 | outputShapes = {}; |
Slava Shklyaev | 9f29f43 | 2020-08-13 13:16:03 +0100 | [diff] [blame] | 135 | timing = {}; |
Michael Butler | bf25823 | 2019-12-16 18:32:45 -0800 | [diff] [blame] | 136 | } |
Michael Butler | e6785e4 | 2019-12-12 16:25:03 -0800 | [diff] [blame] | 137 | } |
| 138 | } |
| 139 | |
| 140 | // store results |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 141 | { |
| 142 | std::lock_guard<std::mutex> hold(mMutex); |
| 143 | |
| 144 | // quick-return if object has already been notified |
| 145 | if (mNotified) { |
Slava Shklyaev | a6d95b1 | 2020-11-27 17:29:10 +0000 | [diff] [blame] | 146 | return; |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | mErrorStatus = errorStatus; |
Michael Butler | bf25823 | 2019-12-16 18:32:45 -0800 | [diff] [blame] | 150 | mOutputShapes = std::move(outputShapes); |
Michael Butler | fe7b000 | 2019-03-13 18:23:10 -0700 | [diff] [blame] | 151 | mTiming = timing; |
| 152 | mNotified = true; |
| 153 | |
| 154 | if (mOnFinish != nullptr) { |
| 155 | ErrorStatus status = mOnFinish(mErrorStatus, mOutputShapes); |
| 156 | mOnFinish = nullptr; |
| 157 | if (status != ErrorStatus::NONE) { |
| 158 | mErrorStatus = status; |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | mCondition.notify_all(); |
| 163 | } |
| 164 | |
Michael Butler | 6bf05b2 | 2019-07-11 11:45:01 -0700 | [diff] [blame] | 165 | } // namespace android::nn |