xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/LocklessQueue.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2022 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 
19*38e8c45fSAndroid Build Coastguard Worker #include <atomic>
20*38e8c45fSAndroid Build Coastguard Worker #include <optional>
21*38e8c45fSAndroid Build Coastguard Worker 
22*38e8c45fSAndroid Build Coastguard Worker // Single consumer multi producer queue. We can understand the two operations independently to see
23*38e8c45fSAndroid Build Coastguard Worker // why they are without race condition.
24*38e8c45fSAndroid Build Coastguard Worker //
25*38e8c45fSAndroid Build Coastguard Worker // push is responsible for maintaining a linked list stored in mPush, and called from multiple
26*38e8c45fSAndroid Build Coastguard Worker // threads without lock. We can see that if two threads never observe the same value from
27*38e8c45fSAndroid Build Coastguard Worker // mPush.load, it just functions as a normal linked list. In the case where two threads observe the
28*38e8c45fSAndroid Build Coastguard Worker // same value, one of them has to execute the compare_exchange first. The one that doesn't execute
29*38e8c45fSAndroid Build Coastguard Worker // the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
30*38e8c45fSAndroid Build Coastguard Worker // compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
31*38e8c45fSAndroid Build Coastguard Worker // see that the process can repeat with an arbitrary number of threads.
32*38e8c45fSAndroid Build Coastguard Worker //
33*38e8c45fSAndroid Build Coastguard Worker // Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
34*38e8c45fSAndroid Build Coastguard Worker // the entire push list with null. This is safe, since the only other reader (push)
35*38e8c45fSAndroid Build Coastguard Worker // of mPush will retry if it changes in between it's read and atomic compare. We
36*38e8c45fSAndroid Build Coastguard Worker // then store the list and pop one element.
37*38e8c45fSAndroid Build Coastguard Worker //
38*38e8c45fSAndroid Build Coastguard Worker // If we already had something in the pop list we just pop directly.
39*38e8c45fSAndroid Build Coastguard Worker template <typename T>
40*38e8c45fSAndroid Build Coastguard Worker class LocklessQueue {
41*38e8c45fSAndroid Build Coastguard Worker public:
isEmpty()42*38e8c45fSAndroid Build Coastguard Worker     bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }
43*38e8c45fSAndroid Build Coastguard Worker 
push(T value)44*38e8c45fSAndroid Build Coastguard Worker     void push(T value) {
45*38e8c45fSAndroid Build Coastguard Worker         Entry* entry = new Entry(std::move(value));
46*38e8c45fSAndroid Build Coastguard Worker         Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
47*38e8c45fSAndroid Build Coastguard Worker         do {
48*38e8c45fSAndroid Build Coastguard Worker             entry->mNext = previousHead;
49*38e8c45fSAndroid Build Coastguard Worker         } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
50*38e8c45fSAndroid Build Coastguard Worker     }
51*38e8c45fSAndroid Build Coastguard Worker 
pop()52*38e8c45fSAndroid Build Coastguard Worker     std::optional<T> pop() {
53*38e8c45fSAndroid Build Coastguard Worker         Entry* popped = mPop.load(/*std::memory_order_acquire*/);
54*38e8c45fSAndroid Build Coastguard Worker         if (popped) {
55*38e8c45fSAndroid Build Coastguard Worker             // Single consumer so this is fine
56*38e8c45fSAndroid Build Coastguard Worker             mPop.store(popped->mNext /* , std::memory_order_release */);
57*38e8c45fSAndroid Build Coastguard Worker             auto value = std::move(popped->mValue);
58*38e8c45fSAndroid Build Coastguard Worker             delete popped;
59*38e8c45fSAndroid Build Coastguard Worker             return value;
60*38e8c45fSAndroid Build Coastguard Worker         } else {
61*38e8c45fSAndroid Build Coastguard Worker             Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */);
62*38e8c45fSAndroid Build Coastguard Worker             if (!grabbedList) return std::nullopt;
63*38e8c45fSAndroid Build Coastguard Worker             // Reverse the list
64*38e8c45fSAndroid Build Coastguard Worker             while (grabbedList->mNext) {
65*38e8c45fSAndroid Build Coastguard Worker                 Entry* next = grabbedList->mNext;
66*38e8c45fSAndroid Build Coastguard Worker                 grabbedList->mNext = popped;
67*38e8c45fSAndroid Build Coastguard Worker                 popped = grabbedList;
68*38e8c45fSAndroid Build Coastguard Worker                 grabbedList = next;
69*38e8c45fSAndroid Build Coastguard Worker             }
70*38e8c45fSAndroid Build Coastguard Worker             mPop.store(popped /* , std::memory_order_release */);
71*38e8c45fSAndroid Build Coastguard Worker             auto value = std::move(grabbedList->mValue);
72*38e8c45fSAndroid Build Coastguard Worker             delete grabbedList;
73*38e8c45fSAndroid Build Coastguard Worker             return value;
74*38e8c45fSAndroid Build Coastguard Worker         }
75*38e8c45fSAndroid Build Coastguard Worker     }
76*38e8c45fSAndroid Build Coastguard Worker 
77*38e8c45fSAndroid Build Coastguard Worker private:
78*38e8c45fSAndroid Build Coastguard Worker     class Entry {
79*38e8c45fSAndroid Build Coastguard Worker     public:
80*38e8c45fSAndroid Build Coastguard Worker         T mValue;
81*38e8c45fSAndroid Build Coastguard Worker         std::atomic<Entry*> mNext;
Entry(T value)82*38e8c45fSAndroid Build Coastguard Worker         Entry(T value) : mValue(value) {}
83*38e8c45fSAndroid Build Coastguard Worker     };
84*38e8c45fSAndroid Build Coastguard Worker     std::atomic<Entry*> mPush = nullptr;
85*38e8c45fSAndroid Build Coastguard Worker     std::atomic<Entry*> mPop = nullptr;
86*38e8c45fSAndroid Build Coastguard Worker };
87