xref: /aosp_15_r20/external/skia/include/private/base/SkSemaphore.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef SkSemaphore_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define SkSemaphore_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAPI.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkOnce.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkThreadAnnotations.h"
14*c8dee2aaSAndroid Build Coastguard Worker 
15*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
16*c8dee2aaSAndroid Build Coastguard Worker #include <atomic>
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker class SkSemaphore {
19*c8dee2aaSAndroid Build Coastguard Worker public:
fCount(count)20*c8dee2aaSAndroid Build Coastguard Worker     constexpr SkSemaphore(int count = 0) : fCount(count), fOSSemaphore(nullptr) {}
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker     // Cleanup the underlying OS semaphore.
23*c8dee2aaSAndroid Build Coastguard Worker     SK_SPI ~SkSemaphore();
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker     // Increment the counter n times.
26*c8dee2aaSAndroid Build Coastguard Worker     // Generally it's better to call signal(n) instead of signal() n times.
27*c8dee2aaSAndroid Build Coastguard Worker     void signal(int n = 1);
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker     // Decrement the counter by 1,
30*c8dee2aaSAndroid Build Coastguard Worker     // then if the counter is < 0, sleep this thread until the counter is >= 0.
31*c8dee2aaSAndroid Build Coastguard Worker     void wait();
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker     // If the counter is positive, decrement it by 1 and return true, otherwise return false.
34*c8dee2aaSAndroid Build Coastguard Worker     SK_SPI bool try_wait();
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker private:
37*c8dee2aaSAndroid Build Coastguard Worker     // This implementation follows the general strategy of
38*c8dee2aaSAndroid Build Coastguard Worker     //     'A Lightweight Semaphore with Partial Spinning'
39*c8dee2aaSAndroid Build Coastguard Worker     // found here
40*c8dee2aaSAndroid Build Coastguard Worker     //     http://preshing.com/20150316/semaphores-are-surprisingly-versatile/
41*c8dee2aaSAndroid Build Coastguard Worker     // That article (and entire blog) are very much worth reading.
42*c8dee2aaSAndroid Build Coastguard Worker     //
43*c8dee2aaSAndroid Build Coastguard Worker     // We wrap an OS-provided semaphore with a user-space atomic counter that
44*c8dee2aaSAndroid Build Coastguard Worker     // lets us avoid interacting with the OS semaphore unless strictly required:
45*c8dee2aaSAndroid Build Coastguard Worker     // moving the count from >=0 to <0 or vice-versa, i.e. sleeping or waking threads.
46*c8dee2aaSAndroid Build Coastguard Worker     struct OSSemaphore;
47*c8dee2aaSAndroid Build Coastguard Worker 
48*c8dee2aaSAndroid Build Coastguard Worker     SK_SPI void osSignal(int n);
49*c8dee2aaSAndroid Build Coastguard Worker     SK_SPI void osWait();
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker     std::atomic<int> fCount;
52*c8dee2aaSAndroid Build Coastguard Worker     SkOnce           fOSSemaphoreOnce;
53*c8dee2aaSAndroid Build Coastguard Worker     OSSemaphore*     fOSSemaphore;
54*c8dee2aaSAndroid Build Coastguard Worker };
55*c8dee2aaSAndroid Build Coastguard Worker 
signal(int n)56*c8dee2aaSAndroid Build Coastguard Worker inline void SkSemaphore::signal(int n) {
57*c8dee2aaSAndroid Build Coastguard Worker     int prev = fCount.fetch_add(n, std::memory_order_release);
58*c8dee2aaSAndroid Build Coastguard Worker 
59*c8dee2aaSAndroid Build Coastguard Worker     // We only want to call the OS semaphore when our logical count crosses
60*c8dee2aaSAndroid Build Coastguard Worker     // from <0 to >=0 (when we need to wake sleeping threads).
61*c8dee2aaSAndroid Build Coastguard Worker     //
62*c8dee2aaSAndroid Build Coastguard Worker     // This is easiest to think about with specific examples of prev and n.
63*c8dee2aaSAndroid Build Coastguard Worker     // If n == 5 and prev == -3, there are 3 threads sleeping and we signal
64*c8dee2aaSAndroid Build Coastguard Worker     // std::min(-(-3), 5) == 3 times on the OS semaphore, leaving the count at 2.
65*c8dee2aaSAndroid Build Coastguard Worker     //
66*c8dee2aaSAndroid Build Coastguard Worker     // If prev >= 0, no threads are waiting, std::min(-prev, n) is always <= 0,
67*c8dee2aaSAndroid Build Coastguard Worker     // so we don't call the OS semaphore, leaving the count at (prev + n).
68*c8dee2aaSAndroid Build Coastguard Worker     int toSignal = std::min(-prev, n);
69*c8dee2aaSAndroid Build Coastguard Worker     if (toSignal > 0) {
70*c8dee2aaSAndroid Build Coastguard Worker         this->osSignal(toSignal);
71*c8dee2aaSAndroid Build Coastguard Worker     }
72*c8dee2aaSAndroid Build Coastguard Worker }
73*c8dee2aaSAndroid Build Coastguard Worker 
wait()74*c8dee2aaSAndroid Build Coastguard Worker inline void SkSemaphore::wait() {
75*c8dee2aaSAndroid Build Coastguard Worker     // Since this fetches the value before the subtract, zero and below means that there are no
76*c8dee2aaSAndroid Build Coastguard Worker     // resources left, so the thread needs to wait.
77*c8dee2aaSAndroid Build Coastguard Worker     if (fCount.fetch_sub(1, std::memory_order_acquire) <= 0) {
78*c8dee2aaSAndroid Build Coastguard Worker         SK_POTENTIALLY_BLOCKING_REGION_BEGIN;
79*c8dee2aaSAndroid Build Coastguard Worker         this->osWait();
80*c8dee2aaSAndroid Build Coastguard Worker         SK_POTENTIALLY_BLOCKING_REGION_END;
81*c8dee2aaSAndroid Build Coastguard Worker     }
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker #endif//SkSemaphore_DEFINED
85