Index: include/private/SkSemaphore.h |
diff --git a/include/private/SkSemaphore.h b/include/private/SkSemaphore.h |
index adee574bb76c9c1398c4c0528f2cde9d4d39172e..cb2f58da4a27724fa49a1be40080a67ee6c158fd 100644 |
--- a/include/private/SkSemaphore.h |
+++ b/include/private/SkSemaphore.h |
@@ -8,26 +8,43 @@ |
#ifndef SkSemaphore_DEFINED |
#define SkSemaphore_DEFINED |
-#include "../private/SkOnce.h" |
#include "SkTypes.h" |
-#include <atomic> |
+#include "../private/SkAtomics.h" |
+#include "../private/SkOncePtr.h" |
-class SkSemaphore { |
-public: |
- constexpr SkSemaphore(int count = 0) |
- : fCount(count), fOSSemaphore(nullptr) {} |
+struct SkBaseSemaphore { |
- ~SkSemaphore(); |
+ // 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); |
+ } |
+ } |
- // Increment the counter n times. |
- // Generally it's better to call signal(n) instead of signal() n times. |
- void signal(int n = 1); |
+ // Increment the counter N times. |
+ // Generally it's better to call signal(N) instead of signal() N times. |
+ void signal(int N); |
// Decrement the counter by 1, |
// then if the counter is <= 0, sleep this thread until the counter is > 0. |
- void wait(); |
+ 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(); |
+ } |
+ } |
-private: |
+ struct OSSemaphore; |
+ |
+ void osSignal(int n); |
+ void osWait(); |
+ void deleteSemaphore(); |
+ |
// This implementation follows the general strategy of |
// 'A Lightweight Semaphore with Partial Spinning' |
// found here |
@@ -37,40 +54,33 @@ |
// 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. |
- struct OSSemaphore; |
- |
- void osSignal(int n); |
- void osWait(); |
- |
- std::atomic<int> fCount; |
- SkOnce fOSSemaphoreOnce; |
- OSSemaphore* fOSSemaphore; |
+ int fCount; |
+ SkBaseOncePtr<OSSemaphore> fOSSemaphore; |
}; |
-inline void SkSemaphore::signal(int n) { |
- int prev = fCount.fetch_add(n, std::memory_order_release); |
+/** |
+ * 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 { |
+public: |
+ // Initializes the counter to 0. |
+ // (Though all current implementations could start from an arbitrary value.) |
+ SkSemaphore(); |
+ ~SkSemaphore(); |
- // 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); |
- } |
-} |
+ void wait(); |
-inline void SkSemaphore::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(); |
- } |
-} |
+ void signal(int n = 1); |
+ |
+private: |
+ SkBaseSemaphore fBaseSemaphore; |
+}; |
#endif//SkSemaphore_DEFINED |