| 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 |