/* * Copyright (C) 2020 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. */ #ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H #define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H #include #include #include #include #include #include #include namespace android::hardware::neuralnetworks::utils { // This class adapts a function pointer and offers two affordances: // 1) This class object can be used to generate a callback (via the implicit conversion operator) // that can be used to send the result to `CallbackValue` when called. // 2) This class object can be used to retrieve the result of the callback with `take`. // // This class is thread compatible. template class CallbackValue final { public: using FunctionType = std::add_pointer_t; using CallbackType = std::function; explicit CallbackValue(FunctionType fn); // Creates a callback that forwards its arguments to `mFunction` and stores the result in // `mReturnValue`. /*implicit*/ operator CallbackType(); // NOLINT(google-explicit-constructor) // Take the result of calling `mFunction`. // Precondition: mReturnValue.has_value() // Postcondition: !mReturnValue.has_value() [[nodiscard]] ReturnType take(); private: std::optional mReturnValue; FunctionType mFunction; }; // Deduction guidelines for CallbackValue when constructed with a function pointer. template CallbackValue(ReturnType (*)(ArgTypes...))->CallbackValue; // Thread-safe container to pass a value between threads. template class TransferValue final { public: // Put the value in `TransferValue`. If `TransferValue` already has a value, this function is a // no-op. void put(Type object) const; // Take the value stored in `TransferValue`. If no value is available, this function will block // until the value becomes available. // Postcondition: !mObject.has_value() [[nodiscard]] Type take() const; private: mutable std::mutex mMutex; mutable std::condition_variable mCondition; mutable std::optional mObject GUARDED_BY(mMutex); }; // template implementations template CallbackValue::CallbackValue(FunctionType fn) : mFunction(fn) {} template CallbackValue::operator CallbackType() { return [this](ArgTypes... args) { mReturnValue = mFunction(args...); }; } template ReturnType CallbackValue::take() { CHECK(mReturnValue.has_value()); std::optional object; std::swap(object, mReturnValue); return std::move(object).value(); } template void TransferValue::put(Type object) const { { std::lock_guard guard(mMutex); // Immediately return if value already exists. if (mObject.has_value()) return; mObject.emplace(std::move(object)); } mCondition.notify_all(); } template Type TransferValue::take() const { std::unique_lock lock(mMutex); base::ScopedLockAssertion lockAssertion(mMutex); mCondition.wait(lock, [this]() REQUIRES(mMutex) { return mObject.has_value(); }); CHECK(mObject.has_value()); std::optional object; std::swap(object, mObject); return std::move(object).value(); } } // namespace android::hardware::neuralnetworks::utils #endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H