1 /*
2  * Copyright (C) 2019 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 #define LOG_TAG "Callbacks"
18 
19 #include "1.2/Callbacks.h"
20 
21 #include <android-base/logging.h>
22 
23 #include <limits>
24 
25 namespace android::hardware::neuralnetworks::V1_2::implementation {
26 
27 using V1_0::ErrorStatus;
28 
29 constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(),
30                               .timeInDriver = std::numeric_limits<uint64_t>::max()};
31 
32 // PreparedModelCallback methods begin here
33 
notify(ErrorStatus errorStatus,const sp<V1_0::IPreparedModel> & preparedModel)34 Return<void> PreparedModelCallback::notify(ErrorStatus errorStatus,
35                                            const sp<V1_0::IPreparedModel>& preparedModel) {
36     {
37         std::lock_guard<std::mutex> hold(mMutex);
38 
39         // quick-return if object has already been notified
40         if (mNotified) {
41             return Void();
42         }
43 
44         // store results and mark as notified
45         mErrorStatus = errorStatus;
46         mPreparedModel = preparedModel;
47         mNotified = true;
48     }
49 
50     mCondition.notify_all();
51     return Void();
52 }
53 
notify_1_2(ErrorStatus errorStatus,const sp<V1_2::IPreparedModel> & preparedModel)54 Return<void> PreparedModelCallback::notify_1_2(ErrorStatus errorStatus,
55                                                const sp<V1_2::IPreparedModel>& preparedModel) {
56     return notify(errorStatus, preparedModel);
57 }
58 
wait() const59 void PreparedModelCallback::wait() const {
60     std::unique_lock<std::mutex> lock(mMutex);
61     mCondition.wait(lock, [this] { return mNotified; });
62 }
63 
getStatus() const64 ErrorStatus PreparedModelCallback::getStatus() const {
65     wait();
66     return mErrorStatus;
67 }
68 
getPreparedModel() const69 sp<V1_0::IPreparedModel> PreparedModelCallback::getPreparedModel() const {
70     wait();
71     return mPreparedModel;
72 }
73 
74 // ExecutionCallback methods begin here
75 
notify(ErrorStatus errorStatus)76 Return<void> ExecutionCallback::notify(ErrorStatus errorStatus) {
77     notifyInternal(errorStatus, {}, kNoTiming);
78     return Void();
79 }
80 
notify_1_2(ErrorStatus errorStatus,const hidl_vec<OutputShape> & outputShapes,const Timing & timing)81 Return<void> ExecutionCallback::notify_1_2(ErrorStatus errorStatus,
82                                            const hidl_vec<OutputShape>& outputShapes,
83                                            const Timing& timing) {
84     if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
85         // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE.
86         if (outputShapes.size() == 0) {
87             LOG(ERROR) << "Notified with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE";
88             notifyInternal(ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
89             return Void();
90         }
91     } else if (errorStatus != ErrorStatus::NONE) {
92         // outputShapes must be empty if errorStatus is neither NONE nor OUTPUT_INSUFFICIENT_SIZE.
93         if (outputShapes.size() != 0) {
94             LOG(ERROR) << "Notified with non-empty output shape vector when error status is "
95                           "neither NONE nor OUTPUT_INSUFFICIENT_SIZE";
96             notifyInternal(ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
97             return Void();
98         }
99     }
100     notifyInternal(errorStatus, outputShapes, timing);
101     return Void();
102 }
103 
wait() const104 void ExecutionCallback::wait() const {
105     std::unique_lock<std::mutex> lock(mMutex);
106     mCondition.wait(lock, [this] { return mNotified; });
107 }
108 
getStatus() const109 ErrorStatus ExecutionCallback::getStatus() const {
110     wait();
111     return mErrorStatus;
112 }
113 
getOutputShapes() const114 const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
115     wait();
116     return mOutputShapes;
117 }
118 
getTiming() const119 Timing ExecutionCallback::getTiming() const {
120     wait();
121     return mTiming;
122 }
123 
notifyInternal(ErrorStatus errorStatus,const hidl_vec<OutputShape> & outputShapes,const Timing & timing)124 void ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
125                                        const hidl_vec<OutputShape>& outputShapes,
126                                        const Timing& timing) {
127     {
128         std::lock_guard<std::mutex> hold(mMutex);
129 
130         // quick-return if object has already been notified
131         if (mNotified) {
132             return;
133         }
134 
135         mErrorStatus = errorStatus;
136         mOutputShapes = outputShapes;
137         mTiming = timing;
138         mNotified = true;
139     }
140     mCondition.notify_all();
141 }
142 
143 }  // namespace android::hardware::neuralnetworks::V1_2::implementation
144