1*d57664e9SAndroid Build Coastguard Worker /* 2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2019 The Android Open Source Project 3*d57664e9SAndroid Build Coastguard Worker * 4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*d57664e9SAndroid Build Coastguard Worker * 8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*d57664e9SAndroid Build Coastguard Worker * 10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*d57664e9SAndroid Build Coastguard Worker * limitations under the License. 15*d57664e9SAndroid Build Coastguard Worker */ 16*d57664e9SAndroid Build Coastguard Worker 17*d57664e9SAndroid Build Coastguard Worker #pragma once 18*d57664e9SAndroid Build Coastguard Worker 19*d57664e9SAndroid Build Coastguard Worker #include "Stream.h" 20*d57664e9SAndroid Build Coastguard Worker 21*d57664e9SAndroid Build Coastguard Worker #include <condition_variable> 22*d57664e9SAndroid Build Coastguard Worker #include <future> 23*d57664e9SAndroid Build Coastguard Worker #include <list> 24*d57664e9SAndroid Build Coastguard Worker #include <map> 25*d57664e9SAndroid Build Coastguard Worker #include <memory> 26*d57664e9SAndroid Build Coastguard Worker #include <mutex> 27*d57664e9SAndroid Build Coastguard Worker #include <string> 28*d57664e9SAndroid Build Coastguard Worker #include <unordered_set> 29*d57664e9SAndroid Build Coastguard Worker #include <vector> 30*d57664e9SAndroid Build Coastguard Worker 31*d57664e9SAndroid Build Coastguard Worker #include <utils/AndroidThreads.h> 32*d57664e9SAndroid Build Coastguard Worker 33*d57664e9SAndroid Build Coastguard Worker namespace android::soundpool { 34*d57664e9SAndroid Build Coastguard Worker 35*d57664e9SAndroid Build Coastguard Worker // TODO: Move helper classes to a utility file, with separate test. 36*d57664e9SAndroid Build Coastguard Worker 37*d57664e9SAndroid Build Coastguard Worker /** 38*d57664e9SAndroid Build Coastguard Worker * JavaThread is used like std::thread but for threads that may call the JVM. 39*d57664e9SAndroid Build Coastguard Worker * 40*d57664e9SAndroid Build Coastguard Worker * std::thread does not easily attach to the JVM. We need JVM capable threads 41*d57664e9SAndroid Build Coastguard Worker * from createThreadEtc() since android binder call optimization may attempt to 42*d57664e9SAndroid Build Coastguard Worker * call back into Java if the SoundPool runs in system server. 43*d57664e9SAndroid Build Coastguard Worker * 44*d57664e9SAndroid Build Coastguard Worker * 45*d57664e9SAndroid Build Coastguard Worker * No locking is required - the member variables are inherently thread-safe. 46*d57664e9SAndroid Build Coastguard Worker */ 47*d57664e9SAndroid Build Coastguard Worker class JavaThread { 48*d57664e9SAndroid Build Coastguard Worker public: JavaThread(std::function<void ()> f,const char * name,int32_t threadPriority)49*d57664e9SAndroid Build Coastguard Worker JavaThread(std::function<void()> f, const char *name, int32_t threadPriority) 50*d57664e9SAndroid Build Coastguard Worker : mF{std::move(f)} { 51*d57664e9SAndroid Build Coastguard Worker createThreadEtc(staticFunction, this, name, threadPriority); 52*d57664e9SAndroid Build Coastguard Worker } 53*d57664e9SAndroid Build Coastguard Worker 54*d57664e9SAndroid Build Coastguard Worker JavaThread(JavaThread &&) = delete; // uses "this" ptr, not moveable. 55*d57664e9SAndroid Build Coastguard Worker ~JavaThread()56*d57664e9SAndroid Build Coastguard Worker ~JavaThread() { 57*d57664e9SAndroid Build Coastguard Worker join(); // manually block until the future is ready as std::future 58*d57664e9SAndroid Build Coastguard Worker // destructor doesn't block unless it comes from std::async 59*d57664e9SAndroid Build Coastguard Worker // and it is the last reference to shared state. 60*d57664e9SAndroid Build Coastguard Worker } 61*d57664e9SAndroid Build Coastguard Worker join()62*d57664e9SAndroid Build Coastguard Worker void join() const { 63*d57664e9SAndroid Build Coastguard Worker mFuture.wait(); 64*d57664e9SAndroid Build Coastguard Worker } 65*d57664e9SAndroid Build Coastguard Worker isClosed()66*d57664e9SAndroid Build Coastguard Worker bool isClosed() const { 67*d57664e9SAndroid Build Coastguard Worker return mIsClosed; 68*d57664e9SAndroid Build Coastguard Worker } 69*d57664e9SAndroid Build Coastguard Worker 70*d57664e9SAndroid Build Coastguard Worker private: staticFunction(void * data)71*d57664e9SAndroid Build Coastguard Worker static int staticFunction(void *data) { 72*d57664e9SAndroid Build Coastguard Worker JavaThread *jt = static_cast<JavaThread *>(data); 73*d57664e9SAndroid Build Coastguard Worker jt->mF(); 74*d57664e9SAndroid Build Coastguard Worker jt->mIsClosed = true; // set the flag that we are closed 75*d57664e9SAndroid Build Coastguard Worker // now before we allow the destructor to execute; 76*d57664e9SAndroid Build Coastguard Worker // otherwise there may be a use after free. 77*d57664e9SAndroid Build Coastguard Worker jt->mPromise.set_value(); 78*d57664e9SAndroid Build Coastguard Worker return 0; 79*d57664e9SAndroid Build Coastguard Worker } 80*d57664e9SAndroid Build Coastguard Worker 81*d57664e9SAndroid Build Coastguard Worker // No locking is provided as these variables are initialized in the constructor 82*d57664e9SAndroid Build Coastguard Worker // and the members referenced are thread-safe objects. 83*d57664e9SAndroid Build Coastguard Worker // (mFuture.wait() can block multiple threads.) 84*d57664e9SAndroid Build Coastguard Worker // Note the order of member variables is reversed for destructor. 85*d57664e9SAndroid Build Coastguard Worker const std::function<void()> mF; 86*d57664e9SAndroid Build Coastguard Worker // Used in join() to block until the thread completes. 87*d57664e9SAndroid Build Coastguard Worker // See https://en.cppreference.com/w/cpp/thread/promise for the void specialization of 88*d57664e9SAndroid Build Coastguard Worker // promise. 89*d57664e9SAndroid Build Coastguard Worker std::promise<void> mPromise; 90*d57664e9SAndroid Build Coastguard Worker std::future<void> mFuture{mPromise.get_future()}; 91*d57664e9SAndroid Build Coastguard Worker std::atomic_bool mIsClosed = false; 92*d57664e9SAndroid Build Coastguard Worker }; 93*d57664e9SAndroid Build Coastguard Worker 94*d57664e9SAndroid Build Coastguard Worker /** 95*d57664e9SAndroid Build Coastguard Worker * The ThreadPool manages thread lifetimes of SoundPool worker threads. 96*d57664e9SAndroid Build Coastguard Worker * 97*d57664e9SAndroid Build Coastguard Worker * TODO: the (eventual) goal of ThreadPool is to transparently and cooperatively 98*d57664e9SAndroid Build Coastguard Worker * maximize CPU utilization while avoiding starvation of other applications. 99*d57664e9SAndroid Build Coastguard Worker * Some possibilities: 100*d57664e9SAndroid Build Coastguard Worker * 101*d57664e9SAndroid Build Coastguard Worker * We should create worker threads when we have SoundPool work and the system is idle. 102*d57664e9SAndroid Build Coastguard Worker * CPU cycles are "use-it-or-lose-it" when the system is idle. 103*d57664e9SAndroid Build Coastguard Worker * 104*d57664e9SAndroid Build Coastguard Worker * We should adjust the priority of worker threads so that the second (and subsequent) worker 105*d57664e9SAndroid Build Coastguard Worker * threads have lower priority (should we try to promote priority also?). 106*d57664e9SAndroid Build Coastguard Worker * 107*d57664e9SAndroid Build Coastguard Worker * We should throttle the spawning of new worker threads, spacing over time, to avoid 108*d57664e9SAndroid Build Coastguard Worker * creating too many new threads all at once, on initialization. 109*d57664e9SAndroid Build Coastguard Worker */ 110*d57664e9SAndroid Build Coastguard Worker class ThreadPool { 111*d57664e9SAndroid Build Coastguard Worker public: 112*d57664e9SAndroid Build Coastguard Worker ThreadPool(size_t maxThreadCount, std::string name, 113*d57664e9SAndroid Build Coastguard Worker int32_t threadPriority = ANDROID_PRIORITY_NORMAL) mMaxThreadCount(maxThreadCount)114*d57664e9SAndroid Build Coastguard Worker : mMaxThreadCount(maxThreadCount) 115*d57664e9SAndroid Build Coastguard Worker , mName{std::move(name)} 116*d57664e9SAndroid Build Coastguard Worker , mThreadPriority(threadPriority) {} 117*d57664e9SAndroid Build Coastguard Worker ~ThreadPool()118*d57664e9SAndroid Build Coastguard Worker ~ThreadPool() { quit(); } 119*d57664e9SAndroid Build Coastguard Worker getActiveThreadCount()120*d57664e9SAndroid Build Coastguard Worker size_t getActiveThreadCount() const { return mActiveThreadCount; } getMaxThreadCount()121*d57664e9SAndroid Build Coastguard Worker size_t getMaxThreadCount() const { return mMaxThreadCount; } 122*d57664e9SAndroid Build Coastguard Worker quit()123*d57664e9SAndroid Build Coastguard Worker void quit() { 124*d57664e9SAndroid Build Coastguard Worker std::list<std::unique_ptr<JavaThread>> threads; 125*d57664e9SAndroid Build Coastguard Worker { 126*d57664e9SAndroid Build Coastguard Worker std::lock_guard lock(mThreadLock); 127*d57664e9SAndroid Build Coastguard Worker if (mQuit) return; // already joined. 128*d57664e9SAndroid Build Coastguard Worker mQuit = true; 129*d57664e9SAndroid Build Coastguard Worker threads = std::move(mThreads); 130*d57664e9SAndroid Build Coastguard Worker mThreads.clear(); 131*d57664e9SAndroid Build Coastguard Worker } 132*d57664e9SAndroid Build Coastguard Worker // mQuit set under lock, no more threads will be created. 133*d57664e9SAndroid Build Coastguard Worker for (auto &thread : threads) { 134*d57664e9SAndroid Build Coastguard Worker thread->join(); 135*d57664e9SAndroid Build Coastguard Worker thread.reset(); 136*d57664e9SAndroid Build Coastguard Worker } 137*d57664e9SAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(mActiveThreadCount != 0, 138*d57664e9SAndroid Build Coastguard Worker "Invalid Active Threads: %zu", (size_t)mActiveThreadCount); 139*d57664e9SAndroid Build Coastguard Worker } 140*d57664e9SAndroid Build Coastguard Worker 141*d57664e9SAndroid Build Coastguard Worker // returns a non-zero id if successful, the id is to help logging messages. launch(std::function<void (int32_t)> f)142*d57664e9SAndroid Build Coastguard Worker int32_t launch(std::function<void(int32_t /* id */)> f) { 143*d57664e9SAndroid Build Coastguard Worker std::list<std::unique_ptr<JavaThread>> threadsToRelease; // release outside of lock. 144*d57664e9SAndroid Build Coastguard Worker std::lock_guard lock(mThreadLock); 145*d57664e9SAndroid Build Coastguard Worker if (mQuit) return 0; // ignore if we have quit 146*d57664e9SAndroid Build Coastguard Worker 147*d57664e9SAndroid Build Coastguard Worker // clean up threads. 148*d57664e9SAndroid Build Coastguard Worker for (auto it = mThreads.begin(); it != mThreads.end(); ) { 149*d57664e9SAndroid Build Coastguard Worker if ((*it)->isClosed()) { 150*d57664e9SAndroid Build Coastguard Worker threadsToRelease.emplace_back(std::move(*it)); 151*d57664e9SAndroid Build Coastguard Worker it = mThreads.erase(it); 152*d57664e9SAndroid Build Coastguard Worker } else { 153*d57664e9SAndroid Build Coastguard Worker ++it; 154*d57664e9SAndroid Build Coastguard Worker } 155*d57664e9SAndroid Build Coastguard Worker } 156*d57664e9SAndroid Build Coastguard Worker 157*d57664e9SAndroid Build Coastguard Worker const size_t threadCount = mThreads.size(); 158*d57664e9SAndroid Build Coastguard Worker if (threadCount < mMaxThreadCount) { 159*d57664e9SAndroid Build Coastguard Worker // if the id wraps, we don't care about collisions. it's just for logging. 160*d57664e9SAndroid Build Coastguard Worker mNextThreadId = mNextThreadId == INT32_MAX ? 1 : ++mNextThreadId; 161*d57664e9SAndroid Build Coastguard Worker const int32_t id = mNextThreadId; 162*d57664e9SAndroid Build Coastguard Worker mThreads.emplace_back(std::make_unique<JavaThread>( 163*d57664e9SAndroid Build Coastguard Worker [this, id, mf = std::move(f)] { mf(id); --mActiveThreadCount; }, 164*d57664e9SAndroid Build Coastguard Worker (mName + std::to_string(id)).c_str(), 165*d57664e9SAndroid Build Coastguard Worker mThreadPriority)); 166*d57664e9SAndroid Build Coastguard Worker ++mActiveThreadCount; 167*d57664e9SAndroid Build Coastguard Worker return id; 168*d57664e9SAndroid Build Coastguard Worker } 169*d57664e9SAndroid Build Coastguard Worker return 0; 170*d57664e9SAndroid Build Coastguard Worker } 171*d57664e9SAndroid Build Coastguard Worker 172*d57664e9SAndroid Build Coastguard Worker // TODO: launch only if load average is low. 173*d57664e9SAndroid Build Coastguard Worker // This gets the load average 174*d57664e9SAndroid Build Coastguard Worker // See also std::thread::hardware_concurrency() for the concurrent capability. getLoadAvg()175*d57664e9SAndroid Build Coastguard Worker static double getLoadAvg() { 176*d57664e9SAndroid Build Coastguard Worker double loadAvg[1]; 177*d57664e9SAndroid Build Coastguard Worker if (getloadavg(loadAvg, std::size(loadAvg)) > 0) { 178*d57664e9SAndroid Build Coastguard Worker return loadAvg[0]; 179*d57664e9SAndroid Build Coastguard Worker } 180*d57664e9SAndroid Build Coastguard Worker return -1.; 181*d57664e9SAndroid Build Coastguard Worker } 182*d57664e9SAndroid Build Coastguard Worker 183*d57664e9SAndroid Build Coastguard Worker private: 184*d57664e9SAndroid Build Coastguard Worker const size_t mMaxThreadCount; 185*d57664e9SAndroid Build Coastguard Worker const std::string mName; 186*d57664e9SAndroid Build Coastguard Worker const int32_t mThreadPriority; 187*d57664e9SAndroid Build Coastguard Worker 188*d57664e9SAndroid Build Coastguard Worker std::atomic_size_t mActiveThreadCount = 0; 189*d57664e9SAndroid Build Coastguard Worker 190*d57664e9SAndroid Build Coastguard Worker std::mutex mThreadLock; 191*d57664e9SAndroid Build Coastguard Worker bool mQuit GUARDED_BY(mThreadLock) = false; 192*d57664e9SAndroid Build Coastguard Worker int32_t mNextThreadId GUARDED_BY(mThreadLock) = 0; 193*d57664e9SAndroid Build Coastguard Worker std::list<std::unique_ptr<JavaThread>> mThreads GUARDED_BY(mThreadLock); 194*d57664e9SAndroid Build Coastguard Worker }; 195*d57664e9SAndroid Build Coastguard Worker 196*d57664e9SAndroid Build Coastguard Worker /** 197*d57664e9SAndroid Build Coastguard Worker * A Perfect HashTable for IDs (key) to pointers (value). 198*d57664e9SAndroid Build Coastguard Worker * 199*d57664e9SAndroid Build Coastguard Worker * There are no collisions. Why? because we generate the IDs for you to look up :-). 200*d57664e9SAndroid Build Coastguard Worker * 201*d57664e9SAndroid Build Coastguard Worker * The goal of this hash table is to map an integer ID handle > 0 to a pointer. 202*d57664e9SAndroid Build Coastguard Worker * We give these IDs in monotonic order (though we may skip if it were to cause a collision). 203*d57664e9SAndroid Build Coastguard Worker * 204*d57664e9SAndroid Build Coastguard Worker * The size of the hashtable must be large enough to accommodate the max number of keys. 205*d57664e9SAndroid Build Coastguard Worker * We suggest 2x. 206*d57664e9SAndroid Build Coastguard Worker * 207*d57664e9SAndroid Build Coastguard Worker * Readers are lockless 208*d57664e9SAndroid Build Coastguard Worker * Single writer could be lockless, but we allow multiple writers through an internal lock. 209*d57664e9SAndroid Build Coastguard Worker * 210*d57664e9SAndroid Build Coastguard Worker * For the Key type K, valid keys generated are > 0 (signed or unsigned) 211*d57664e9SAndroid Build Coastguard Worker * For the Value type V, values are pointers - nullptr means empty. 212*d57664e9SAndroid Build Coastguard Worker */ 213*d57664e9SAndroid Build Coastguard Worker template <typename K, typename V> 214*d57664e9SAndroid Build Coastguard Worker class PerfectHash { 215*d57664e9SAndroid Build Coastguard Worker public: PerfectHash(size_t hashCapacity)216*d57664e9SAndroid Build Coastguard Worker PerfectHash(size_t hashCapacity) 217*d57664e9SAndroid Build Coastguard Worker : mHashCapacity(hashCapacity) 218*d57664e9SAndroid Build Coastguard Worker , mK2V{new std::atomic<V>[hashCapacity]()} { 219*d57664e9SAndroid Build Coastguard Worker } 220*d57664e9SAndroid Build Coastguard Worker 221*d57664e9SAndroid Build Coastguard Worker // Generate a key for a value V. 222*d57664e9SAndroid Build Coastguard Worker // There is a testing function getKforV() which checks what the value reports as its key. 223*d57664e9SAndroid Build Coastguard Worker // 224*d57664e9SAndroid Build Coastguard Worker // Calls back into getKforV under lock. 225*d57664e9SAndroid Build Coastguard Worker // 226*d57664e9SAndroid Build Coastguard Worker // We expect that the hashCapacity is 2x the number of stored keys in order 227*d57664e9SAndroid Build Coastguard Worker // to have one or two tries to find an empty slot 228*d57664e9SAndroid Build Coastguard Worker K generateKey(V value, std::function<K(V)> getKforV, K oldKey = 0) { 229*d57664e9SAndroid Build Coastguard Worker std::lock_guard lock(mHashLock); 230*d57664e9SAndroid Build Coastguard Worker // try to remove the old key. 231*d57664e9SAndroid Build Coastguard Worker if (oldKey > 0) { // key valid 232*d57664e9SAndroid Build Coastguard Worker const V v = getValue(oldKey); 233*d57664e9SAndroid Build Coastguard Worker if (v != nullptr) { // value still valid 234*d57664e9SAndroid Build Coastguard Worker const K atPosition = getKforV(v); 235*d57664e9SAndroid Build Coastguard Worker if (atPosition < 0 || // invalid value 236*d57664e9SAndroid Build Coastguard Worker atPosition == oldKey || // value's key still valid and matches old key 237*d57664e9SAndroid Build Coastguard Worker ((atPosition ^ oldKey) & (mHashCapacity - 1)) != 0) { // stale key entry 238*d57664e9SAndroid Build Coastguard Worker getValue(oldKey) = nullptr; // invalidate 239*d57664e9SAndroid Build Coastguard Worker } 240*d57664e9SAndroid Build Coastguard Worker } // else if value is invalid, no need to invalidate. 241*d57664e9SAndroid Build Coastguard Worker } 242*d57664e9SAndroid Build Coastguard Worker // check if we are invalidating only. 243*d57664e9SAndroid Build Coastguard Worker if (value == nullptr) return 0; 244*d57664e9SAndroid Build Coastguard Worker // now insert the new value and return the key. 245*d57664e9SAndroid Build Coastguard Worker size_t tries = 0; 246*d57664e9SAndroid Build Coastguard Worker for (; tries < mHashCapacity; ++tries) { 247*d57664e9SAndroid Build Coastguard Worker mNextKey = mNextKey == std::numeric_limits<K>::max() ? 1 : mNextKey + 1; 248*d57664e9SAndroid Build Coastguard Worker const V v = getValue(mNextKey); 249*d57664e9SAndroid Build Coastguard Worker //ALOGD("tries: %zu, key:%d value:%p", tries, (int)mNextKey, v); 250*d57664e9SAndroid Build Coastguard Worker if (v == nullptr) break; // empty 251*d57664e9SAndroid Build Coastguard Worker const K atPosition = getKforV(v); 252*d57664e9SAndroid Build Coastguard Worker //ALOGD("tries: %zu key atPosition:%d", tries, (int)atPosition); 253*d57664e9SAndroid Build Coastguard Worker if (atPosition < 0 || // invalid value 254*d57664e9SAndroid Build Coastguard Worker ((atPosition ^ mNextKey) & (mHashCapacity - 1)) != 0) { // stale key entry 255*d57664e9SAndroid Build Coastguard Worker break; 256*d57664e9SAndroid Build Coastguard Worker } 257*d57664e9SAndroid Build Coastguard Worker } 258*d57664e9SAndroid Build Coastguard Worker LOG_ALWAYS_FATAL_IF(tries == mHashCapacity, "hash table overflow!"); 259*d57664e9SAndroid Build Coastguard Worker //ALOGD("%s: found after %zu tries", __func__, tries); 260*d57664e9SAndroid Build Coastguard Worker getValue(mNextKey) = value; 261*d57664e9SAndroid Build Coastguard Worker return mNextKey; 262*d57664e9SAndroid Build Coastguard Worker } 263*d57664e9SAndroid Build Coastguard Worker getValue(K key)264*d57664e9SAndroid Build Coastguard Worker std::atomic<V> &getValue(K key) { return mK2V[key & (mHashCapacity - 1)]; } getValue(K key)265*d57664e9SAndroid Build Coastguard Worker const std::atomic_int32_t &getValue(K key) const { return mK2V[key & (mHashCapacity - 1)]; } 266*d57664e9SAndroid Build Coastguard Worker 267*d57664e9SAndroid Build Coastguard Worker private: 268*d57664e9SAndroid Build Coastguard Worker mutable std::mutex mHashLock; 269*d57664e9SAndroid Build Coastguard Worker const size_t mHashCapacity; // size of mK2V no lock needed. 270*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<std::atomic<V>[]> mK2V; // no lock needed for read access. GUARDED_BY(mHashLock)271*d57664e9SAndroid Build Coastguard Worker K mNextKey GUARDED_BY(mHashLock) {}; 272*d57664e9SAndroid Build Coastguard Worker }; 273*d57664e9SAndroid Build Coastguard Worker 274*d57664e9SAndroid Build Coastguard Worker /** 275*d57664e9SAndroid Build Coastguard Worker * StreamMap contains the all the valid streams available to SoundPool. 276*d57664e9SAndroid Build Coastguard Worker * 277*d57664e9SAndroid Build Coastguard Worker * There is no Lock required for this class because the streams are 278*d57664e9SAndroid Build Coastguard Worker * allocated in the constructor, the lookup is lockless, and the Streams 279*d57664e9SAndroid Build Coastguard Worker * returned are locked internally. 280*d57664e9SAndroid Build Coastguard Worker * 281*d57664e9SAndroid Build Coastguard Worker * The lookup uses a perfect hash. 282*d57664e9SAndroid Build Coastguard Worker * It is possible to use a lockless hash table or to use a stripe-locked concurrent 283*d57664e9SAndroid Build Coastguard Worker * hashmap for essentially lock-free lookup. 284*d57664e9SAndroid Build Coastguard Worker * 285*d57664e9SAndroid Build Coastguard Worker * This follows Map-Reduce parallelism model. 286*d57664e9SAndroid Build Coastguard Worker * https://en.wikipedia.org/wiki/MapReduce 287*d57664e9SAndroid Build Coastguard Worker * 288*d57664e9SAndroid Build Coastguard Worker * Conceivably the forEach could be parallelized using std::for_each with a 289*d57664e9SAndroid Build Coastguard Worker * std::execution::par policy. 290*d57664e9SAndroid Build Coastguard Worker * 291*d57664e9SAndroid Build Coastguard Worker * https://en.cppreference.com/w/cpp/algorithm/for_each 292*d57664e9SAndroid Build Coastguard Worker */ 293*d57664e9SAndroid Build Coastguard Worker class StreamMap { 294*d57664e9SAndroid Build Coastguard Worker public: 295*d57664e9SAndroid Build Coastguard Worker explicit StreamMap(int32_t streams); 296*d57664e9SAndroid Build Coastguard Worker 297*d57664e9SAndroid Build Coastguard Worker // Returns the stream associated with streamID or nullptr if not found. 298*d57664e9SAndroid Build Coastguard Worker // This need not be locked. 299*d57664e9SAndroid Build Coastguard Worker // The stream ID will never migrate to another Stream, but it may change 300*d57664e9SAndroid Build Coastguard Worker // underneath you. The Stream operations that take a streamID will confirm 301*d57664e9SAndroid Build Coastguard Worker // that the streamID matches under the Stream lock before executing otherwise 302*d57664e9SAndroid Build Coastguard Worker // it ignores the command as stale. 303*d57664e9SAndroid Build Coastguard Worker Stream* findStream(int32_t streamID) const; 304*d57664e9SAndroid Build Coastguard Worker 305*d57664e9SAndroid Build Coastguard Worker // Iterates through the stream pool applying the function f. 306*d57664e9SAndroid Build Coastguard Worker // Since this enumerates over every single stream, it is unlocked. 307*d57664e9SAndroid Build Coastguard Worker // 308*d57664e9SAndroid Build Coastguard Worker // See related: https://en.cppreference.com/w/cpp/algorithm/for_each forEach(std::function<void (const Stream *)> f)309*d57664e9SAndroid Build Coastguard Worker void forEach(std::function<void(const Stream *)>f) const { 310*d57664e9SAndroid Build Coastguard Worker for (size_t i = 0; i < mStreamPoolSize; ++i) { 311*d57664e9SAndroid Build Coastguard Worker f(&mStreamPool[i]); 312*d57664e9SAndroid Build Coastguard Worker } 313*d57664e9SAndroid Build Coastguard Worker } 314*d57664e9SAndroid Build Coastguard Worker forEach(std::function<void (Stream *)> f)315*d57664e9SAndroid Build Coastguard Worker void forEach(std::function<void(Stream *)>f) { 316*d57664e9SAndroid Build Coastguard Worker for (size_t i = 0; i < mStreamPoolSize; ++i) { 317*d57664e9SAndroid Build Coastguard Worker f(&mStreamPool[i]); 318*d57664e9SAndroid Build Coastguard Worker } 319*d57664e9SAndroid Build Coastguard Worker } 320*d57664e9SAndroid Build Coastguard Worker 321*d57664e9SAndroid Build Coastguard Worker // Returns the pair stream for a given Stream. 322*d57664e9SAndroid Build Coastguard Worker // This need not be locked as it is a property of the pointer address. getPairStream(const Stream * stream)323*d57664e9SAndroid Build Coastguard Worker Stream* getPairStream(const Stream* stream) const { 324*d57664e9SAndroid Build Coastguard Worker const size_t index = streamPosition(stream); 325*d57664e9SAndroid Build Coastguard Worker return &mStreamPool[index ^ 1]; 326*d57664e9SAndroid Build Coastguard Worker } 327*d57664e9SAndroid Build Coastguard Worker 328*d57664e9SAndroid Build Coastguard Worker // find the position of the stream in mStreamPool array. 329*d57664e9SAndroid Build Coastguard Worker size_t streamPosition(const Stream* stream) const; // no lock needed 330*d57664e9SAndroid Build Coastguard Worker getStreamMapSize()331*d57664e9SAndroid Build Coastguard Worker size_t getStreamMapSize() const { 332*d57664e9SAndroid Build Coastguard Worker return mStreamPoolSize; 333*d57664e9SAndroid Build Coastguard Worker } 334*d57664e9SAndroid Build Coastguard Worker 335*d57664e9SAndroid Build Coastguard Worker // find the next valid ID for a stream and store in hash table. 336*d57664e9SAndroid Build Coastguard Worker int32_t getNextIdForStream(Stream* stream) const; 337*d57664e9SAndroid Build Coastguard Worker 338*d57664e9SAndroid Build Coastguard Worker private: 339*d57664e9SAndroid Build Coastguard Worker 340*d57664e9SAndroid Build Coastguard Worker // use the hash table to attempt to find the stream. 341*d57664e9SAndroid Build Coastguard Worker // nullptr is returned if the lookup fails. 342*d57664e9SAndroid Build Coastguard Worker Stream* lookupStreamFromId(int32_t streamID) const; 343*d57664e9SAndroid Build Coastguard Worker 344*d57664e9SAndroid Build Coastguard Worker // The stream pool is initialized in the constructor, effectively const. 345*d57664e9SAndroid Build Coastguard Worker // no locking required for access. 346*d57664e9SAndroid Build Coastguard Worker // 347*d57664e9SAndroid Build Coastguard Worker // The constructor parameter "streams" results in streams pairs of streams. 348*d57664e9SAndroid Build Coastguard Worker // We have twice as many streams because we wish to return a streamID "handle" 349*d57664e9SAndroid Build Coastguard Worker // back to the app immediately, while we may be stopping the other stream in the 350*d57664e9SAndroid Build Coastguard Worker // pair to get its AudioTrack :-). 351*d57664e9SAndroid Build Coastguard Worker // 352*d57664e9SAndroid Build Coastguard Worker // Of the stream pair, only one of the streams may have an AudioTrack. 353*d57664e9SAndroid Build Coastguard Worker // The fixed association of a stream pair allows callbacks from the AudioTrack 354*d57664e9SAndroid Build Coastguard Worker // to be associated properly to either one or the other of the stream pair. 355*d57664e9SAndroid Build Coastguard Worker // 356*d57664e9SAndroid Build Coastguard Worker // TODO: The stream pair arrangement can be removed if we have better AudioTrack 357*d57664e9SAndroid Build Coastguard Worker // callback handling (being able to remove and change the callback after construction). 358*d57664e9SAndroid Build Coastguard Worker // 359*d57664e9SAndroid Build Coastguard Worker // Streams may be accessed anytime off of the stream pool 360*d57664e9SAndroid Build Coastguard Worker // as there is internal locking on each stream. 361*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<Stream[]> mStreamPool; // no lock needed for access. 362*d57664e9SAndroid Build Coastguard Worker size_t mStreamPoolSize; // no lock needed for access. 363*d57664e9SAndroid Build Coastguard Worker 364*d57664e9SAndroid Build Coastguard Worker // In order to find the Stream from a StreamID, we could do a linear lookup in mStreamPool. 365*d57664e9SAndroid Build Coastguard Worker // As an alternative, one could use stripe-locked or lock-free concurrent hashtables. 366*d57664e9SAndroid Build Coastguard Worker // 367*d57664e9SAndroid Build Coastguard Worker // When considering linear search vs hashmap, verify the typical use-case size. 368*d57664e9SAndroid Build Coastguard Worker // Linear search is faster than std::unordered_map (circa 2018) for less than 40 elements. 369*d57664e9SAndroid Build Coastguard Worker // [ Skarupke, M. (2018), "You Can Do Better than std::unordered_map: New and Recent 370*d57664e9SAndroid Build Coastguard Worker // Improvements to Hash Table Performance." C++Now 2018. cppnow.org, see 371*d57664e9SAndroid Build Coastguard Worker // https://www.youtube.com/watch?v=M2fKMP47slQ ] 372*d57664e9SAndroid Build Coastguard Worker // 373*d57664e9SAndroid Build Coastguard Worker // Here, we use a PerfectHash of Id to Stream *, since we can control the 374*d57664e9SAndroid Build Coastguard Worker // StreamID returned to the user. This allows O(1) read access to mStreamPool lock-free. 375*d57664e9SAndroid Build Coastguard Worker // 376*d57664e9SAndroid Build Coastguard Worker // We prefer that the next stream ID is monotonic for aesthetic reasons 377*d57664e9SAndroid Build Coastguard Worker // (if we didn't care about monotonicity, a simple method is to apply a generation count 378*d57664e9SAndroid Build Coastguard Worker // to each stream in the unused upper bits of its index in mStreamPool for the id). 379*d57664e9SAndroid Build Coastguard Worker // 380*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<PerfectHash<int32_t, Stream *>> mPerfectHash; 381*d57664e9SAndroid Build Coastguard Worker }; 382*d57664e9SAndroid Build Coastguard Worker 383*d57664e9SAndroid Build Coastguard Worker /** 384*d57664e9SAndroid Build Coastguard Worker * StreamManager is used to manage the streams (accessed by StreamID from Java). 385*d57664e9SAndroid Build Coastguard Worker * 386*d57664e9SAndroid Build Coastguard Worker * Locking order (proceeds from application to component). 387*d57664e9SAndroid Build Coastguard Worker * SoundPool mApiLock (if needed) -> StreamManager mStreamManagerLock 388*d57664e9SAndroid Build Coastguard Worker * -> pair Stream mLock -> queued Stream mLock 389*d57664e9SAndroid Build Coastguard Worker */ 390*d57664e9SAndroid Build Coastguard Worker class StreamManager : public StreamMap { 391*d57664e9SAndroid Build Coastguard Worker public: 392*d57664e9SAndroid Build Coastguard Worker // Note: the SoundPool pointer is only used for stream initialization. 393*d57664e9SAndroid Build Coastguard Worker // It is not stored in StreamManager. 394*d57664e9SAndroid Build Coastguard Worker StreamManager(int32_t streams, size_t threads, const audio_attributes_t& attributes, 395*d57664e9SAndroid Build Coastguard Worker std::string opPackageName); 396*d57664e9SAndroid Build Coastguard Worker ~StreamManager(); 397*d57664e9SAndroid Build Coastguard Worker 398*d57664e9SAndroid Build Coastguard Worker // Returns positive streamID on success, 0 on failure. This is locked. 399*d57664e9SAndroid Build Coastguard Worker int32_t queueForPlay(const std::shared_ptr<Sound> &sound, 400*d57664e9SAndroid Build Coastguard Worker int32_t soundID, float leftVolume, float rightVolume, 401*d57664e9SAndroid Build Coastguard Worker int32_t priority, int32_t loop, float rate, int32_t playerIId) 402*d57664e9SAndroid Build Coastguard Worker NO_THREAD_SAFETY_ANALYSIS; // uses unique_lock 403*d57664e9SAndroid Build Coastguard Worker 404*d57664e9SAndroid Build Coastguard Worker /////////////////////////////////////////////////////////////////////// 405*d57664e9SAndroid Build Coastguard Worker // Called from soundpool::Stream 406*d57664e9SAndroid Build Coastguard Worker getAttributes()407*d57664e9SAndroid Build Coastguard Worker const audio_attributes_t* getAttributes() const { return &mAttributes; } 408*d57664e9SAndroid Build Coastguard Worker getOpPackageName()409*d57664e9SAndroid Build Coastguard Worker const std::string& getOpPackageName() const { return mOpPackageName; } 410*d57664e9SAndroid Build Coastguard Worker 411*d57664e9SAndroid Build Coastguard Worker // Moves the stream to the restart queue (called upon BUFFER_END of the static track) 412*d57664e9SAndroid Build Coastguard Worker // this is locked internally. 413*d57664e9SAndroid Build Coastguard Worker // If activeStreamIDToMatch is nonzero, it will only move to the restart queue 414*d57664e9SAndroid Build Coastguard Worker // if the streamIDToMatch is found on the active queue. 415*d57664e9SAndroid Build Coastguard Worker void moveToRestartQueue(Stream* stream, int32_t activeStreamIDToMatch = 0); 416*d57664e9SAndroid Build Coastguard Worker 417*d57664e9SAndroid Build Coastguard Worker private: 418*d57664e9SAndroid Build Coastguard Worker 419*d57664e9SAndroid Build Coastguard Worker void run(int32_t id) NO_THREAD_SAFETY_ANALYSIS; // worker thread, takes unique_lock. 420*d57664e9SAndroid Build Coastguard Worker void dump() const; // no lock needed 421*d57664e9SAndroid Build Coastguard Worker 422*d57664e9SAndroid Build Coastguard Worker // returns true if more worker threads are needed. needMoreThreads_l()423*d57664e9SAndroid Build Coastguard Worker bool needMoreThreads_l() REQUIRES(mStreamManagerLock) { 424*d57664e9SAndroid Build Coastguard Worker return mRestartStreams.size() > 0 && 425*d57664e9SAndroid Build Coastguard Worker (mThreadPool->getActiveThreadCount() == 0 426*d57664e9SAndroid Build Coastguard Worker || std::distance(mRestartStreams.begin(), 427*d57664e9SAndroid Build Coastguard Worker mRestartStreams.upper_bound(systemTime())) 428*d57664e9SAndroid Build Coastguard Worker > (ptrdiff_t)mThreadPool->getActiveThreadCount()); 429*d57664e9SAndroid Build Coastguard Worker } 430*d57664e9SAndroid Build Coastguard Worker 431*d57664e9SAndroid Build Coastguard Worker // returns true if the stream was added. 432*d57664e9SAndroid Build Coastguard Worker bool moveToRestartQueue_l( 433*d57664e9SAndroid Build Coastguard Worker Stream* stream, int32_t activeStreamIDToMatch = 0) REQUIRES(mStreamManagerLock); 434*d57664e9SAndroid Build Coastguard Worker // returns number of queues the stream was removed from (should be 0 or 1); 435*d57664e9SAndroid Build Coastguard Worker // a special code of -1 is returned if activeStreamIDToMatch is > 0 and 436*d57664e9SAndroid Build Coastguard Worker // the stream wasn't found on the active queue. 437*d57664e9SAndroid Build Coastguard Worker ssize_t removeFromQueues_l( 438*d57664e9SAndroid Build Coastguard Worker Stream* stream, int32_t activeStreamIDToMatch = 0) REQUIRES(mStreamManagerLock); 439*d57664e9SAndroid Build Coastguard Worker void addToRestartQueue_l(Stream *stream) REQUIRES(mStreamManagerLock); 440*d57664e9SAndroid Build Coastguard Worker void addToActiveQueue_l(Stream *stream) REQUIRES(mStreamManagerLock); 441*d57664e9SAndroid Build Coastguard Worker void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock); 442*d57664e9SAndroid Build Coastguard Worker 443*d57664e9SAndroid Build Coastguard Worker const audio_attributes_t mAttributes; 444*d57664e9SAndroid Build Coastguard Worker const std::string mOpPackageName; 445*d57664e9SAndroid Build Coastguard Worker 446*d57664e9SAndroid Build Coastguard Worker // For legacy compatibility, we lock the stream manager on stop when 447*d57664e9SAndroid Build Coastguard Worker // there is only one stream. This allows a play to be called immediately 448*d57664e9SAndroid Build Coastguard Worker // after stopping, otherwise it is possible that the play might be discarded 449*d57664e9SAndroid Build Coastguard Worker // (returns 0) because that stream may be in the worker thread call to stop. 450*d57664e9SAndroid Build Coastguard Worker const bool mLockStreamManagerStop; 451*d57664e9SAndroid Build Coastguard Worker 452*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<ThreadPool> mThreadPool; // locked internally 453*d57664e9SAndroid Build Coastguard Worker 454*d57664e9SAndroid Build Coastguard Worker // mStreamManagerLock is used to lock access for transitions between the 455*d57664e9SAndroid Build Coastguard Worker // 4 stream queues by the Manager Thread or by the user initiated play(). 456*d57664e9SAndroid Build Coastguard Worker // A stream pair has exactly one stream on exactly one of the queues. 457*d57664e9SAndroid Build Coastguard Worker std::mutex mStreamManagerLock; 458*d57664e9SAndroid Build Coastguard Worker std::condition_variable mStreamManagerCondition GUARDED_BY(mStreamManagerLock); 459*d57664e9SAndroid Build Coastguard Worker 460*d57664e9SAndroid Build Coastguard Worker bool mQuit GUARDED_BY(mStreamManagerLock) = false; 461*d57664e9SAndroid Build Coastguard Worker 462*d57664e9SAndroid Build Coastguard Worker // There are constructor arg "streams" pairs of streams, only one of each 463*d57664e9SAndroid Build Coastguard Worker // pair on the 4 stream queues below. The other stream in the pair serves as 464*d57664e9SAndroid Build Coastguard Worker // placeholder to accumulate user changes, pending actual availability of the 465*d57664e9SAndroid Build Coastguard Worker // AudioTrack, as it may be in use, requiring stop-then-restart. 466*d57664e9SAndroid Build Coastguard Worker // 467*d57664e9SAndroid Build Coastguard Worker // The 4 queues are implemented in the appropriate STL container based on perceived 468*d57664e9SAndroid Build Coastguard Worker // optimality. 469*d57664e9SAndroid Build Coastguard Worker 470*d57664e9SAndroid Build Coastguard Worker // 1) mRestartStreams: Streams awaiting stop. 471*d57664e9SAndroid Build Coastguard Worker // The paired stream may be active (but with no AudioTrack), and will be restarted 472*d57664e9SAndroid Build Coastguard Worker // with an active AudioTrack when the current stream is stopped. 473*d57664e9SAndroid Build Coastguard Worker std::multimap<int64_t /* stopTimeNs */, Stream*> 474*d57664e9SAndroid Build Coastguard Worker mRestartStreams GUARDED_BY(mStreamManagerLock); 475*d57664e9SAndroid Build Coastguard Worker 476*d57664e9SAndroid Build Coastguard Worker // 2) mActiveStreams: Streams that are active. 477*d57664e9SAndroid Build Coastguard Worker // The paired stream will be inactive. 478*d57664e9SAndroid Build Coastguard Worker // This is in order of specified by kStealActiveStream_OldestFirst 479*d57664e9SAndroid Build Coastguard Worker std::list<Stream*> mActiveStreams GUARDED_BY(mStreamManagerLock); 480*d57664e9SAndroid Build Coastguard Worker 481*d57664e9SAndroid Build Coastguard Worker // 3) mAvailableStreams: Streams that are inactive. 482*d57664e9SAndroid Build Coastguard Worker // The paired stream will also be inactive. 483*d57664e9SAndroid Build Coastguard Worker // No particular order. 484*d57664e9SAndroid Build Coastguard Worker std::unordered_set<Stream*> mAvailableStreams GUARDED_BY(mStreamManagerLock); 485*d57664e9SAndroid Build Coastguard Worker 486*d57664e9SAndroid Build Coastguard Worker // 4) mProcessingStreams: Streams that are being processed by the ManagerThreads 487*d57664e9SAndroid Build Coastguard Worker // When on this queue, the stream and its pair are not available for stealing. 488*d57664e9SAndroid Build Coastguard Worker // Each ManagerThread will have at most one stream on the mProcessingStreams queue. 489*d57664e9SAndroid Build Coastguard Worker // The paired stream may be active or restarting. 490*d57664e9SAndroid Build Coastguard Worker // No particular order. 491*d57664e9SAndroid Build Coastguard Worker std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock); 492*d57664e9SAndroid Build Coastguard Worker }; 493*d57664e9SAndroid Build Coastguard Worker 494*d57664e9SAndroid Build Coastguard Worker } // namespace android::soundpool 495