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.3/Callbacks.h"
20 
21 #include <android-base/logging.h>
22 
23 #include <limits>
24 
25 namespace android::hardware::neuralnetworks::V1_3::implementation {
26 
27 using V1_2::OutputShape;
28 using V1_2::Timing;
29 
30 constexpr Timing kNoTiming = {.timeOnDevice = std::numeric_limits<uint64_t>::max(),
31                               .timeInDriver = std::numeric_limits<uint64_t>::max()};
32 
33 // PreparedModelCallback methods begin here
34 
notifyInternal(ErrorStatus errorStatus,const sp<V1_0::IPreparedModel> & preparedModel)35 Return<void> PreparedModelCallback::notifyInternal(ErrorStatus errorStatus,
36                                                    const sp<V1_0::IPreparedModel>& preparedModel) {
37     {
38         std::lock_guard<std::mutex> hold(mMutex);
39 
40         // quick-return if object has already been notified
41         if (mNotified) {
42             return Void();
43         }
44 
45         // store results and mark as notified
46         mErrorStatus = errorStatus;
47         mPreparedModel = preparedModel;
48         mNotified = true;
49     }
50 
51     mCondition.notify_all();
52     return Void();
53 }
54 
notify(V1_0::ErrorStatus errorStatus,const sp<V1_0::IPreparedModel> & preparedModel)55 Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus errorStatus,
56                                            const sp<V1_0::IPreparedModel>& preparedModel) {
57     return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
58 }
59 
notify_1_2(V1_0::ErrorStatus errorStatus,const sp<V1_2::IPreparedModel> & preparedModel)60 Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
61                                                const sp<V1_2::IPreparedModel>& preparedModel) {
62     return notifyInternal(static_cast<ErrorStatus>(errorStatus), preparedModel);
63 }
64 
notify_1_3(V1_3::ErrorStatus errorStatus,const sp<V1_3::IPreparedModel> & preparedModel)65 Return<void> PreparedModelCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
66                                                const sp<V1_3::IPreparedModel>& preparedModel) {
67     return notifyInternal(errorStatus, preparedModel);
68 }
69 
wait() const70 void PreparedModelCallback::wait() const {
71     std::unique_lock<std::mutex> lock(mMutex);
72     mCondition.wait(lock, [this] { return mNotified; });
73 }
74 
getStatus() const75 ErrorStatus PreparedModelCallback::getStatus() const {
76     wait();
77     return mErrorStatus;
78 }
79 
getPreparedModel() const80 sp<V1_0::IPreparedModel> PreparedModelCallback::getPreparedModel() const {
81     wait();
82     return mPreparedModel;
83 }
84 
85 // ExecutionCallback methods begin here
86 
notify(V1_0::ErrorStatus errorStatus)87 Return<void> ExecutionCallback::notify(V1_0::ErrorStatus errorStatus) {
88     return notifyInternal(static_cast<ErrorStatus>(errorStatus), {}, kNoTiming);
89 }
90 
notify_1_2(V1_0::ErrorStatus errorStatus,const hidl_vec<OutputShape> & outputShapes,const Timing & timing)91 Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus errorStatus,
92                                            const hidl_vec<OutputShape>& outputShapes,
93                                            const Timing& timing) {
94     return notifyInternal(static_cast<ErrorStatus>(errorStatus), outputShapes, timing);
95 }
96 
notify_1_3(V1_3::ErrorStatus errorStatus,const hidl_vec<OutputShape> & outputShapes,const Timing & timing)97 Return<void> ExecutionCallback::notify_1_3(V1_3::ErrorStatus errorStatus,
98                                            const hidl_vec<OutputShape>& outputShapes,
99                                            const Timing& timing) {
100     return notifyInternal(errorStatus, outputShapes, timing);
101 }
102 
wait() const103 void ExecutionCallback::wait() const {
104     std::unique_lock<std::mutex> lock(mMutex);
105     mCondition.wait(lock, [this] { return mNotified; });
106 }
107 
getStatus() const108 ErrorStatus ExecutionCallback::getStatus() const {
109     wait();
110     return mErrorStatus;
111 }
112 
getOutputShapes() const113 const std::vector<OutputShape>& ExecutionCallback::getOutputShapes() const {
114     wait();
115     return mOutputShapes;
116 }
117 
getTiming() const118 Timing ExecutionCallback::getTiming() const {
119     wait();
120     return mTiming;
121 }
122 
notifyInternal(ErrorStatus errorStatus,hidl_vec<OutputShape> outputShapes,Timing timing)123 Return<void> ExecutionCallback::notifyInternal(ErrorStatus errorStatus,
124                                                hidl_vec<OutputShape> outputShapes, Timing timing) {
125     // check results
126     if (errorStatus == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
127         // outputShapes must not be empty if OUTPUT_INSUFFICIENT_SIZE.
128         if (outputShapes.size() == 0) {
129             LOG(ERROR) << "Notifid with empty output shape vector when OUTPUT_INSUFFICIENT_SIZE";
130             errorStatus = ErrorStatus::GENERAL_FAILURE;
131             outputShapes = {};
132             timing = kNoTiming;
133         }
134     } else if (errorStatus != ErrorStatus::NONE) {
135         // outputShapes must be empty if errorStatus is neither NONE nor OUTPUT_INSUFFICIENT_SIZE.
136         if (outputShapes.size() != 0) {
137             LOG(ERROR) << "Notified with non-empty output shape vector when error status is "
138                           "neither NONE nor OUTPUT_INSUFFICIENT_SIZE";
139             errorStatus = ErrorStatus::GENERAL_FAILURE;
140             outputShapes = {};
141             timing = kNoTiming;
142         }
143     }
144 
145     // store results
146     {
147         std::lock_guard<std::mutex> hold(mMutex);
148 
149         // quick-return if object has already been notified
150         if (mNotified) {
151             return Void();
152         }
153 
154         mErrorStatus = errorStatus;
155         mOutputShapes = std::move(outputShapes);
156         mTiming = timing;
157         mNotified = true;
158     }
159     mCondition.notify_all();
160     return Void();
161 }
162 
163 }  // namespace android::hardware::neuralnetworks::V1_3::implementation
164