/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "SampleDriver" #include "SampleDriver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AidlBufferTracker.h" #include "AidlHalUtils.h" #include "CpuExecutor.h" #include "SampleDriverUtils.h" #include "Tracing.h" #include "Utils.h" namespace android { namespace nn { namespace sample_driver { namespace { int64_t nanosecondsDuration(TimePoint end, TimePoint start) { return std::chrono::duration_cast(end - start).count(); }; constexpr aidl_hal::Timing kNoTiming = {.timeOnDeviceNs = -1, .timeInDriverNs = -1}; } // namespace ndk::ScopedAStatus SampleDriver::getVersionString(std::string* versionString) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getVersionString"); *versionString = "JUST_AN_EXAMPLE"; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SampleDriver::getType(aidl_hal::DeviceType* deviceType) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getType"); *deviceType = aidl_hal::DeviceType::CPU; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SampleDriver::getSupportedExtensions( std::vector* supportedExtensions) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getSupportedExtensions"); *supportedExtensions = {/* No extensions. */}; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SampleDriver::getNumberOfCacheFilesNeeded( aidl_hal::NumberOfCacheFiles* numberOfCacheFiles) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INITIALIZATION, "SampleDriver::getNumberOfCacheFilesNeeded"); // Set both numbers to be 0 for cache not supported. numberOfCacheFiles->numDataCache = 0; numberOfCacheFiles->numModelCache = 0; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SampleDriver::prepareModel( const aidl_hal::Model& model, aidl_hal::ExecutionPreference preference, aidl_hal::Priority priority, int64_t deadlineNs, const std::vector& /*modelCache*/, const std::vector& /*dataCache*/, const std::vector& /*token*/, const std::shared_ptr& callback) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION, "SampleDriver::prepareModel"); auto copiedModel = aidl_hal::utils::clone(model); if (!copiedModel.has_value()) { return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, copiedModel.error().message); } return prepareModelBase(std::move(copiedModel).value(), this, preference, priority, deadlineNs, callback); } ndk::ScopedAStatus SampleDriver::prepareModelFromCache( int64_t /*deadlineNs*/, const std::vector& /*modelCache*/, const std::vector& /*dataCache*/, const std::vector& /*token*/, const std::shared_ptr& callback) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_COMPILATION, "SampleDriver::prepareModelFromCache"); notify(callback, aidl_hal::ErrorStatus::GENERAL_FAILURE, nullptr); return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "Caching is not supported in the sample driver."); } // Safely downcast an IPreparedModel object to SamplePreparedModel. // This function will return nullptr if the IPreparedModel object is not originated from the sample // driver process. static const SamplePreparedModel* castToSamplePreparedModel( const std::shared_ptr& preparedModel) { if (preparedModel->isRemote()) { return nullptr; } else { // This static_cast is safe because SamplePreparedModel is the only class that implements // the IPreparedModel interface in the sample driver process. return static_cast(preparedModel.get()); } } ndk::ScopedAStatus SampleDriver::allocate( const aidl_hal::BufferDesc& desc, const std::vector& halPreparedModels, const std::vector& inputRoles, const std::vector& outputRoles, aidl_hal::DeviceBuffer* buffer) { VLOG(DRIVER) << "SampleDriver::allocate"; constexpr auto getModel = [](const std::shared_ptr& preparedModel) -> const aidl_hal::Model* { const auto* samplePreparedModel = castToSamplePreparedModel(preparedModel); if (samplePreparedModel == nullptr) { LOG(ERROR) << "SampleDriver::allocate -- unknown remote IPreparedModel."; return nullptr; } return samplePreparedModel->getModel(); }; std::vector> preparedModels; preparedModels.reserve(halPreparedModels.size()); for (const auto& halPreparedModelParcel : halPreparedModels) { preparedModels.push_back(halPreparedModelParcel.preparedModel); } std::set roles; aidl_hal::Operand operand; if (!validateMemoryDesc(desc, preparedModels, inputRoles, outputRoles, getModel, &roles, &operand)) { LOG(ERROR) << "SampleDriver::allocate -- validation failed."; return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "SampleDriver::allocate -- validation failed."); } if (isExtensionOperandType(operand.type)) { LOG(ERROR) << "SampleDriver::allocate -- does not support extension type."; return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "SampleDriver::allocate -- does not support extension type."); } // TODO(xusongw): Support allocating buffers with unknown dimensions or rank. // An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical // types. uint32_t size = nonExtensionOperandSizeOfData(convert(operand.type).value(), toUnsigned(operand.dimensions).value()); VLOG(DRIVER) << "SampleDriver::allocate -- type = " << toString(operand.type) << ", dimensions = " << toString(operand.dimensions) << ", size = " << size; if (size == 0) { LOG(ERROR) << "SampleDriver::allocate -- does not support dynamic output shape."; return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "SampleDriver::allocate -- does not support dynamic output shape."); } // An operand obtained from validateMemoryDesc is guaranteed to be representable in canonical // types, so it safe to do an unvalidated conversion here. auto bufferWrapper = AidlManagedBuffer::create(size, std::move(roles), unvalidatedConvert(operand).value()); if (bufferWrapper == nullptr) { LOG(ERROR) << "SampleDriver::allocate -- not enough memory."; return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "SampleDriver::allocate -- not enough memory."); } auto token = mBufferTracker->add(bufferWrapper); if (token == nullptr) { LOG(ERROR) << "SampleDriver::allocate -- AidlBufferTracker returned invalid token."; return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "SampleDriver::allocate -- AidlBufferTracker returned invalid token."); } const uint32_t tokenValue = token->get(); std::shared_ptr sampleBuffer = ndk::SharedRefBase::make(std::move(bufferWrapper), std::move(token)); VLOG(DRIVER) << "SampleDriver::allocate -- successfully allocates the requested memory"; buffer->buffer = std::move(sampleBuffer); buffer->token = tokenValue; return ndk::ScopedAStatus::ok(); } int SampleDriver::run() { ABinderProcess_setThreadPoolMaxThreadCount(4); const std::string name = std::string(SampleDriver::descriptor) + "/" + mName; const binder_status_t status = AServiceManager_addService(this->asBinder().get(), name.c_str()); if (status != STATUS_OK) { return 1; } ABinderProcess_joinThreadPool(); return 1; } static void copyRunTimePoolInfos(const RunTimePoolInfo& srcPool, const RunTimePoolInfo& dstPool) { CHECK(srcPool.getBuffer() != nullptr); CHECK(dstPool.getBuffer() != nullptr); CHECK(srcPool.getSize() == dstPool.getSize()); std::copy(srcPool.getBuffer(), srcPool.getBuffer() + srcPool.getSize(), dstPool.getBuffer()); dstPool.flush(); } ndk::ScopedAStatus SampleBuffer::copyTo(const aidl_hal::Memory& dst) { const auto canonicalMemory = convert(dst); if (!canonicalMemory.has_value()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, canonicalMemory.error().message); } const auto dstPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value()); if (!dstPool.has_value()) { LOG(ERROR) << "SampleBuffer::copyTo -- unable to map dst memory."; return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "SampleBuffer::copyTo -- unable to map dst memory."); } const auto validationStatus = aidl_hal::utils::convert(kBuffer->validateCopyTo(dstPool->getSize())).value(); if (validationStatus != aidl_hal::ErrorStatus::NONE) { return toAStatus(validationStatus); } const auto srcPool = kBuffer->createRunTimePoolInfo(); copyRunTimePoolInfos(srcPool, dstPool.value()); return ndk::ScopedAStatus::ok(); } static aidl_hal::ErrorStatus copyFromInternal( const aidl_hal::Memory& src, const std::vector& dimensions, const std::shared_ptr& bufferWrapper) { CHECK(bufferWrapper != nullptr); const auto canonicalMemory = convert(src); if (!canonicalMemory.has_value()) { return aidl_hal::ErrorStatus::INVALID_ARGUMENT; } const auto srcPool = RunTimePoolInfo::createFromMemory(canonicalMemory.value()); if (!srcPool.has_value()) { LOG(ERROR) << "SampleBuffer::copyFrom -- unable to map src memory."; return aidl_hal::ErrorStatus::GENERAL_FAILURE; } const auto validationStatus = aidl_hal::utils::convert(bufferWrapper->validateCopyFrom( dimensions, srcPool->getSize())) .value(); if (validationStatus != aidl_hal::ErrorStatus::NONE) { return validationStatus; } const auto dstPool = bufferWrapper->createRunTimePoolInfo(); copyRunTimePoolInfos(srcPool.value(), dstPool); return aidl_hal::ErrorStatus::NONE; } ndk::ScopedAStatus SampleBuffer::copyFrom(const aidl_hal::Memory& src, const std::vector& dimensions) { const auto unsignedDimensions = toUnsigned(dimensions); if (!unsignedDimensions.has_value()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, unsignedDimensions.error().message); } const auto status = copyFromInternal(src, unsignedDimensions.value(), kBuffer); if (status != aidl_hal::ErrorStatus::NONE) { kBuffer->setInitialized(false); return toAStatus(status); } kBuffer->updateDimensions(unsignedDimensions.value()); kBuffer->setInitialized(true); return ndk::ScopedAStatus::ok(); } bool SamplePreparedModel::initialize() { const auto canonicalPools = convert(mModel.pools); if (!canonicalPools.has_value()) { return false; } return setRunTimePoolInfosFromCanonicalMemories(&mPoolInfos, canonicalPools.value()); } static std::tuple, std::vector>> createRunTimePoolInfos(const Request& request, const SampleDriver& driver, const SamplePreparedModel* preparedModel) { std::vector requestPoolInfos; std::vector> bufferWrappers; requestPoolInfos.reserve(request.pools.size()); bufferWrappers.reserve(request.pools.size()); for (uint32_t i = 0; i < request.pools.size(); i++) { const auto& pool = request.pools[i]; if (const auto* memory = std::get_if(&pool)) { auto buffer = RunTimePoolInfo::createFromMemory(*memory); if (!buffer.has_value()) { LOG(ERROR) << "createRuntimeMemoriesFromMemoryPools -- could not map pools"; return {aidl_hal::ErrorStatus::GENERAL_FAILURE, {}, {}}; } requestPoolInfos.push_back(std::move(*buffer)); bufferWrappers.push_back(nullptr); } else if (const auto* token = std::get_if(&pool)) { auto bufferWrapper = driver.getBufferTracker()->get(static_cast(*token)); if (bufferWrapper == nullptr) { return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}}; } const auto validationStatus = aidl_hal::utils::convert( bufferWrapper->validateRequest(i, request, preparedModel)) .value(); if (validationStatus != aidl_hal::ErrorStatus::NONE) { return {validationStatus, {}, {}}; } requestPoolInfos.push_back(bufferWrapper->createRunTimePoolInfo()); bufferWrappers.push_back(std::move(bufferWrapper)); } else { // If the pool is not a Memory or a token, the input is invalid. return {aidl_hal::ErrorStatus::INVALID_ARGUMENT, {}, {}}; } } return {aidl_hal::ErrorStatus::NONE, std::move(requestPoolInfos), std::move(bufferWrappers)}; } static aidl_hal::ErrorStatus updateDeviceMemories( aidl_hal::ErrorStatus status, const Request& request, const std::vector>& bufferWrappers, const std::vector& outputShapes) { if (status == aidl_hal::ErrorStatus::NONE) { for (uint32_t i = 0; i < request.outputs.size(); i++) { const uint32_t poolIndex = request.outputs[i].location.poolIndex; const auto& pool = request.pools[poolIndex]; if (std::holds_alternative(pool)) { const auto unsignedDimensions = toUnsigned(outputShapes[i].dimensions).value(); if (!bufferWrappers[poolIndex]->updateDimensions(unsignedDimensions)) { return aidl_hal::ErrorStatus::GENERAL_FAILURE; } } } for (uint32_t i = 0; i < request.outputs.size(); i++) { const uint32_t poolIndex = request.outputs[i].location.poolIndex; const auto& pool = request.pools[poolIndex]; if (std::holds_alternative(pool)) { bufferWrappers[poolIndex]->setInitialized(true); } } } else if (status == aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) { // If CpuExecutor reports OUTPUT_INSUFFCIENT_SIZE on a device memory, this is because the // dimensions of the device memory are incorrectly specified. The driver should return // GENERAL_FAILURE instead in this case. for (uint32_t i = 0; i < request.outputs.size(); i++) { const uint32_t poolIndex = request.outputs[i].location.poolIndex; const auto& pool = request.pools[poolIndex]; if (std::holds_alternative(pool)) { if (!outputShapes[i].isSufficient) { LOG(ERROR) << "Invalid dimensions for output " << i << ": actual shape = " << toString(outputShapes[i].dimensions); return aidl_hal::ErrorStatus::GENERAL_FAILURE; } } } } return aidl_hal::ErrorStatus::NONE; } ndk::ScopedAStatus SamplePreparedModel::executeSynchronously( const aidl_hal::Request& halRequest, bool measureTiming, int64_t halDeadlineNs, int64_t loopTimeoutDurationNs, aidl_hal::ExecutionResult* executionResult) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION, "SampleDriver::executeSynchronously"); VLOG(DRIVER) << "executeSynchronously(" << SHOW_IF_DEBUG(halRequest.toString()) << ")"; TimePoint driverStart, driverEnd, deviceStart, deviceEnd; if (measureTiming) driverStart = Clock::now(); const auto model = convert(mModel).value(); auto maybeRequest = convert(halRequest); if (!maybeRequest.has_value()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message); } const auto request = std::move(maybeRequest).value(); const auto validationResult = validateRequestForModel(request, model); if (!validationResult.ok()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error()); } if (halDeadlineNs < -1) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid deadline: " + toString(halDeadlineNs)); } if (loopTimeoutDurationNs < -1) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid loop timeout duration: " + toString(loopTimeoutDurationNs)); } const auto deadline = makeDeadline(halDeadlineNs); if (hasDeadlinePassed(deadline)) { return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT); } NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS, "SampleDriver::executeSynchronouslyBase"); const auto [poolStatus, requestPoolInfos, bufferWrappers] = createRunTimePoolInfos(request, *mDriver, this); if (poolStatus != aidl_hal::ErrorStatus::NONE) { return toAStatus(poolStatus); } NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION, "SampleDriver::executeSynchronouslyBase"); CpuExecutor executor = mDriver->getExecutor(); if (loopTimeoutDurationNs >= 0) { executor.setLoopTimeout(loopTimeoutDurationNs); } if (deadline.has_value()) { executor.setDeadline(*deadline); } if (measureTiming) deviceStart = Clock::now(); int n = executor.run(model, request, mPoolInfos, requestPoolInfos); if (measureTiming) deviceEnd = Clock::now(); VLOG(DRIVER) << "executor.run returned " << n; aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n); if (executionStatus != aidl_hal::ErrorStatus::NONE && executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) { return toAStatus(executionStatus); } auto outputShapes = aidl_hal::utils::convert(executor.getOutputShapes()).value(); // Update device memory metadata. const aidl_hal::ErrorStatus updateStatus = updateDeviceMemories(executionStatus, request, bufferWrappers, outputShapes); if (updateStatus != aidl_hal::ErrorStatus::NONE) { return toAStatus(updateStatus); } executionResult->outputSufficientSize = executionStatus != aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE; executionResult->outputShapes = std::move(outputShapes); executionResult->timing = kNoTiming; if (measureTiming && executionStatus == aidl_hal::ErrorStatus::NONE) { driverEnd = Clock::now(); aidl_hal::Timing timing = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart), .timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)}; VLOG(DRIVER) << "executeSynchronously timing = " << timing.toString(); executionResult->timing = timing; } return ndk::ScopedAStatus::ok(); } // The sample driver will finish the execution and then return. ndk::ScopedAStatus SamplePreparedModel::executeFenced( const aidl_hal::Request& halRequest, const std::vector& waitFor, bool measureTiming, int64_t halDeadlineNs, int64_t loopTimeoutDurationNs, int64_t durationNs, aidl_hal::FencedExecutionResult* executionResult) { NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION, "SamplePreparedModel::executeFenced"); VLOG(DRIVER) << "executeFenced(" << SHOW_IF_DEBUG(halRequest.toString()) << ")"; TimePoint driverStart, driverEnd, deviceStart, deviceEnd; if (measureTiming) driverStart = Clock::now(); const auto model = convert(mModel).value(); auto maybeRequest = convert(halRequest); if (!maybeRequest.has_value()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, maybeRequest.error().message); } const auto request = std::move(maybeRequest).value(); const auto validationResult = validateRequestForModel(request, model, /*allowUnspecifiedOutput=*/false); if (!validationResult.ok()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, validationResult.error()); } if (halDeadlineNs < -1) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid deadline: " + toString(halDeadlineNs)); } if (loopTimeoutDurationNs < -1) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid loop timeout duration: " + toString(loopTimeoutDurationNs)); } if (durationNs < -1) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid fenced execution duration: " + toString(durationNs)); } const auto deadline = makeDeadline(halDeadlineNs); if (hasDeadlinePassed(deadline)) { return toAStatus(aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT); } // Wait for the dependent events to signal for (const auto& fenceHandle : waitFor) { int syncFenceFd = fenceHandle.get(); if (syncWait(syncFenceFd, -1) != FenceState::SIGNALED) { LOG(ERROR) << "syncWait failed"; return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "syncWait failed"); } } // Update deadline if the timeout duration is closer than the deadline. auto closestDeadline = deadline; if (durationNs >= 0) { const auto timeoutDurationDeadline = makeDeadline(durationNs); if (!closestDeadline.has_value() || *closestDeadline > timeoutDurationDeadline) { closestDeadline = timeoutDurationDeadline; } } TimePoint driverStartAfterFence; if (measureTiming) driverStartAfterFence = Clock::now(); NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_INPUTS_AND_OUTPUTS, "SamplePreparedModel::executeFenced"); const auto [poolStatus, requestPoolInfos, bufferWrappers] = createRunTimePoolInfos(request, *mDriver, this); if (poolStatus != aidl_hal::ErrorStatus::NONE) { return toAStatus(poolStatus); } NNTRACE_FULL_SWITCH(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION, "SamplePreparedModel::executeFenced"); CpuExecutor executor = mDriver->getExecutor(); if (loopTimeoutDurationNs >= 0) { executor.setLoopTimeout(loopTimeoutDurationNs); } if (closestDeadline.has_value()) { executor.setDeadline(*closestDeadline); } if (measureTiming) deviceStart = Clock::now(); int n = executor.run(model, request, mPoolInfos, requestPoolInfos); if (measureTiming) deviceEnd = Clock::now(); VLOG(DRIVER) << "executor.run returned " << n; aidl_hal::ErrorStatus executionStatus = convertResultCodeToAidlErrorStatus(n); if (executionStatus != aidl_hal::ErrorStatus::NONE) { return toAStatus(executionStatus); } // Set output memories to the initialized state. if (executionStatus == aidl_hal::ErrorStatus::NONE) { for (const auto& output : request.outputs) { const uint32_t poolIndex = output.location.poolIndex; const auto& pool = request.pools[poolIndex]; if (std::holds_alternative(pool)) { bufferWrappers[poolIndex]->setInitialized(true); } } } aidl_hal::Timing timingSinceLaunch = kNoTiming; aidl_hal::Timing timingAfterFence = kNoTiming; if (measureTiming) { driverEnd = Clock::now(); timingSinceLaunch = {.timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart), .timeInDriverNs = nanosecondsDuration(driverEnd, driverStart)}; timingAfterFence = { .timeOnDeviceNs = nanosecondsDuration(deviceEnd, deviceStart), .timeInDriverNs = nanosecondsDuration(driverEnd, driverStartAfterFence)}; VLOG(DRIVER) << "executeFenced timingSinceLaunch = " << timingSinceLaunch.toString(); VLOG(DRIVER) << "executeFenced timingAfterFence = " << timingAfterFence.toString(); } executionResult->callback = ndk::SharedRefBase::make( timingSinceLaunch, timingAfterFence, executionStatus); executionResult->syncFence = ndk::ScopedFileDescriptor(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SamplePreparedModel::configureExecutionBurst( std::shared_ptr* burst) { std::shared_ptr self = this->template ref(); *burst = ndk::SharedRefBase::make(std::move(self)); return ndk::ScopedAStatus::ok(); } SampleBurst::SampleBurst(std::shared_ptr preparedModel) : kPreparedModel(std::move(preparedModel)) { CHECK(kPreparedModel != nullptr); } ndk::ScopedAStatus SampleBurst::executeSynchronously( const aidl_hal::Request& request, const std::vector& memoryIdentifierTokens, bool measureTiming, int64_t deadlineNs, int64_t loopTimeoutDurationNs, aidl_hal::ExecutionResult* executionResult) { if (request.pools.size() != memoryIdentifierTokens.size()) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "request.pools.size() != memoryIdentifierTokens.size()"); } if (!std::all_of(memoryIdentifierTokens.begin(), memoryIdentifierTokens.end(), [](int64_t token) { return token >= -1; })) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierTokens"); } // Ensure at most one execution is in flight at a time. const bool executionAlreadyInFlight = mExecutionInFlight.test_and_set(); if (executionAlreadyInFlight) { return toAStatus(aidl_hal::ErrorStatus::GENERAL_FAILURE, "Burst object supports at most one execution at a time"); } const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); }); return kPreparedModel->executeSynchronously(request, measureTiming, deadlineNs, loopTimeoutDurationNs, executionResult); } ndk::ScopedAStatus SampleBurst::releaseMemoryResource(int64_t memoryIdentifierToken) { if (memoryIdentifierToken < -1) { return toAStatus(aidl_hal::ErrorStatus::INVALID_ARGUMENT, "Invalid memoryIdentifierToken"); } return ndk::ScopedAStatus::ok(); } } // namespace sample_driver } // namespace nn } // namespace android