diff options
author | sclittle <sclittle@chromium.org> | 2016-05-04 18:23:30 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-05-04 18:23:30 -0700 |
commit | d9f5d20f81b15190fa858eda1f536deae21e8e78 (patch) | |
tree | be843fd0a48d47d9ed0d2cc7ef61a4821208c33a | |
parent | 7eb33da7eede34c050b865dbb1b60c3dcea7191b (diff) |
Revert of Modernize SkMutex and SkSemaphore. (patchset #2 id:20001 of https://codereview.chromium.org/1947153002/ )
Reason for revert:
This CL seems to have broken the Linux x64 and Mac
bots, e.g.
https://build.chromium.org/p/chromium/builders/Linux%20x64/builds/19052
https://build.chromium.org/p/chromium/builders/Mac/builds/15151
The error appears to have something to do with new static initializers being
added.
Original issue's description:
> Modernize SkMutex and SkSemaphore.
>
> - use <atomic>
> - fuse SkMutex and SkBaseMutex
> - fuse SkSemaphore and SkBaseSemaphore
>
> Still TODO:
> - replace SK_DECLARE_STATIC_MUTEX(name) with static SkMutex name
>
> I just didn't want to bother fixing all that up until I know this CL sticks.
>
> BUG=skia:
> GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1947153002
>
> No public API changes.
> TBR=reed@google.com
>
> Committed: https://skia.googlesource.com/skia/+/427c2819d9237d7d7729c59238036cfc73c072ea
TBR=herb@google.com,mtklein@chromium.org,reed@google.com,bsalomon@google.com
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=609340
Review-Url: https://codereview.chromium.org/1945343003
-rw-r--r-- | include/core/SkPixelRef.h | 2 | ||||
-rw-r--r-- | include/ports/SkFontConfigInterface.h | 1 | ||||
-rw-r--r-- | include/private/SkMutex.h | 56 | ||||
-rw-r--r-- | include/private/SkSemaphore.h | 96 | ||||
-rw-r--r-- | src/core/SkSemaphore.cpp | 49 | ||||
-rw-r--r-- | src/lazy/SkDiscardableMemoryPool.cpp | 13 | ||||
-rw-r--r-- | src/lazy/SkDiscardableMemoryPool.h | 3 |
7 files changed, 142 insertions, 78 deletions
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h index d836b2fd7c..90e0de540d 100644 --- a/include/core/SkPixelRef.h +++ b/include/core/SkPixelRef.h @@ -342,7 +342,7 @@ protected: /** Return the mutex associated with this pixelref. This value is assigned in the constructor, and cannot change during the lifetime of the object. */ - SkMutex* mutex() const { return &fMutex; } + SkBaseMutex* mutex() const { return &fMutex; } // only call from constructor. Flags this to always be locked, removing // the need to grab the mutex and call onLockPixels/onUnlockPixels. diff --git a/include/ports/SkFontConfigInterface.h b/include/ports/SkFontConfigInterface.h index 9f98e356d2..72cad0ad6b 100644 --- a/include/ports/SkFontConfigInterface.h +++ b/include/ports/SkFontConfigInterface.h @@ -13,6 +13,7 @@ #include "SkRefCnt.h" #include "SkTypeface.h" +struct SkBaseMutex; class SkFontMgr; /** diff --git a/include/private/SkMutex.h b/include/private/SkMutex.h index b9af00f8a8..8c78e1205c 100644 --- a/include/private/SkMutex.h +++ b/include/private/SkMutex.h @@ -8,20 +8,33 @@ #ifndef SkMutex_DEFINED #define SkMutex_DEFINED +// This file is not part of the public Skia API. #include "../private/SkSemaphore.h" -#include "../private/SkThreadID.h" #include "SkTypes.h" -// TODO: no need for this anymore. -#define SK_DECLARE_STATIC_MUTEX(name) static SkMutex name; - -class SkMutex { -public: - constexpr SkMutex() = default; - - SkMutex(const SkMutex&) = delete; - SkMutex& operator=(const SkMutex&) = delete; - +#ifdef SK_DEBUG + #include "../private/SkThreadID.h" +#endif + +#define SK_MUTEX_SEMAPHORE_INIT {1, {0}} + +#ifdef SK_DEBUG + #define SK_BASE_MUTEX_INIT {SK_MUTEX_SEMAPHORE_INIT, 0} +#else + #define SK_BASE_MUTEX_INIT {SK_MUTEX_SEMAPHORE_INIT} +#endif + +// Using POD-style initialization prevents the generation of a static initializer. +// +// Without magic statics there are no thread safety guarantees on initialization +// of local statics (even POD). As a result, it is illegal to use +// SK_DECLARE_STATIC_MUTEX in a function. +// +// Because SkBaseMutex is not a primitive, a static SkBaseMutex cannot be +// initialized in a class with this macro. +#define SK_DECLARE_STATIC_MUTEX(name) namespace {} static SkBaseMutex name = SK_BASE_MUTEX_INIT; + +struct SkBaseMutex { void acquire() { fSemaphore.wait(); SkDEBUGCODE(fOwner = SkGetThreadID();) @@ -37,9 +50,20 @@ public: SkASSERT(fOwner == SkGetThreadID()); } -private: - SkSemaphore fSemaphore{1}; - SkDEBUGCODE(SkThreadID fOwner{kIllegalThreadID};) + SkBaseSemaphore fSemaphore; + SkDEBUGCODE(SkThreadID fOwner;) +}; + +// This needs to use subclassing instead of encapsulation to make SkAutoMutexAcquire to work. +class SkMutex : public SkBaseMutex { +public: + SkMutex () { + fSemaphore = SK_MUTEX_SEMAPHORE_INIT; + SkDEBUGCODE(fOwner = kIllegalThreadID); + } + ~SkMutex () { fSemaphore.deleteSemaphore(); } + SkMutex(const SkMutex&) = delete; + SkMutex& operator=(const SkMutex&) = delete; }; template <typename Lock> @@ -92,10 +116,10 @@ private: Lock &fLock; }; -typedef SkAutoTAcquire<SkMutex> SkAutoMutexAcquire; +typedef SkAutoTAcquire<SkBaseMutex> SkAutoMutexAcquire; #define SkAutoMutexAcquire(...) SK_REQUIRE_LOCAL_VAR(SkAutoMutexAcquire) -typedef SkAutoTExclusive<SkMutex> SkAutoMutexExclusive; +typedef SkAutoTExclusive<SkBaseMutex> SkAutoMutexExclusive; #define SkAutoMutexExclusive(...) SK_REQUIRE_LOCAL_VAR(SkAutoMutexExclusive) #endif//SkMutex_DEFINED diff --git a/include/private/SkSemaphore.h b/include/private/SkSemaphore.h index adee574bb7..cb2f58da4a 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(); + } + } + + struct OSSemaphore; + + void osSignal(int n); + void osWait(); + void deleteSemaphore(); -private: // This implementation follows the general strategy of // 'A Lightweight Semaphore with Partial Spinning' // found here @@ -37,40 +54,33 @@ private: // 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 diff --git a/src/core/SkSemaphore.cpp b/src/core/SkSemaphore.cpp index 0646b152e8..da422e282f 100644 --- a/src/core/SkSemaphore.cpp +++ b/src/core/SkSemaphore.cpp @@ -9,7 +9,7 @@ #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) #include <mach/mach.h> - struct SkSemaphore::OSSemaphore { + struct SkBaseSemaphore::OSSemaphore { semaphore_t fSemaphore; OSSemaphore() { @@ -21,7 +21,7 @@ void wait() { semaphore_wait(fSemaphore); } }; #elif defined(SK_BUILD_FOR_WIN32) - struct SkSemaphore::OSSemaphore { + struct SkBaseSemaphore::OSSemaphore { HANDLE fSemaphore; OSSemaphore() { @@ -41,7 +41,7 @@ // It's important we test for Mach before this. This code will compile but not work there. #include <errno.h> #include <semaphore.h> - struct SkSemaphore::OSSemaphore { + struct SkBaseSemaphore::OSSemaphore { sem_t fSemaphore; OSSemaphore() { sem_init(&fSemaphore, 0/*cross process?*/, 0/*initial count*/); } @@ -57,16 +57,43 @@ /////////////////////////////////////////////////////////////////////////////// -void SkSemaphore::osSignal(int n) { - fOSSemaphoreOnce([this] { fOSSemaphore = new OSSemaphore; }); - fOSSemaphore->signal(n); +void SkBaseSemaphore::signal(int n) { + SkASSERT(n >= 0); + + // 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 prev = sk_atomic_fetch_add(&fCount, n, sk_memory_order_release); + int toSignal = SkTMin(-prev, n); + if (toSignal > 0) { + this->osSignal(toSignal); + } } -void SkSemaphore::osWait() { - fOSSemaphoreOnce([this] { fOSSemaphore = new OSSemaphore; }); - fOSSemaphore->wait(); +static SkBaseSemaphore::OSSemaphore* semaphore(SkBaseSemaphore* semaphore) { + return semaphore->fOSSemaphore.get([](){ return new SkBaseSemaphore::OSSemaphore(); }); } -SkSemaphore::~SkSemaphore() { - delete fOSSemaphore; +void SkBaseSemaphore::osSignal(int n) { semaphore(this)->signal(n); } + +void SkBaseSemaphore::osWait() { semaphore(this)->wait(); } + +void SkBaseSemaphore::deleteSemaphore() { + delete (OSSemaphore*) fOSSemaphore; } + +/////////////////////////////////////////////////////////////////////////////// + +SkSemaphore::SkSemaphore(){ fBaseSemaphore = {0, {0}}; } + +SkSemaphore::~SkSemaphore() { fBaseSemaphore.deleteSemaphore(); } + +void SkSemaphore::wait() { fBaseSemaphore.wait(); } + +void SkSemaphore::signal(int n) {fBaseSemaphore.signal(n); } diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp index 2be4c755f1..1f3bcf93e9 100644 --- a/src/lazy/SkDiscardableMemoryPool.cpp +++ b/src/lazy/SkDiscardableMemoryPool.cpp @@ -29,7 +29,7 @@ public: /** * Without mutex, will be not be thread safe. */ - DiscardableMemoryPool(size_t budget, SkMutex* mutex = nullptr); + DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = nullptr); virtual ~DiscardableMemoryPool(); SkDiscardableMemory* create(size_t bytes) override; @@ -52,9 +52,9 @@ public: #endif // SK_LAZY_CACHE_STATS private: - SkMutex* fMutex; - size_t fBudget; - size_t fUsed; + SkBaseMutex* fMutex; + size_t fBudget; + size_t fUsed; SkTInternalLList<PoolDiscardableMemory> fList; /** Function called to free memory if needed */ @@ -128,7 +128,8 @@ void PoolDiscardableMemory::unlock() { //////////////////////////////////////////////////////////////////////////////// -DiscardableMemoryPool::DiscardableMemoryPool(size_t budget, SkMutex* mutex) +DiscardableMemoryPool::DiscardableMemoryPool(size_t budget, + SkBaseMutex* mutex) : fMutex(mutex) , fBudget(budget) , fUsed(0) { @@ -240,7 +241,7 @@ void DiscardableMemoryPool::dumpPool() { } // namespace -SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkMutex* mutex) { +SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) { return new DiscardableMemoryPool(size, mutex); } diff --git a/src/lazy/SkDiscardableMemoryPool.h b/src/lazy/SkDiscardableMemoryPool.h index ad8d796f66..92ba48bcb4 100644 --- a/src/lazy/SkDiscardableMemoryPool.h +++ b/src/lazy/SkDiscardableMemoryPool.h @@ -52,7 +52,8 @@ public: * the pool works. * Without mutex, will be not be thread safe. */ - static SkDiscardableMemoryPool* Create(size_t size, SkMutex* mutex = nullptr); + static SkDiscardableMemoryPool* Create( + size_t size, SkBaseMutex* mutex = nullptr); }; /** |