| 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 "../private/SkOnce.h" |
| 11 #include "SkTypes.h" | 12 #include "SkTypes.h" |
| 12 #include "../private/SkAtomics.h" | 13 #include <atomic> |
| 13 #include "../private/SkOncePtr.h" | |
| 14 | 14 |
| 15 struct SkBaseSemaphore { | 15 class SkBaseSemaphore { |
| 16 public: |
| 17 constexpr SkBaseSemaphore(int count = 0) |
| 18 : fCount(count), fOSSemaphore(nullptr) {} |
| 16 | 19 |
| 17 // Increment the counter by 1. | 20 // Increment the counter n times. |
| 18 // This is a specialization for supporting SkMutex. | 21 // Generally it's better to call signal(n) instead of signal() n times. |
| 19 void signal() { | 22 void signal(int n = 1); |
| 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 | 23 |
| 32 // Decrement the counter by 1, | 24 // Decrement the counter by 1, |
| 33 // then if the counter is <= 0, sleep this thread until the counter is > 0. | 25 // then if the counter is <= 0, sleep this thread until the counter is > 0. |
| 34 void wait() { | 26 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 | 27 |
| 42 struct OSSemaphore; | 28 // SkBaseSemaphore has no destructor. Call this to clean it up. |
| 29 void cleanup(); |
| 43 | 30 |
| 44 void osSignal(int n); | 31 private: |
| 45 void osWait(); | |
| 46 void deleteSemaphore(); | |
| 47 | |
| 48 // This implementation follows the general strategy of | 32 // This implementation follows the general strategy of |
| 49 // 'A Lightweight Semaphore with Partial Spinning' | 33 // 'A Lightweight Semaphore with Partial Spinning' |
| 50 // found here | 34 // found here |
| 51 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ | 35 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ |
| 52 // That article (and entire blog) are very much worth reading. | 36 // That article (and entire blog) are very much worth reading. |
| 53 // | 37 // |
| 54 // We wrap an OS-provided semaphore with a user-space atomic counter that | 38 // 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: | 39 // 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. | 40 // moving the count from >0 to <=0 or vice-versa, i.e. sleeping or waking th
reads. |
| 57 int fCount; | 41 struct OSSemaphore; |
| 58 SkBaseOncePtr<OSSemaphore> fOSSemaphore; | 42 |
| 43 void osSignal(int n); |
| 44 void osWait(); |
| 45 |
| 46 std::atomic<int> fCount; |
| 47 SkOnce fOSSemaphoreOnce; |
| 48 OSSemaphore* fOSSemaphore; |
| 59 }; | 49 }; |
| 60 | 50 |
| 61 /** | 51 class SkSemaphore : public SkBaseSemaphore { |
| 62 * SkSemaphore is a fast mostly-user-space semaphore. | |
| 63 * | |
| 64 * A semaphore is logically an atomic integer with a few special properties: | |
| 65 * - The integer always starts at 0. | |
| 66 * - You can only increment or decrement it, never read or write it. | |
| 67 * - Increment is spelled 'signal()'; decrement is spelled 'wait()'. | |
| 68 * - If a call to wait() decrements the counter to <= 0, | |
| 69 * the calling thread sleeps until another thread signal()s it back above 0. | |
| 70 */ | |
| 71 class SkSemaphore : SkNoncopyable { | |
| 72 public: | 52 public: |
| 73 // Initializes the counter to 0. | 53 using SkBaseSemaphore::SkBaseSemaphore; |
| 74 // (Though all current implementations could start from an arbitrary value.) | 54 ~SkSemaphore() { this->cleanup(); } |
| 75 SkSemaphore(); | |
| 76 ~SkSemaphore(); | |
| 77 | |
| 78 void wait(); | |
| 79 | |
| 80 void signal(int n = 1); | |
| 81 | |
| 82 private: | |
| 83 SkBaseSemaphore fBaseSemaphore; | |
| 84 }; | 55 }; |
| 85 | 56 |
| 57 inline void SkBaseSemaphore::signal(int n) { |
| 58 int prev = fCount.fetch_add(n, std::memory_order_release); |
| 59 |
| 60 // We only want to call the OS semaphore when our logical count crosses |
| 61 // from <= 0 to >0 (when we need to wake sleeping threads). |
| 62 // |
| 63 // This is easiest to think about with specific examples of prev and n. |
| 64 // If n == 5 and prev == -3, there are 3 threads sleeping and we signal |
| 65 // SkTMin(-(-3), 5) == 3 times on the OS semaphore, leaving the count at 2. |
| 66 // |
| 67 // If prev >= 0, no threads are waiting, SkTMin(-prev, n) is always <= 0, |
| 68 // so we don't call the OS semaphore, leaving the count at (prev + n). |
| 69 int toSignal = SkTMin(-prev, n); |
| 70 if (toSignal > 0) { |
| 71 this->osSignal(toSignal); |
| 72 } |
| 73 } |
| 74 |
| 75 inline void SkBaseSemaphore::wait() { |
| 76 // Since this fetches the value before the subtract, zero and below means th
at there are no |
| 77 // resources left, so the thread needs to wait. |
| 78 if (fCount.fetch_sub(1, std::memory_order_acquire) <= 0) { |
| 79 this->osWait(); |
| 80 } |
| 81 } |
| 82 |
| 86 #endif//SkSemaphore_DEFINED | 83 #endif//SkSemaphore_DEFINED |
| OLD | NEW |