xref: /aosp_15_r20/frameworks/base/media/jni/soundpool/StreamManager.h (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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