OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #ifndef SkSemaphore_DEFINED | 8 #ifndef SkSemaphore_DEFINED |
9 #define SkSemaphore_DEFINED | 9 #define SkSemaphore_DEFINED |
10 | 10 |
11 #include "SkTypes.h" | 11 #include "SkTypes.h" |
12 #include "SkAtomics.h" | 12 #include "SkAtomics.h" |
| 13 #include "../private/SkOncePtr.h" |
| 14 |
| 15 struct SkBaseSemaphore { |
| 16 |
| 17 // Increment the counter by 1. |
| 18 // This is a specialization for supporting SkMutex. |
| 19 void signal() { |
| 20 // Since this fetches the value before the add, 0 indicates that this th
read is running and |
| 21 // no threads are waiting, -1 and below means that threads are waiting,
but only signal 1 |
| 22 // thread to run. |
| 23 if (sk_atomic_fetch_add(&fCount, 1, sk_memory_order_release) < 0) { |
| 24 this->osSignal(1); |
| 25 } |
| 26 } |
| 27 |
| 28 // Increment the counter N times. |
| 29 // Generally it's better to call signal(N) instead of signal() N times. |
| 30 void signal(int N); |
| 31 |
| 32 // Decrement the counter by 1, |
| 33 // then if the counter is <= 0, sleep this thread until the counter is > 0. |
| 34 void wait() { |
| 35 // Since this fetches the value before the subtract, zero and below mean
s that there are no |
| 36 // resources left, so the thread needs to wait. |
| 37 if (sk_atomic_fetch_sub(&fCount, 1, sk_memory_order_acquire) <= 0) { |
| 38 this->osWait(); |
| 39 } |
| 40 } |
| 41 |
| 42 struct OSSemaphore; |
| 43 |
| 44 void osSignal(int n); |
| 45 void osWait(); |
| 46 void deleteSemaphore(); |
| 47 |
| 48 // This implementation follows the general strategy of |
| 49 // 'A Lightweight Semaphore with Partial Spinning' |
| 50 // found here |
| 51 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ |
| 52 // That article (and entire blog) are very much worth reading. |
| 53 // |
| 54 // We wrap an OS-provided semaphore with a user-space atomic counter that |
| 55 // lets us avoid interacting with the OS semaphore unless strictly required: |
| 56 // moving the count from >0 to <=0 or vice-versa, i.e. sleeping or waking th
reads. |
| 57 int fCount; |
| 58 SkBaseOncePtr<OSSemaphore> fOSSemaphore; |
| 59 }; |
13 | 60 |
14 /** | 61 /** |
15 * SkSemaphore is a fast mostly-user-space semaphore. | 62 * SkSemaphore is a fast mostly-user-space semaphore. |
16 * | 63 * |
17 * A semaphore is logically an atomic integer with a few special properties: | 64 * A semaphore is logically an atomic integer with a few special properties: |
18 * - The integer always starts at 0. | 65 * - The integer always starts at 0. |
19 * - You can only increment or decrement it, never read or write it. | 66 * - You can only increment or decrement it, never read or write it. |
20 * - Increment is spelled 'signal()'; decrement is spelled 'wait()'. | 67 * - Increment is spelled 'signal()'; decrement is spelled 'wait()'. |
21 * - If a call to wait() decrements the counter to <= 0, | 68 * - If a call to wait() decrements the counter to <= 0, |
22 * the calling thread sleeps until another thread signal()s it back above 0. | 69 * the calling thread sleeps until another thread signal()s it back above 0. |
23 */ | 70 */ |
24 class SkSemaphore : SkNoncopyable { | 71 class SkSemaphore : SkNoncopyable { |
25 public: | 72 public: |
26 // Initializes the counter to 0. | 73 // Initializes the counter to 0. |
27 // (Though all current implementations could start from an arbitrary value.) | 74 // (Though all current implementations could start from an arbitrary value.) |
28 SkSemaphore(); | 75 SkSemaphore(); |
29 ~SkSemaphore(); | 76 ~SkSemaphore(); |
30 | 77 |
31 // Increment the counter N times. | |
32 // Generally it's better to call signal(N) instead of signal() N times. | |
33 void signal(int N = 1); | |
34 | |
35 // Decrement the counter by 1, | |
36 // then if the counter is <= 0, sleep this thread until the counter is > 0. | |
37 void wait(); | 78 void wait(); |
38 | 79 |
| 80 void signal(int n = 1); |
| 81 |
39 private: | 82 private: |
40 // This implementation follows the general strategy of | 83 SkBaseSemaphore fBaseSemaphore; |
41 // 'A Lightweight Semaphore with Partial Spinning' | |
42 // found here | |
43 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ | |
44 // That article (and entire blog) are very much worth reading. | |
45 // | |
46 // We wrap an OS-provided semaphore with a user-space atomic counter that | |
47 // lets us avoid interacting with the OS semaphore unless strictly required: | |
48 // moving the count from >0 to <=0 or vice-versa, i.e. sleeping or waking th
reads. | |
49 struct OSSemaphore; | |
50 | |
51 SkAtomic<int> fCount; | |
52 OSSemaphore* fOSSemaphore; | |
53 }; | 84 }; |
54 | 85 |
55 #endif//SkSemaphore_DEFINED | 86 #endif//SkSemaphore_DEFINED |
OLD | NEW |