blob: 8c91232685a41d7b3968398323a02f85d4129283 [file] [log] [blame]
Michael Butler60296322019-01-17 17:54:51 -08001/*
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
Michael Butler89e99ba2019-01-24 02:36:37 -080017#define LOG_TAG "ExecutionBurstServer"
18
Michael Butler60296322019-01-17 17:54:51 -080019#include "ExecutionBurstServer.h"
20
21#include <android-base/logging.h>
Michael Butler3260db92019-04-26 17:51:23 -070022
Michael Butlerc82044a2019-06-24 10:36:20 -070023#include <algorithm>
Michael Butler4ef48f12019-05-02 14:09:17 -070024#include <cstring>
Michael Butlerc932ebb2019-04-11 14:24:06 -070025#include <limits>
Michael Butler3260db92019-04-26 17:51:23 -070026#include <map>
Michael Butlerc82044a2019-06-24 10:36:20 -070027#include <memory>
28#include <tuple>
29#include <utility>
30#include <vector>
Michael Butler3260db92019-04-26 17:51:23 -070031
Michael Butlera8f883c2020-03-12 15:16:38 -070032#include "HalInterfaces.h"
Michael Butler3db6fe52019-01-29 11:20:30 -080033#include "Tracing.h"
Michael Butler60296322019-01-17 17:54:51 -080034
Michael Butler3db6fe52019-01-29 11:20:30 -080035namespace android::nn {
Michael Butler238fe722019-03-21 12:17:27 -070036namespace {
Michael Butler60296322019-01-17 17:54:51 -080037
Michael Butler19af9d22019-07-11 11:45:01 -070038using namespace hal;
39
Michael Butlerc82044a2019-06-24 10:36:20 -070040using hardware::MQDescriptorSync;
Michael Butlera8f883c2020-03-12 15:16:38 -070041using V1_2::FmqRequestDatum;
42using V1_2::FmqResultDatum;
43using V1_2::IBurstCallback;
44using V1_2::IBurstContext;
Michael Butlerc82044a2019-06-24 10:36:20 -070045
Michael Butlerc932ebb2019-04-11 14:24:06 -070046constexpr Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
47 std::numeric_limits<uint64_t>::max()};
48
Michael Butler238fe722019-03-21 12:17:27 -070049// DefaultBurstExecutorWithCache adapts an IPreparedModel so that it can be
50// used as an IBurstExecutorWithCache. Specifically, the cache simply stores the
51// hidl_memory object, and the execution forwards calls to the provided
52// IPreparedModel's "executeSynchronously" method. With this class, hidl_memory
53// must be mapped and unmapped for each execution.
54class DefaultBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
55 public:
Xusong Wang196a1872019-10-25 12:06:20 -070056 DefaultBurstExecutorWithCache(V1_2::IPreparedModel* preparedModel)
57 : mpPreparedModel(preparedModel) {}
Michael Butler60296322019-01-17 17:54:51 -080058
Michael Butler238fe722019-03-21 12:17:27 -070059 bool isCacheEntryPresent(int32_t slot) const override {
Michael Butler3260db92019-04-26 17:51:23 -070060 const auto it = mMemoryCache.find(slot);
Michael Butler1ee58a52019-04-30 13:49:32 -070061 return (it != mMemoryCache.end()) && it->second.valid();
Michael Butler238fe722019-03-21 12:17:27 -070062 }
Michael Butler47c988f62019-03-14 17:34:48 -070063
Michael Butler238fe722019-03-21 12:17:27 -070064 void addCacheEntry(const hidl_memory& memory, int32_t slot) override {
Michael Butler238fe722019-03-21 12:17:27 -070065 mMemoryCache[slot] = memory;
66 }
Michael Butler60296322019-01-17 17:54:51 -080067
Michael Butler3260db92019-04-26 17:51:23 -070068 void removeCacheEntry(int32_t slot) override { mMemoryCache.erase(slot); }
Michael Butler238fe722019-03-21 12:17:27 -070069
Michael Butlerf690d312019-12-12 16:25:03 -080070 std::tuple<V1_0::ErrorStatus, hidl_vec<OutputShape>, Timing> execute(
Xusong Wang7ac6c9d2020-01-08 16:52:37 -080071 const V1_0::Request& request, const std::vector<int32_t>& slots,
Michael Butler238fe722019-03-21 12:17:27 -070072 MeasureTiming measure) override {
73 // convert slots to pools
74 hidl_vec<hidl_memory> pools(slots.size());
Michael Butler3260db92019-04-26 17:51:23 -070075 std::transform(slots.begin(), slots.end(), pools.begin(),
76 [this](int32_t slot) { return mMemoryCache[slot]; });
Michael Butler238fe722019-03-21 12:17:27 -070077
78 // create full request
Xusong Wang7ac6c9d2020-01-08 16:52:37 -080079 V1_0::Request fullRequest = request;
Michael Butler238fe722019-03-21 12:17:27 -070080 fullRequest.pools = std::move(pools);
81
82 // setup execution
Michael Butlerf690d312019-12-12 16:25:03 -080083 V1_0::ErrorStatus returnedStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
Michael Butler238fe722019-03-21 12:17:27 -070084 hidl_vec<OutputShape> returnedOutputShapes;
85 Timing returnedTiming;
86 auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](
Michael Butlerf690d312019-12-12 16:25:03 -080087 V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
Michael Butler238fe722019-03-21 12:17:27 -070088 const Timing& timing) {
89 returnedStatus = status;
90 returnedOutputShapes = outputShapes;
91 returnedTiming = timing;
Michael Butler47c988f62019-03-14 17:34:48 -070092 };
Michael Butler60296322019-01-17 17:54:51 -080093
Michael Butler238fe722019-03-21 12:17:27 -070094 // execute
95 const Return<void> ret = mpPreparedModel->executeSynchronously(fullRequest, measure, cb);
Michael Butlerf690d312019-12-12 16:25:03 -080096 if (!ret.isOk() || returnedStatus != V1_0::ErrorStatus::NONE) {
Michael Butler238fe722019-03-21 12:17:27 -070097 LOG(ERROR) << "IPreparedModelAdapter::execute -- Error executing";
Xusong Wanga66fee12020-01-17 10:36:52 -080098 return {returnedStatus, std::move(returnedOutputShapes), kNoTiming};
Michael Butler89e99ba2019-01-24 02:36:37 -080099 }
Michael Butler60296322019-01-17 17:54:51 -0800100
Michael Butler238fe722019-03-21 12:17:27 -0700101 return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
Michael Butler60296322019-01-17 17:54:51 -0800102 }
103
Michael Butler238fe722019-03-21 12:17:27 -0700104 private:
Xusong Wang196a1872019-10-25 12:06:20 -0700105 V1_2::IPreparedModel* const mpPreparedModel;
Michael Butler3260db92019-04-26 17:51:23 -0700106 std::map<int32_t, hidl_memory> mMemoryCache;
Michael Butler238fe722019-03-21 12:17:27 -0700107};
Michael Butler47c988f62019-03-14 17:34:48 -0700108
Michael Butler238fe722019-03-21 12:17:27 -0700109} // anonymous namespace
Michael Butler60296322019-01-17 17:54:51 -0800110
Michael Butler60296322019-01-17 17:54:51 -0800111// serialize result
Michael Butlerf690d312019-12-12 16:25:03 -0800112std::vector<FmqResultDatum> serialize(V1_0::ErrorStatus errorStatus,
Michael Butlerc932ebb2019-04-11 14:24:06 -0700113 const std::vector<OutputShape>& outputShapes, Timing timing) {
Michael Butler60296322019-01-17 17:54:51 -0800114 // count how many elements need to be sent for a request
115 size_t count = 2 + outputShapes.size();
116 for (const auto& outputShape : outputShapes) {
117 count += outputShape.dimensions.size();
118 }
119
120 // create buffer to temporarily store elements
121 std::vector<FmqResultDatum> data;
122 data.reserve(count);
123
124 // package packetInfo
125 {
126 FmqResultDatum datum;
127 datum.packetInformation({/*.packetSize=*/static_cast<uint32_t>(count),
128 /*.errorStatus=*/errorStatus,
129 /*.numberOfOperands=*/static_cast<uint32_t>(outputShapes.size())});
130 data.push_back(datum);
131 }
132
133 // package output shape data
134 for (const auto& operand : outputShapes) {
135 // package operand information
Steven Moreland393ac6d2019-04-25 15:33:25 -0700136 FmqResultDatum::OperandInformation info{};
137 info.isSufficient = operand.isSufficient;
138 info.numberOfDimensions = static_cast<uint32_t>(operand.dimensions.size());
139
Michael Butler60296322019-01-17 17:54:51 -0800140 FmqResultDatum datum;
Steven Moreland393ac6d2019-04-25 15:33:25 -0700141 datum.operandInformation(info);
Michael Butler60296322019-01-17 17:54:51 -0800142 data.push_back(datum);
143
144 // package operand dimensions
145 for (uint32_t dimension : operand.dimensions) {
146 FmqResultDatum datum;
147 datum.operandDimensionValue(dimension);
148 data.push_back(datum);
149 }
150 }
151
152 // package executionTiming
153 {
154 FmqResultDatum datum;
155 datum.executionTiming(timing);
156 data.push_back(datum);
157 }
158
159 // return result
160 return data;
161}
162
Michael Butlerc932ebb2019-04-11 14:24:06 -0700163// deserialize request
Xusong Wang7ac6c9d2020-01-08 16:52:37 -0800164std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>> deserialize(
Michael Butlerc932ebb2019-04-11 14:24:06 -0700165 const std::vector<FmqRequestDatum>& data) {
166 using discriminator = FmqRequestDatum::hidl_discriminator;
Michael Butler60296322019-01-17 17:54:51 -0800167
Michael Butlerc932ebb2019-04-11 14:24:06 -0700168 size_t index = 0;
169
170 // validate packet information
Michael Butler6b9c50a2022-12-01 17:50:37 -0800171 if (index >= data.size() ||
172 data.at(index).getDiscriminator() != discriminator::packetInformation) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700173 LOG(ERROR) << "FMQ Request packet ill-formed";
174 return std::nullopt;
175 }
176
177 // unpackage packet information
Michael Butler6b9c50a2022-12-01 17:50:37 -0800178 const FmqRequestDatum::PacketInformation& packetInfo = data.at(index).packetInformation();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700179 index++;
180 const uint32_t packetSize = packetInfo.packetSize;
181 const uint32_t numberOfInputOperands = packetInfo.numberOfInputOperands;
182 const uint32_t numberOfOutputOperands = packetInfo.numberOfOutputOperands;
183 const uint32_t numberOfPools = packetInfo.numberOfPools;
184
Michael Butler3260db92019-04-26 17:51:23 -0700185 // verify packet size
186 if (data.size() != packetSize) {
187 LOG(ERROR) << "FMQ Request packet ill-formed";
188 return std::nullopt;
189 }
190
Michael Butlerc932ebb2019-04-11 14:24:06 -0700191 // unpackage input operands
192 std::vector<RequestArgument> inputs;
193 inputs.reserve(numberOfInputOperands);
194 for (size_t operand = 0; operand < numberOfInputOperands; ++operand) {
195 // validate input operand information
Michael Butler6b9c50a2022-12-01 17:50:37 -0800196 if (index >= data.size() ||
197 data.at(index).getDiscriminator() != discriminator::inputOperandInformation) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700198 LOG(ERROR) << "FMQ Request packet ill-formed";
199 return std::nullopt;
Michael Butler60296322019-01-17 17:54:51 -0800200 }
201
Michael Butlerc932ebb2019-04-11 14:24:06 -0700202 // unpackage operand information
203 const FmqRequestDatum::OperandInformation& operandInfo =
Michael Butler6b9c50a2022-12-01 17:50:37 -0800204 data.at(index).inputOperandInformation();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700205 index++;
206 const bool hasNoValue = operandInfo.hasNoValue;
207 const DataLocation location = operandInfo.location;
208 const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
Michael Butler3db6fe52019-01-29 11:20:30 -0800209
Michael Butlerc932ebb2019-04-11 14:24:06 -0700210 // unpackage operand dimensions
211 std::vector<uint32_t> dimensions;
212 dimensions.reserve(numberOfDimensions);
213 for (size_t i = 0; i < numberOfDimensions; ++i) {
214 // validate dimension
Michael Butler6b9c50a2022-12-01 17:50:37 -0800215 if (index >= data.size() ||
216 data.at(index).getDiscriminator() != discriminator::inputOperandDimensionValue) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700217 LOG(ERROR) << "FMQ Request packet ill-formed";
218 return std::nullopt;
219 }
220
221 // unpackage dimension
Michael Butler6b9c50a2022-12-01 17:50:37 -0800222 const uint32_t dimension = data.at(index).inputOperandDimensionValue();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700223 index++;
224
225 // store result
226 dimensions.push_back(dimension);
227 }
228
229 // store result
230 inputs.push_back(
231 {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
232 }
233
234 // unpackage output operands
235 std::vector<RequestArgument> outputs;
236 outputs.reserve(numberOfOutputOperands);
237 for (size_t operand = 0; operand < numberOfOutputOperands; ++operand) {
238 // validate output operand information
Michael Butler6b9c50a2022-12-01 17:50:37 -0800239 if (index >= data.size() ||
240 data.at(index).getDiscriminator() != discriminator::outputOperandInformation) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700241 LOG(ERROR) << "FMQ Request packet ill-formed";
242 return std::nullopt;
243 }
244
245 // unpackage operand information
246 const FmqRequestDatum::OperandInformation& operandInfo =
Michael Butler6b9c50a2022-12-01 17:50:37 -0800247 data.at(index).outputOperandInformation();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700248 index++;
249 const bool hasNoValue = operandInfo.hasNoValue;
250 const DataLocation location = operandInfo.location;
251 const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
252
253 // unpackage operand dimensions
254 std::vector<uint32_t> dimensions;
255 dimensions.reserve(numberOfDimensions);
256 for (size_t i = 0; i < numberOfDimensions; ++i) {
257 // validate dimension
Michael Butler6b9c50a2022-12-01 17:50:37 -0800258 if (index >= data.size() ||
259 data.at(index).getDiscriminator() != discriminator::outputOperandDimensionValue) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700260 LOG(ERROR) << "FMQ Request packet ill-formed";
261 return std::nullopt;
262 }
263
264 // unpackage dimension
Michael Butler6b9c50a2022-12-01 17:50:37 -0800265 const uint32_t dimension = data.at(index).outputOperandDimensionValue();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700266 index++;
267
268 // store result
269 dimensions.push_back(dimension);
270 }
271
272 // store result
273 outputs.push_back(
274 {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
275 }
276
277 // unpackage pools
278 std::vector<int32_t> slots;
279 slots.reserve(numberOfPools);
280 for (size_t pool = 0; pool < numberOfPools; ++pool) {
281 // validate input operand information
Michael Butler6b9c50a2022-12-01 17:50:37 -0800282 if (index >= data.size() ||
283 data.at(index).getDiscriminator() != discriminator::poolIdentifier) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700284 LOG(ERROR) << "FMQ Request packet ill-formed";
285 return std::nullopt;
286 }
287
288 // unpackage operand information
Michael Butler6b9c50a2022-12-01 17:50:37 -0800289 const int32_t poolId = data.at(index).poolIdentifier();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700290 index++;
291
292 // store result
293 slots.push_back(poolId);
294 }
295
296 // validate measureTiming
Michael Butler6b9c50a2022-12-01 17:50:37 -0800297 if (index >= data.size() || data.at(index).getDiscriminator() != discriminator::measureTiming) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700298 LOG(ERROR) << "FMQ Request packet ill-formed";
299 return std::nullopt;
300 }
301
302 // unpackage measureTiming
Michael Butler6b9c50a2022-12-01 17:50:37 -0800303 const MeasureTiming measure = data.at(index).measureTiming();
Michael Butlerc932ebb2019-04-11 14:24:06 -0700304 index++;
305
306 // validate packet information
307 if (index != packetSize) {
Michael Butler6b9c50a2022-12-01 17:50:37 -0800308 LOG(ERROR) << "FMQ Request packet ill-formed";
Michael Butlerc932ebb2019-04-11 14:24:06 -0700309 return std::nullopt;
310 }
311
312 // return request
Xusong Wang7ac6c9d2020-01-08 16:52:37 -0800313 V1_0::Request request = {/*.inputs=*/inputs, /*.outputs=*/outputs, /*.pools=*/{}};
Michael Butlerc932ebb2019-04-11 14:24:06 -0700314 return std::make_tuple(std::move(request), std::move(slots), measure);
315}
316
317// RequestChannelReceiver methods
318
319std::unique_ptr<RequestChannelReceiver> RequestChannelReceiver::create(
Michael Butlerc82044a2019-06-24 10:36:20 -0700320 const FmqRequestDescriptor& requestChannel, std::chrono::microseconds pollingTimeWindow) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700321 std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
322 std::make_unique<FmqRequestChannel>(requestChannel);
Michael Butlerc82044a2019-06-24 10:36:20 -0700323
Michael Butlerc932ebb2019-04-11 14:24:06 -0700324 if (!fmqRequestChannel->isValid()) {
325 LOG(ERROR) << "Unable to create RequestChannelReceiver";
326 return nullptr;
327 }
Michael Butlerc82044a2019-06-24 10:36:20 -0700328 if (fmqRequestChannel->getEventFlagWord() == nullptr) {
329 LOG(ERROR)
330 << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
331 return nullptr;
332 }
333
334 return std::make_unique<RequestChannelReceiver>(std::move(fmqRequestChannel),
335 pollingTimeWindow);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700336}
337
338RequestChannelReceiver::RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
Michael Butlerc82044a2019-06-24 10:36:20 -0700339 std::chrono::microseconds pollingTimeWindow)
340 : mFmqRequestChannel(std::move(fmqRequestChannel)), kPollingTimeWindow(pollingTimeWindow) {}
Michael Butlerc932ebb2019-04-11 14:24:06 -0700341
Xusong Wang7ac6c9d2020-01-08 16:52:37 -0800342std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>>
Michael Butlerc932ebb2019-04-11 14:24:06 -0700343RequestChannelReceiver::getBlocking() {
344 const auto packet = getPacketBlocking();
345 if (!packet) {
346 return std::nullopt;
347 }
348
349 return deserialize(*packet);
350}
351
352void RequestChannelReceiver::invalidate() {
353 mTeardown = true;
354
355 // force unblock
356 // ExecutionBurstServer is by default waiting on a request packet. If the
Michael Butlerc82044a2019-06-24 10:36:20 -0700357 // client process destroys its burst object, the server may still be waiting
358 // on the futex. This force unblock wakes up any thread waiting on the
359 // futex.
360 // TODO: look for a different/better way to signal/notify the futex to wake
361 // up any thread waiting on it
362 FmqRequestDatum datum;
363 datum.packetInformation({/*.packetSize=*/0, /*.numberOfInputOperands=*/0,
364 /*.numberOfOutputOperands=*/0, /*.numberOfPools=*/0});
365 mFmqRequestChannel->writeBlocking(&datum, 1);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700366}
367
368std::optional<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
369 using discriminator = FmqRequestDatum::hidl_discriminator;
370
371 if (mTeardown) {
372 return std::nullopt;
373 }
374
Michael Butlerc82044a2019-06-24 10:36:20 -0700375 // First spend time polling if results are available in FMQ instead of
376 // waiting on the futex. Polling is more responsive (yielding lower
377 // latencies), but can take up more power, so only poll for a limited period
378 // of time.
379
380 auto& getCurrentTime = std::chrono::high_resolution_clock::now;
381 const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
382
383 while (getCurrentTime() < timeToStopPolling) {
384 // if class is being torn down, immediately return
385 if (mTeardown.load(std::memory_order_relaxed)) {
386 return std::nullopt;
387 }
388
389 // Check if data is available. If it is, immediately retrieve it and
390 // return.
391 const size_t available = mFmqRequestChannel->availableToRead();
392 if (available > 0) {
393 // This is the first point when we know an execution is occurring,
394 // so begin to collect systraces. Note that a similar systrace does
395 // not exist at the corresponding point in
396 // ResultChannelReceiver::getPacketBlocking because the execution is
397 // already in flight.
398 NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
399 "ExecutionBurstServer getting packet");
400 std::vector<FmqRequestDatum> packet(available);
401 const bool success = mFmqRequestChannel->read(packet.data(), available);
402 if (!success) {
403 LOG(ERROR) << "Error receiving packet";
404 return std::nullopt;
405 }
406 return std::make_optional(std::move(packet));
Michael Butlerc932ebb2019-04-11 14:24:06 -0700407 }
408 }
409
Michael Butlerc82044a2019-06-24 10:36:20 -0700410 // If we get to this point, we either stopped polling because it was taking
411 // too long or polling was not allowed. Instead, perform a blocking call
412 // which uses a futex to save power.
413
414 // wait for request packet and read first element of request packet
415 FmqRequestDatum datum;
416 bool success = mFmqRequestChannel->readBlocking(&datum, 1);
417
418 // This is the first point when we know an execution is occurring, so begin
419 // to collect systraces. Note that a similar systrace does not exist at the
420 // corresponding point in ResultChannelReceiver::getPacketBlocking because
421 // the execution is already in flight.
Michael Butlerc932ebb2019-04-11 14:24:06 -0700422 NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstServer getting packet");
423
Michael Butlerc932ebb2019-04-11 14:24:06 -0700424 // retrieve remaining elements
425 // NOTE: all of the data is already available at this point, so there's no
426 // need to do a blocking wait to wait for more data. This is known because
427 // in FMQ, all writes are published (made available) atomically. Currently,
428 // the producer always publishes the entire packet in one function call, so
429 // if the first element of the packet is available, the remaining elements
430 // are also available.
Michael Butler3260db92019-04-26 17:51:23 -0700431 const size_t count = mFmqRequestChannel->availableToRead();
432 std::vector<FmqRequestDatum> packet(count + 1);
Michael Butler4ef48f12019-05-02 14:09:17 -0700433 std::memcpy(&packet.front(), &datum, sizeof(datum));
Michael Butler3260db92019-04-26 17:51:23 -0700434 success &= mFmqRequestChannel->read(packet.data() + 1, count);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700435
Michael Butler3260db92019-04-26 17:51:23 -0700436 // terminate loop
437 if (mTeardown) {
438 return std::nullopt;
439 }
440
441 // ensure packet was successfully received
Michael Butlerc932ebb2019-04-11 14:24:06 -0700442 if (!success) {
Michael Butler3260db92019-04-26 17:51:23 -0700443 LOG(ERROR) << "Error receiving packet";
444 return std::nullopt;
Michael Butlerc932ebb2019-04-11 14:24:06 -0700445 }
446
Michael Butler4ef48f12019-05-02 14:09:17 -0700447 return std::make_optional(std::move(packet));
Michael Butlerc932ebb2019-04-11 14:24:06 -0700448}
449
450// ResultChannelSender methods
451
452std::unique_ptr<ResultChannelSender> ResultChannelSender::create(
453 const FmqResultDescriptor& resultChannel) {
454 std::unique_ptr<FmqResultChannel> fmqResultChannel =
455 std::make_unique<FmqResultChannel>(resultChannel);
Michael Butlerc82044a2019-06-24 10:36:20 -0700456
Michael Butlerc932ebb2019-04-11 14:24:06 -0700457 if (!fmqResultChannel->isValid()) {
458 LOG(ERROR) << "Unable to create RequestChannelSender";
459 return nullptr;
460 }
Michael Butlerc82044a2019-06-24 10:36:20 -0700461 if (fmqResultChannel->getEventFlagWord() == nullptr) {
462 LOG(ERROR) << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
463 return nullptr;
464 }
465
466 return std::make_unique<ResultChannelSender>(std::move(fmqResultChannel));
Michael Butlerc932ebb2019-04-11 14:24:06 -0700467}
468
Michael Butlerc82044a2019-06-24 10:36:20 -0700469ResultChannelSender::ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel)
470 : mFmqResultChannel(std::move(fmqResultChannel)) {}
Michael Butlerc932ebb2019-04-11 14:24:06 -0700471
Michael Butlerf690d312019-12-12 16:25:03 -0800472bool ResultChannelSender::send(V1_0::ErrorStatus errorStatus,
Michael Butlerc932ebb2019-04-11 14:24:06 -0700473 const std::vector<OutputShape>& outputShapes, Timing timing) {
474 const std::vector<FmqResultDatum> serialized = serialize(errorStatus, outputShapes, timing);
475 return sendPacket(serialized);
476}
477
478bool ResultChannelSender::sendPacket(const std::vector<FmqResultDatum>& packet) {
Michael Butler3260db92019-04-26 17:51:23 -0700479 if (packet.size() > mFmqResultChannel->availableToWrite()) {
480 LOG(ERROR)
481 << "ResultChannelSender::sendPacket -- packet size exceeds size available in FMQ";
482 const std::vector<FmqResultDatum> errorPacket =
Michael Butlerf690d312019-12-12 16:25:03 -0800483 serialize(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
Michael Butlerc82044a2019-06-24 10:36:20 -0700484
485 // Always send the packet with "blocking" because this signals the futex
486 // and unblocks the consumer if it is waiting on the futex.
487 return mFmqResultChannel->writeBlocking(errorPacket.data(), errorPacket.size());
Michael Butler3260db92019-04-26 17:51:23 -0700488 }
489
Michael Butlerc82044a2019-06-24 10:36:20 -0700490 // Always send the packet with "blocking" because this signals the futex and
491 // unblocks the consumer if it is waiting on the futex.
492 return mFmqResultChannel->writeBlocking(packet.data(), packet.size());
Michael Butlerc932ebb2019-04-11 14:24:06 -0700493}
494
495// ExecutionBurstServer methods
496
497sp<ExecutionBurstServer> ExecutionBurstServer::create(
498 const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
499 const MQDescriptorSync<FmqResultDatum>& resultChannel,
Michael Butlerc82044a2019-06-24 10:36:20 -0700500 std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
501 std::chrono::microseconds pollingTimeWindow) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700502 // check inputs
503 if (callback == nullptr || executorWithCache == nullptr) {
504 LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
505 return nullptr;
506 }
507
508 // create FMQ objects
509 std::unique_ptr<RequestChannelReceiver> requestChannelReceiver =
Michael Butlerc82044a2019-06-24 10:36:20 -0700510 RequestChannelReceiver::create(requestChannel, pollingTimeWindow);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700511 std::unique_ptr<ResultChannelSender> resultChannelSender =
512 ResultChannelSender::create(resultChannel);
513
514 // check FMQ objects
515 if (!requestChannelReceiver || !resultChannelSender) {
516 LOG(ERROR) << "ExecutionBurstServer::create failed to create FastMessageQueue";
517 return nullptr;
518 }
519
520 // make and return context
521 return new ExecutionBurstServer(callback, std::move(requestChannelReceiver),
522 std::move(resultChannelSender), std::move(executorWithCache));
523}
524
525sp<ExecutionBurstServer> ExecutionBurstServer::create(
526 const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
Xusong Wang196a1872019-10-25 12:06:20 -0700527 const MQDescriptorSync<FmqResultDatum>& resultChannel, V1_2::IPreparedModel* preparedModel,
Michael Butlerc82044a2019-06-24 10:36:20 -0700528 std::chrono::microseconds pollingTimeWindow) {
Michael Butlerc932ebb2019-04-11 14:24:06 -0700529 // check relevant input
530 if (preparedModel == nullptr) {
531 LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
532 return nullptr;
533 }
534
535 // adapt IPreparedModel to have caching
536 const std::shared_ptr<DefaultBurstExecutorWithCache> preparedModelAdapter =
537 std::make_shared<DefaultBurstExecutorWithCache>(preparedModel);
538
539 // make and return context
540 return ExecutionBurstServer::create(callback, requestChannel, resultChannel,
Michael Butlerc82044a2019-06-24 10:36:20 -0700541 preparedModelAdapter, pollingTimeWindow);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700542}
543
544ExecutionBurstServer::ExecutionBurstServer(
545 const sp<IBurstCallback>& callback, std::unique_ptr<RequestChannelReceiver> requestChannel,
546 std::unique_ptr<ResultChannelSender> resultChannel,
547 std::shared_ptr<IBurstExecutorWithCache> executorWithCache)
548 : mCallback(callback),
549 mRequestChannelReceiver(std::move(requestChannel)),
550 mResultChannelSender(std::move(resultChannel)),
551 mExecutorWithCache(std::move(executorWithCache)) {
552 // TODO: highly document the threading behavior of this class
553 mWorker = std::thread([this] { task(); });
554}
555
556ExecutionBurstServer::~ExecutionBurstServer() {
557 // set teardown flag
558 mTeardown = true;
559 mRequestChannelReceiver->invalidate();
560
561 // wait for task thread to end
562 mWorker.join();
563}
564
565Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
Michael Butlerba59a542019-06-28 17:06:27 -0700566 std::lock_guard<std::mutex> hold(mMutex);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700567 mExecutorWithCache->removeCacheEntry(slot);
568 return Void();
569}
570
571void ExecutionBurstServer::ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots) {
572 const auto slotIsKnown = [this](int32_t slot) {
573 return mExecutorWithCache->isCacheEntryPresent(slot);
574 };
575
576 // find unique unknown slots
577 std::vector<int32_t> unknownSlots = slots;
578 auto unknownSlotsEnd = unknownSlots.end();
579 std::sort(unknownSlots.begin(), unknownSlotsEnd);
580 unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlotsEnd);
581 unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
582 unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
583
584 // quick-exit if all slots are known
585 if (unknownSlots.empty()) {
586 return;
587 }
588
Michael Butlerf690d312019-12-12 16:25:03 -0800589 V1_0::ErrorStatus errorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
Michael Butlerc932ebb2019-04-11 14:24:06 -0700590 std::vector<hidl_memory> returnedMemories;
Michael Butlerf690d312019-12-12 16:25:03 -0800591 auto cb = [&errorStatus, &returnedMemories](V1_0::ErrorStatus status,
Michael Butlerc932ebb2019-04-11 14:24:06 -0700592 const hidl_vec<hidl_memory>& memories) {
593 errorStatus = status;
594 returnedMemories = memories;
595 };
596
597 const Return<void> ret = mCallback->getMemories(unknownSlots, cb);
598
Michael Butlerf690d312019-12-12 16:25:03 -0800599 if (!ret.isOk() || errorStatus != V1_0::ErrorStatus::NONE ||
Michael Butlerc932ebb2019-04-11 14:24:06 -0700600 returnedMemories.size() != unknownSlots.size()) {
601 LOG(ERROR) << "Error retrieving memories";
602 return;
603 }
604
605 // add memories to unknown slots
606 for (size_t i = 0; i < unknownSlots.size(); ++i) {
607 mExecutorWithCache->addCacheEntry(returnedMemories[i], unknownSlots[i]);
608 }
609}
610
611void ExecutionBurstServer::task() {
612 // loop until the burst object is being destroyed
613 while (!mTeardown) {
614 // receive request
615 auto arguments = mRequestChannelReceiver->getBlocking();
616
617 // if the request packet was not properly received, return a generic
618 // error and skip the execution
619 //
620 // if the burst is being torn down, skip the execution exection so the
621 // "task" function can end
622 if (!arguments) {
623 if (!mTeardown) {
Michael Butlerf690d312019-12-12 16:25:03 -0800624 mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
Michael Butlerc932ebb2019-04-11 14:24:06 -0700625 }
626 continue;
627 }
628
629 // otherwise begin tracing execution
630 NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
631 "ExecutionBurstServer getting memory, executing, and returning results");
632
633 // unpack the arguments; types are Request, std::vector<int32_t>, and
Michael Butler238fe722019-03-21 12:17:27 -0700634 // MeasureTiming, respectively
Michael Butlerc932ebb2019-04-11 14:24:06 -0700635 const auto [requestWithoutPools, slotsOfPools, measure] = std::move(*arguments);
Michael Butler60296322019-01-17 17:54:51 -0800636
Michael Butler238fe722019-03-21 12:17:27 -0700637 // ensure executor with cache has required memory
638 std::lock_guard<std::mutex> hold(mMutex);
639 ensureCacheEntriesArePresentLocked(slotsOfPools);
640
641 // perform computation; types are ErrorStatus, hidl_vec<OutputShape>,
642 // and Timing, respectively
643 const auto [errorStatus, outputShapes, returnedTiming] =
644 mExecutorWithCache->execute(requestWithoutPools, slotsOfPools, measure);
Michael Butler60296322019-01-17 17:54:51 -0800645
646 // return result
Michael Butlerc932ebb2019-04-11 14:24:06 -0700647 mResultChannelSender->send(errorStatus, outputShapes, returnedTiming);
Michael Butler60296322019-01-17 17:54:51 -0800648 }
649}
650
Michael Butler3db6fe52019-01-29 11:20:30 -0800651} // namespace android::nn