Home | History | Annotate | Download | only in runtime
      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