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