blob: b2ac2aece737ddb624d52fbe4a65c5333a89d532 [file] [log] [blame]
Michael Butler8aafe682017-09-22 18:21:59 -07001/*
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 Shklyaeva1812872020-11-11 15:41:18 +000017#define LOG_TAG "ExecutionCallback"
Stefano Galarraga6c3a8cc2019-07-02 16:28:53 +010018
Slava Shklyaeva1812872020-11-11 15:41:18 +000019#include "ExecutionCallback.h"
Michael Butlerfe7b0002019-03-13 18:23:10 -070020
Michael Butler8aafe682017-09-22 18:21:59 -070021#include <android-base/logging.h>
Slava Shklyaev9f29f432020-08-13 13:16:03 +010022
Michael Butlerfe7b0002019-03-13 18:23:10 -070023#include <limits>
Xusong Wang36f3cad2019-10-23 10:35:51 -070024#include <utility>
25#include <vector>
Michael Butler8aafe682017-09-22 18:21:59 -070026
Michael Butler6bf05b22019-07-11 11:45:01 -070027namespace android::nn {
28
Slava Shklyaeva6d95b12020-11-27 17:29:10 +000029void ExecutionCallback::notify(ErrorStatus status, const std::vector<OutputShape>& outputShapes,
30 const Timing& timing) {
31 notifyInternal(status, outputShapes, timing);
Xusong Wangc73a89b2018-11-05 09:59:30 -080032}
33
Michael Butlerfe7b0002019-03-13 18:23:10 -070034void 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
54ErrorStatus ExecutionCallback::getStatus() const {
Michael Butler8aafe682017-09-22 18:21:59 -070055 wait();
56 return mErrorStatus;
57}
58
Michael Butlerfe7b0002019-03-13 18:23:10 -070059const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
Xusong Wang0c0cbb52018-11-06 18:48:20 -080060 wait();
61 return mOutputShapes;
62}
63
Michael Butlerfe7b0002019-03-13 18:23:10 -070064Timing ExecutionCallback::getTiming() const {
David Gross257ee7a2019-01-23 14:59:10 -080065 wait();
66 return mTiming;
67}
68
Michael Butlerfe7b0002019-03-13 18:23:10 -070069bool 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
89void 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 Shklyaeva6d95b12020-11-27 17:29:10 +0000114void ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
115 std::vector<OutputShape> outputShapes, Timing timing) {
Michael Butlere6785e42019-12-12 16:25:03 -0800116 // check results
Slava Shklyaeva6d95b12020-11-27 17:29:10 +0000117 {
Michael Butlerbf258232019-12-16 18:32:45 -0800118 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 Shklyaev9f29f432020-08-13 13:16:03 +0100125 timing = {};
Michael Butlerbf258232019-12-16 18:32:45 -0800126 }
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 Shklyaev9f29f432020-08-13 13:16:03 +0100135 timing = {};
Michael Butlerbf258232019-12-16 18:32:45 -0800136 }
Michael Butlere6785e42019-12-12 16:25:03 -0800137 }
138 }
139
140 // store results
Michael Butlerfe7b0002019-03-13 18:23:10 -0700141 {
142 std::lock_guard<std::mutex> hold(mMutex);
143
144 // quick-return if object has already been notified
145 if (mNotified) {
Slava Shklyaeva6d95b12020-11-27 17:29:10 +0000146 return;
Michael Butlerfe7b0002019-03-13 18:23:10 -0700147 }
148
149 mErrorStatus = errorStatus;
Michael Butlerbf258232019-12-16 18:32:45 -0800150 mOutputShapes = std::move(outputShapes);
Michael Butlerfe7b0002019-03-13 18:23:10 -0700151 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 Butler6bf05b22019-07-11 11:45:01 -0700165} // namespace android::nn