Index: include/private/SkSemaphore.h |
diff --git a/include/private/SkSemaphore.h b/include/private/SkSemaphore.h |
index cb2f58da4a27724fa49a1be40080a67ee6c158fd..3da2b99ab4a9f6bf4dd812633f159c04ef729af5 100644 |
--- a/include/private/SkSemaphore.h |
+++ b/include/private/SkSemaphore.h |
@@ -8,43 +8,27 @@ |
#ifndef SkSemaphore_DEFINED |
#define SkSemaphore_DEFINED |
+#include "../private/SkOnce.h" |
#include "SkTypes.h" |
-#include "../private/SkAtomics.h" |
-#include "../private/SkOncePtr.h" |
+#include <atomic> |
-struct SkBaseSemaphore { |
- |
- // Increment the counter by 1. |
- // This is a specialization for supporting SkMutex. |
- void signal() { |
- // Since this fetches the value before the add, 0 indicates that this thread is running and |
- // no threads are waiting, -1 and below means that threads are waiting, but only signal 1 |
- // thread to run. |
- if (sk_atomic_fetch_add(&fCount, 1, sk_memory_order_release) < 0) { |
- this->osSignal(1); |
- } |
- } |
+class SkBaseSemaphore { |
+public: |
+ constexpr SkBaseSemaphore(int count = 0) |
+ : fCount(count), fOSSemaphore(nullptr) {} |
- // Increment the counter N times. |
- // Generally it's better to call signal(N) instead of signal() N times. |
- void signal(int N); |
+ // Increment the counter n times. |
+ // Generally it's better to call signal(n) instead of signal() n times. |
+ void signal(int n = 1); |
// Decrement the counter by 1, |
// then if the counter is <= 0, sleep this thread until the counter is > 0. |
- void wait() { |
- // Since this fetches the value before the subtract, zero and below means that there are no |
- // resources left, so the thread needs to wait. |
- if (sk_atomic_fetch_sub(&fCount, 1, sk_memory_order_acquire) <= 0) { |
- this->osWait(); |
- } |
- } |
- |
- struct OSSemaphore; |
+ void wait(); |
- void osSignal(int n); |
- void osWait(); |
- void deleteSemaphore(); |
+ // SkBaseSemaphore has no destructor. Call this to clean it up. |
+ void cleanup(); |
+private: |
// This implementation follows the general strategy of |
// 'A Lightweight Semaphore with Partial Spinning' |
// found here |
@@ -54,33 +38,46 @@ struct SkBaseSemaphore { |
// We wrap an OS-provided semaphore with a user-space atomic counter that |
// lets us avoid interacting with the OS semaphore unless strictly required: |
// moving the count from >0 to <=0 or vice-versa, i.e. sleeping or waking threads. |
- int fCount; |
- SkBaseOncePtr<OSSemaphore> fOSSemaphore; |
+ struct OSSemaphore; |
+ |
+ void osSignal(int n); |
+ void osWait(); |
+ |
+ std::atomic<int> fCount; |
+ SkOnce fOSSemaphoreOnce; |
+ OSSemaphore* fOSSemaphore; |
}; |
-/** |
- * SkSemaphore is a fast mostly-user-space semaphore. |
- * |
- * A semaphore is logically an atomic integer with a few special properties: |
- * - The integer always starts at 0. |
- * - You can only increment or decrement it, never read or write it. |
- * - Increment is spelled 'signal()'; decrement is spelled 'wait()'. |
- * - If a call to wait() decrements the counter to <= 0, |
- * the calling thread sleeps until another thread signal()s it back above 0. |
- */ |
-class SkSemaphore : SkNoncopyable { |
+class SkSemaphore : public SkBaseSemaphore { |
public: |
- // Initializes the counter to 0. |
- // (Though all current implementations could start from an arbitrary value.) |
- SkSemaphore(); |
- ~SkSemaphore(); |
+ using SkBaseSemaphore::SkBaseSemaphore; |
+ ~SkSemaphore() { this->cleanup(); } |
+}; |
- void wait(); |
+inline void SkBaseSemaphore::signal(int n) { |
+ int prev = fCount.fetch_add(n, std::memory_order_release); |
- void signal(int n = 1); |
+ // We only want to call the OS semaphore when our logical count crosses |
+ // from <= 0 to >0 (when we need to wake sleeping threads). |
+ // |
+ // This is easiest to think about with specific examples of prev and n. |
+ // If n == 5 and prev == -3, there are 3 threads sleeping and we signal |
+ // SkTMin(-(-3), 5) == 3 times on the OS semaphore, leaving the count at 2. |
+ // |
+ // If prev >= 0, no threads are waiting, SkTMin(-prev, n) is always <= 0, |
+ // so we don't call the OS semaphore, leaving the count at (prev + n). |
+ int toSignal = SkTMin(-prev, n); |
+ if (toSignal > 0) { |
+ this->osSignal(toSignal); |
+ } |
+} |
-private: |
- SkBaseSemaphore fBaseSemaphore; |
-}; |
+inline void SkBaseSemaphore::wait() { |
+ // Since this fetches the value before the subtract, zero and below means that there are no |
+ // resources left, so the thread needs to wait. |
+ if (fCount.fetch_sub(1, std::memory_order_acquire) <= 0) { |
+ this->osWait(); |
+ } |
+} |
#endif//SkSemaphore_DEFINED |