1 /* 2 * Copyright (C) 2024 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 #pragma once 18 19 #include <deque> 20 #include <mutex> 21 #include <thread> 22 #include <utils/Mutex.h> // has thread safety annotations 23 24 namespace android::audio_utils { 25 26 /** 27 * CommandThread is used for serial execution of commands 28 * on a single worker thread. 29 * 30 * This class is thread-safe. 31 */ 32 33 class CommandThread { 34 public: CommandThread()35 CommandThread() { 36 // threadLoop() should be started after the class is initialized. 37 mThread = std::thread([this](){this->threadLoop();}); 38 } 39 ~CommandThread()40 ~CommandThread() { 41 quit(); 42 mThread.join(); 43 } 44 45 /** 46 * Add a command to the command queue. 47 * 48 * If the func is a closure containing references, suggest using shared_ptr 49 * instead to maintain proper lifetime. 50 * 51 * @param name for dump() purposes. 52 * @param func command to execute 53 */ add(std::string_view name,std::function<void ()> && func)54 void add(std::string_view name, std::function<void()>&& func) { 55 std::lock_guard lg(mMutex); 56 if (mQuit) return; 57 mCommands.emplace_back(name, std::move(func)); 58 if (mCommands.size() == 1) mConditionVariable.notify_one(); 59 } 60 61 /** 62 * Returns the string of commands, separated by newlines. 63 */ dump()64 std::string dump() const { 65 std::string result; 66 std::lock_guard lg(mMutex); 67 for (const auto &p : mCommands) { 68 result.append(p.first).append("\n"); 69 } 70 return result; 71 } 72 73 /** 74 * Quits the command thread and empties the command queue. 75 */ quit()76 void quit() { 77 std::lock_guard lg(mMutex); 78 if (mQuit) return; 79 mQuit = true; 80 mCommands.clear(); 81 mConditionVariable.notify_one(); 82 } 83 84 /** 85 * Returns the number of commands on the queue. 86 */ size()87 size_t size() const { 88 std::lock_guard lg(mMutex); 89 return mCommands.size(); 90 } 91 92 private: 93 std::thread mThread; 94 mutable std::mutex mMutex; 95 std::condition_variable mConditionVariable GUARDED_BY(mMutex); 96 std::deque<std::pair<std::string, std::function<void()>>> mCommands GUARDED_BY(mMutex); 97 bool mQuit GUARDED_BY(mMutex) = false; 98 threadLoop()99 void threadLoop() NO_THREAD_SAFETY_ANALYSIS { 100 std::unique_lock ul(mMutex); 101 while (!mQuit) { 102 if (!mCommands.empty()) { 103 auto name = std::move(mCommands.front().first); 104 auto func = std::move(mCommands.front().second); 105 mCommands.pop_front(); 106 ul.unlock(); 107 // ALOGD("%s: executing %s", __func__, name.c_str()); 108 func(); 109 ul.lock(); 110 continue; 111 } 112 mConditionVariable.wait(ul); 113 } 114 } 115 }; 116 117 } // namespace android::audio_utils 118