Coverage Report

Created: 2024-05-20 07:14

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