1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkSemaphore_DEFINED 9 #define SkSemaphore_DEFINED 10 11 #include "../private/SkOnce.h" 12 #include "SkTypes.h" 13 #include <atomic> 14 15 class SkBaseSemaphore { 16 public: 17 constexpr SkBaseSemaphore(int count = 0) 18 : fCount(count), fOSSemaphore(nullptr) {} 19 20 // Increment the counter n times. 21 // Generally it's better to call signal(n) instead of signal() n times. 22 void signal(int n = 1); 23 24 // Decrement the counter by 1, 25 // then if the counter is < 0, sleep this thread until the counter is >= 0. 26 void wait(); 27 28 // If the counter is positive, decrement it by 1 and return true, otherwise return false. 29 bool try_wait(); 30 31 // SkBaseSemaphore has no destructor. Call this to clean it up. 32 void cleanup(); 33 34 private: 35 // This implementation follows the general strategy of 36 // 'A Lightweight Semaphore with Partial Spinning' 37 // found here 38 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ 39 // That article (and entire blog) are very much worth reading. 40 // 41 // We wrap an OS-provided semaphore with a user-space atomic counter that 42 // lets us avoid interacting with the OS semaphore unless strictly required: 43 // moving the count from >=0 to <0 or vice-versa, i.e. sleeping or waking threads. 44 struct OSSemaphore; 45 46 void osSignal(int n); 47 void osWait(); 48 49 std::atomic<int> fCount; 50 SkOnce fOSSemaphoreOnce; 51 OSSemaphore* fOSSemaphore; 52 }; 53 54 class SkSemaphore : public SkBaseSemaphore { 55 public: 56 using SkBaseSemaphore::SkBaseSemaphore; 57 ~SkSemaphore() { this->cleanup(); } 58 }; 59 60 inline void SkBaseSemaphore::signal(int n) { 61 int prev = fCount.fetch_add(n, std::memory_order_release); 62 63 // We only want to call the OS semaphore when our logical count crosses 64 // from <0 to >=0 (when we need to wake sleeping threads). 65 // 66 // This is easiest to think about with specific examples of prev and n. 67 // If n == 5 and prev == -3, there are 3 threads sleeping and we signal 68 // SkTMin(-(-3), 5) == 3 times on the OS semaphore, leaving the count at 2. 69 // 70 // If prev >= 0, no threads are waiting, SkTMin(-prev, n) is always <= 0, 71 // so we don't call the OS semaphore, leaving the count at (prev + n). 72 int toSignal = SkTMin(-prev, n); 73 if (toSignal > 0) { 74 this->osSignal(toSignal); 75 } 76 } 77 78 inline void SkBaseSemaphore::wait() { 79 // Since this fetches the value before the subtract, zero and below means that there are no 80 // resources left, so the thread needs to wait. 81 if (fCount.fetch_sub(1, std::memory_order_acquire) <= 0) { 82 this->osWait(); 83 } 84 } 85 86 #endif//SkSemaphore_DEFINED 87