xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/Tracing/LocklessStack.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2021 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #pragma once
18*38e8c45fSAndroid Build Coastguard Worker #include <atomic>
19*38e8c45fSAndroid Build Coastguard Worker 
20*38e8c45fSAndroid Build Coastguard Worker template <typename T>
21*38e8c45fSAndroid Build Coastguard Worker // Single consumer multi producer stack. We can understand the two operations independently to see
22*38e8c45fSAndroid Build Coastguard Worker // why they are without race condition.
23*38e8c45fSAndroid Build Coastguard Worker //
24*38e8c45fSAndroid Build Coastguard Worker // push is responsible for maintaining a linked list stored in mPush, and called from multiple
25*38e8c45fSAndroid Build Coastguard Worker // threads without lock. We can see that if two threads never observe the same value from
26*38e8c45fSAndroid Build Coastguard Worker // mPush.load, it just functions as a normal linked list. In the case where two threads observe the
27*38e8c45fSAndroid Build Coastguard Worker // same value, one of them has to execute the compare_exchange first. The one that doesn't execute
28*38e8c45fSAndroid Build Coastguard Worker // the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
29*38e8c45fSAndroid Build Coastguard Worker // compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
30*38e8c45fSAndroid Build Coastguard Worker // see that the process can repeat with an arbitrary number of threads.
31*38e8c45fSAndroid Build Coastguard Worker //
32*38e8c45fSAndroid Build Coastguard Worker // Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
33*38e8c45fSAndroid Build Coastguard Worker // the entire push list with null. This is safe, since the only other reader (push)
34*38e8c45fSAndroid Build Coastguard Worker // of mPush will retry if it changes in between it's read and atomic compare. We
35*38e8c45fSAndroid Build Coastguard Worker // then store the list and pop one element.
36*38e8c45fSAndroid Build Coastguard Worker //
37*38e8c45fSAndroid Build Coastguard Worker // If we already had something in the pop list we just pop directly.
38*38e8c45fSAndroid Build Coastguard Worker class LocklessStack {
39*38e8c45fSAndroid Build Coastguard Worker public:
40*38e8c45fSAndroid Build Coastguard Worker     class Entry {
41*38e8c45fSAndroid Build Coastguard Worker     public:
42*38e8c45fSAndroid Build Coastguard Worker         T* mValue;
43*38e8c45fSAndroid Build Coastguard Worker         std::atomic<Entry*> mNext;
Entry(T * value)44*38e8c45fSAndroid Build Coastguard Worker         Entry(T* value): mValue(value) {}
45*38e8c45fSAndroid Build Coastguard Worker     };
46*38e8c45fSAndroid Build Coastguard Worker     std::atomic<Entry*> mPush = nullptr;
47*38e8c45fSAndroid Build Coastguard Worker     std::atomic<Entry*> mPop = nullptr;
push(T * value)48*38e8c45fSAndroid Build Coastguard Worker     void push(T* value) {
49*38e8c45fSAndroid Build Coastguard Worker         Entry* entry = new Entry(value);
50*38e8c45fSAndroid Build Coastguard Worker         Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
51*38e8c45fSAndroid Build Coastguard Worker         do {
52*38e8c45fSAndroid Build Coastguard Worker             entry->mNext = previousHead;
53*38e8c45fSAndroid Build Coastguard Worker         } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
54*38e8c45fSAndroid Build Coastguard Worker     }
pop()55*38e8c45fSAndroid Build Coastguard Worker     T* pop() {
56*38e8c45fSAndroid Build Coastguard Worker         Entry* popped = mPop.load(/*std::memory_order_acquire*/);
57*38e8c45fSAndroid Build Coastguard Worker         if (popped) {
58*38e8c45fSAndroid Build Coastguard Worker             // Single consumer so this is fine
59*38e8c45fSAndroid Build Coastguard Worker             mPop.store(popped->mNext/* , std::memory_order_release */);
60*38e8c45fSAndroid Build Coastguard Worker             auto value = popped->mValue;
61*38e8c45fSAndroid Build Coastguard Worker             delete popped;
62*38e8c45fSAndroid Build Coastguard Worker             return value;
63*38e8c45fSAndroid Build Coastguard Worker         } else {
64*38e8c45fSAndroid Build Coastguard Worker             Entry *grabbedList = mPush.exchange(nullptr/* , std::memory_order_acquire */);
65*38e8c45fSAndroid Build Coastguard Worker             if (!grabbedList) return nullptr;
66*38e8c45fSAndroid Build Coastguard Worker             mPop.store(grabbedList->mNext/* , std::memory_order_release */);
67*38e8c45fSAndroid Build Coastguard Worker             auto value = grabbedList->mValue;
68*38e8c45fSAndroid Build Coastguard Worker             delete grabbedList;
69*38e8c45fSAndroid Build Coastguard Worker             return value;
70*38e8c45fSAndroid Build Coastguard Worker         }
71*38e8c45fSAndroid Build Coastguard Worker     }
72*38e8c45fSAndroid Build Coastguard Worker };
73