diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/config/SkUserConfig.h | 10 | ||||
-rw-r--r-- | include/core/SkAtomics.h | 13 | ||||
-rw-r--r-- | include/core/SkBarriers.h | 15 | ||||
-rw-r--r-- | include/core/SkMutex.h | 13 | ||||
-rw-r--r-- | include/core/SkPostConfig.h | 28 | ||||
-rw-r--r-- | include/core/SkThread.h | 25 | ||||
-rw-r--r-- | include/core/SkThreadPriv.h | 8 | ||||
-rw-r--r-- | include/ports/SkAtomics_sync.h | 55 | ||||
-rw-r--r-- | include/ports/SkAtomics_win.h | 54 | ||||
-rw-r--r-- | include/ports/SkBarriers_arm.h | 36 | ||||
-rw-r--r-- | include/ports/SkBarriers_tsan.h | 31 | ||||
-rw-r--r-- | include/ports/SkBarriers_x86.h | 39 | ||||
-rw-r--r-- | include/ports/SkMutex_pthread.h | 96 | ||||
-rw-r--r-- | include/ports/SkMutex_win.h | 79 |
14 files changed, 439 insertions, 63 deletions
diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h index 5f59b9131c..92e2b90762 100644 --- a/include/config/SkUserConfig.h +++ b/include/config/SkUserConfig.h @@ -168,14 +168,4 @@ */ //#define SK_PDF_USE_PATHOPS -/* Skia uses these defines as the target of include preprocessor directives. - * The header files pointed to by these defines provide declarations and - * possibly inline implementations of threading primitives. - * - * See SkThread.h for documentation on what these includes must contain. - */ -//#define SK_ATOMICS_PLATFORM_H "SkAtomics_xxx.h" -//#define SK_MUTEX_PLATFORM_H "SkMutex_xxx.h" -//#define SK_BARRIERS_PLATFORM_H "SkBarriers_xxx.h" - #endif diff --git a/include/core/SkAtomics.h b/include/core/SkAtomics.h new file mode 100644 index 0000000000..ed19533163 --- /dev/null +++ b/include/core/SkAtomics.h @@ -0,0 +1,13 @@ +#ifndef SkAtomics_DEFINED +#define SkAtomics_DEFINED + +// This file is not part of the public Skia API. +#include "SkTypes.h" + +#if defined(_MSC_VER) + #include "../ports/SkAtomics_win.h" +#else + #include "../ports/SkAtomics_sync.h" +#endif + +#endif//SkAtomics_DEFINED diff --git a/include/core/SkBarriers.h b/include/core/SkBarriers.h new file mode 100644 index 0000000000..2067a829a3 --- /dev/null +++ b/include/core/SkBarriers.h @@ -0,0 +1,15 @@ +#ifndef SkBarriers_DEFINED +#define SkBarriers_DEFINED + +// This file is not part of the public Skia API. +#include "SkTypes.h" + +#if SK_HAS_COMPILER_FEATURE(thread_sanitizer) + #include "../ports/SkBarriers_tsan.h" +#elif defined(SK_CPU_ARM32) || defined(SK_CPU_ARM64) + #include "../ports/SkBarriers_arm.h" +#else + #include "../ports/SkBarriers_x86.h" +#endif + +#endif//SkBarriers_DEFINED diff --git a/include/core/SkMutex.h b/include/core/SkMutex.h new file mode 100644 index 0000000000..7dbe957d8b --- /dev/null +++ b/include/core/SkMutex.h @@ -0,0 +1,13 @@ +#ifndef SkMutex_DEFINED +#define SkMutex_DEFINED + +// This file is not part of the public Skia API. +#include "SkTypes.h" + +#if defined(SK_BUILD_FOR_WIN) + #include "../ports/SkMutex_win.h" +#else + #include "../ports/SkMutex_pthread.h" +#endif + +#endif//SkMutex_DEFINED diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h index 2d6ab44b65..de63c671bd 100644 --- a/include/core/SkPostConfig.h +++ b/include/core/SkPostConfig.h @@ -385,34 +385,6 @@ ////////////////////////////////////////////////////////////////////// -#ifndef SK_ATOMICS_PLATFORM_H -# if defined(_MSC_VER) -# define SK_ATOMICS_PLATFORM_H "../../src/ports/SkAtomics_win.h" -# else -# define SK_ATOMICS_PLATFORM_H "../../src/ports/SkAtomics_sync.h" -# endif -#endif - -#ifndef SK_MUTEX_PLATFORM_H -# if defined(SK_BUILD_FOR_WIN) -# define SK_MUTEX_PLATFORM_H "../../src/ports/SkMutex_win.h" -# else -# define SK_MUTEX_PLATFORM_H "../../src/ports/SkMutex_pthread.h" -# endif -#endif - -#ifndef SK_BARRIERS_PLATFORM_H -# if SK_HAS_COMPILER_FEATURE(thread_sanitizer) -# define SK_BARRIERS_PLATFORM_H "../../src/ports/SkBarriers_tsan.h" -# elif defined(SK_CPU_ARM32) || defined(SK_CPU_ARM64) -# define SK_BARRIERS_PLATFORM_H "../../src/ports/SkBarriers_arm.h" -# else -# define SK_BARRIERS_PLATFORM_H "../../src/ports/SkBarriers_x86.h" -# endif -#endif - -////////////////////////////////////////////////////////////////////// - #ifndef SK_EGL # if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_NACL) # define SK_EGL 1 diff --git a/include/core/SkThread.h b/include/core/SkThread.h index fffe787e71..8d7fb3912c 100644 --- a/include/core/SkThread.h +++ b/include/core/SkThread.h @@ -10,7 +10,7 @@ #include "SkTypes.h" -// SK_ATOMICS_PLATFORM_H must provide inline implementations for the following declarations. +// SkAtomics.h must provide inline implementations for the following declarations. /** Atomically adds one to the int referenced by addr and returns the previous value. * No additional memory barrier is required; this must act as a compiler barrier. @@ -44,11 +44,7 @@ static void sk_membar_acquire__after_atomic_dec(); */ static void sk_membar_acquire__after_atomic_conditional_inc(); -#ifdef GOOGLE3 - #include "SkAtomics_sync.h" -#else - #include SK_ATOMICS_PLATFORM_H -#endif +#include "SkAtomics.h" /** Atomically adds one to the int referenced by addr iff the referenced int was not 0 * and returns the previous value. @@ -65,7 +61,7 @@ template<typename INT_TYPE> static inline INT_TYPE sk_atomic_conditional_inc(INT return prev; } -// SK_BARRIERS_PLATFORM_H must provide implementations for the following declarations: +// SkBarriers.h must provide implementations for the following declarations: /** Prevent the compiler from reordering across this barrier. */ static void sk_compiler_barrier(); @@ -82,13 +78,9 @@ template <typename T> T sk_acquire_load(T*); */ template <typename T> void sk_release_store(T*, T); -#ifdef GOOGLE3 - #include "SkBarriers_x86.h" -#else - #include SK_BARRIERS_PLATFORM_H -#endif +#include "SkBarriers.h" -/** SK_MUTEX_PLATFORM_H must provide the following (or equivalent) declarations. +/** SkMutex.h must provide the following (or equivalent) declarations. class SkBaseMutex { public: @@ -106,12 +98,7 @@ public: #define SK_DECLARE_STATIC_MUTEX(name) static SkBaseMutex name = ... */ -#ifdef GOOGLE3 - #include "SkMutex_pthread.h" -#else - #include SK_MUTEX_PLATFORM_H -#endif - +#include "SkMutex.h" class SkAutoMutexAcquire : SkNoncopyable { public: diff --git a/include/core/SkThreadPriv.h b/include/core/SkThreadPriv.h index ab40731dc0..09d5a669f1 100644 --- a/include/core/SkThreadPriv.h +++ b/include/core/SkThreadPriv.h @@ -10,7 +10,7 @@ #include "SkTypes.h" -// SK_ATOMICS_PLATFORM_H must provide inline implementations for the following declarations. +// SkAtomics.h must provide inline implementations for the following declarations. /** Atomic compare and set, for pointers. * If *addr == before, set *addr to after. Always returns previous value of *addr. @@ -18,10 +18,6 @@ */ static void* sk_atomic_cas(void** addr, void* before, void* after); -#ifdef GOOGLE3 - #include "SkAtomics_sync.h" -#else - #include SK_ATOMICS_PLATFORM_H -#endif +#include "SkAtomics.h" #endif//SkThreadPriv_DEFINED diff --git a/include/ports/SkAtomics_sync.h b/include/ports/SkAtomics_sync.h new file mode 100644 index 0000000000..9389c00103 --- /dev/null +++ b/include/ports/SkAtomics_sync.h @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAtomics_sync_DEFINED +#define SkAtomics_sync_DEFINED + +/** GCC/Clang __sync based atomics. */ + +#include <stdint.h> + +static inline __attribute__((always_inline)) int32_t sk_atomic_inc(int32_t* addr) { + return __sync_fetch_and_add(addr, 1); +} + +static inline __attribute__((always_inline)) int64_t sk_atomic_inc(int64_t* addr) { +#if defined(__mips__) && !defined(__LP64__) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) + /** Some versions of the GCC 32-bit MIPS toolchains (e.g. 4.8) for android are missing + * support for the __sync* functions that operate on 64-bit values. The workaround + * is to use __atomic* functions until we can move everything to <stdatomic.h>. + */ + return __atomic_fetch_add(addr, 1, __ATOMIC_SEQ_CST); +#else + return __sync_fetch_and_add(addr, 1); +#endif +} + +static inline __attribute__((always_inline)) int32_t sk_atomic_add(int32_t* addr, int32_t inc) { + return __sync_fetch_and_add(addr, inc); +} + +static inline __attribute__((always_inline)) int32_t sk_atomic_dec(int32_t* addr) { + return __sync_fetch_and_add(addr, -1); +} + +static inline __attribute__((always_inline)) void sk_membar_acquire__after_atomic_dec() { } + +static inline __attribute__((always_inline)) bool sk_atomic_cas(int32_t* addr, + int32_t before, + int32_t after) { + return __sync_bool_compare_and_swap(addr, before, after); +} + +static inline __attribute__((always_inline)) void* sk_atomic_cas(void** addr, + void* before, + void* after) { + return __sync_val_compare_and_swap(addr, before, after); +} + +static inline __attribute__((always_inline)) void sk_membar_acquire__after_atomic_conditional_inc() { } + +#endif diff --git a/include/ports/SkAtomics_win.h b/include/ports/SkAtomics_win.h new file mode 100644 index 0000000000..a1876d269b --- /dev/null +++ b/include/ports/SkAtomics_win.h @@ -0,0 +1,54 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAtomics_win_DEFINED +#define SkAtomics_win_DEFINED + +/** Windows Interlocked atomics. */ + +#include <intrin.h> +#include <stdint.h> + +//MSDN says in order to declare an interlocked function for use as an +//intrinsic, include intrin.h and put the function in a #pragma intrinsic +//directive. +//The pragma appears to be unnecessary, but doesn't hurt. +#pragma intrinsic(_InterlockedIncrement, _InterlockedExchangeAdd, _InterlockedDecrement) +#pragma intrinsic(_InterlockedCompareExchange) + +static inline int32_t sk_atomic_inc(int32_t* addr) { + // InterlockedIncrement returns the new value, we want to return the old. + return _InterlockedIncrement(reinterpret_cast<long*>(addr)) - 1; +} + +static inline int64_t sk_atomic_inc(int64_t* addr) { + // InterlockedIncrement returns the new value, we want to return the old. + return InterlockedIncrement64(addr) - 1; +} + +static inline int32_t sk_atomic_add(int32_t* addr, int32_t inc) { + return _InterlockedExchangeAdd(reinterpret_cast<long*>(addr), static_cast<long>(inc)); +} + +static inline int32_t sk_atomic_dec(int32_t* addr) { + // InterlockedDecrement returns the new value, we want to return the old. + return _InterlockedDecrement(reinterpret_cast<long*>(addr)) + 1; +} + +static inline void sk_membar_acquire__after_atomic_dec() { } + +static inline bool sk_atomic_cas(int32_t* addr, int32_t before, int32_t after) { + return _InterlockedCompareExchange(reinterpret_cast<long*>(addr), after, before) == before; +} + +static inline void* sk_atomic_cas(void** addr, void* before, void* after) { + return InterlockedCompareExchangePointer(addr, after, before); +} + +static inline void sk_membar_acquire__after_atomic_conditional_inc() { } + +#endif diff --git a/include/ports/SkBarriers_arm.h b/include/ports/SkBarriers_arm.h new file mode 100644 index 0000000000..386294e9b1 --- /dev/null +++ b/include/ports/SkBarriers_arm.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBarriers_arm_DEFINED +#define SkBarriers_arm_DEFINED + +static inline void sk_compiler_barrier() { asm volatile("" : : : "memory"); } + +template <typename T> +T sk_acquire_load(T* ptr) { + T val = *ptr; + __sync_synchronize(); // Issue a full barrier, which is an overkill acquire barrier. + return val; +} + +template <typename T> +T sk_consume_load(T* ptr) { + T val = *ptr; + // Unlike acquire, consume loads (data-dependent loads) are guaranteed not to reorder on ARM. + // No memory barrier is needed, so we just use a compiler barrier. + // C.f. http://preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/ + sk_compiler_barrier(); + return val; +} + +template <typename T> +void sk_release_store(T* ptr, T val) { + __sync_synchronize(); // Issue a full barrier, which is an overkill release barrier. + *ptr = val; +} + +#endif//SkBarriers_x86_DEFINED diff --git a/include/ports/SkBarriers_tsan.h b/include/ports/SkBarriers_tsan.h new file mode 100644 index 0000000000..d72dbfd390 --- /dev/null +++ b/include/ports/SkBarriers_tsan.h @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBarriers_tsan_DEFINED +#define SkBarriers_tsan_DEFINED + +static inline void sk_compiler_barrier() { asm volatile("" : : : "memory"); } + +template <typename T> +T sk_acquire_load(T* ptr) { + SkASSERT(__atomic_always_lock_free(sizeof(T), ptr)); + return __atomic_load_n(ptr, __ATOMIC_ACQUIRE); +} + +template <typename T> +T sk_consume_load(T* ptr) { + SkASSERT(__atomic_always_lock_free(sizeof(T), ptr)); + return __atomic_load_n(ptr, __ATOMIC_CONSUME); +} + +template <typename T> +void sk_release_store(T* ptr, T val) { + SkASSERT(__atomic_always_lock_free(sizeof(T), ptr)); + return __atomic_store_n(ptr, val, __ATOMIC_RELEASE); +} + +#endif//SkBarriers_tsan_DEFINED diff --git a/include/ports/SkBarriers_x86.h b/include/ports/SkBarriers_x86.h new file mode 100644 index 0000000000..56e2658e97 --- /dev/null +++ b/include/ports/SkBarriers_x86.h @@ -0,0 +1,39 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBarriers_x86_DEFINED +#define SkBarriers_x86_DEFINED + +#ifdef SK_BUILD_FOR_WIN +# include <intrin.h> +static inline void sk_compiler_barrier() { _ReadWriteBarrier(); } +#else +static inline void sk_compiler_barrier() { asm volatile("" : : : "memory"); } +#endif + +template <typename T> +T sk_acquire_load(T* ptr) { + T val = *ptr; + // On x86, all loads are acquire loads, so we only need a compiler barrier. + sk_compiler_barrier(); + return val; +} + +template <typename T> +T sk_consume_load(T* ptr) { + // On x86, consume is the same as acquire, i.e. a normal load. + return sk_acquire_load(ptr); +} + +template <typename T> +void sk_release_store(T* ptr, T val) { + // On x86, all stores are release stores, so we only need a compiler barrier. + sk_compiler_barrier(); + *ptr = val; +} + +#endif//SkBarriers_x86_DEFINED diff --git a/include/ports/SkMutex_pthread.h b/include/ports/SkMutex_pthread.h new file mode 100644 index 0000000000..7452ece610 --- /dev/null +++ b/include/ports/SkMutex_pthread.h @@ -0,0 +1,96 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMutex_pthread_DEFINED +#define SkMutex_pthread_DEFINED + +/** Posix pthread_mutex based mutex. */ + +#include <errno.h> +#include <pthread.h> + +// We use error-checking mutexes in Debug builds or normal fast mutexes in Release builds. +// Debug builds get these checks for free: +// - a double acquire() from the same thread fails immediately instead of deadlocking; +// - release() checks that the mutex is being unlocked by its owner thread. +// I don't see a built-in way to implement assertHeld(), so we track that with an fOwner field. + +// This isn't technically portable, but on Linux and Android pthread_t is some sort of int, and +// on Darwin it's a pointer. So assuming pthread_self() never returns 0, it works as a sentinel. +SkDEBUGCODE(static const pthread_t kNoOwner = 0;) + +// An SkBaseMutex is a POD structure that can be directly initialized at declaration time with +// SK_DECLARE_STATIC_MUTEX. This avoids the generation of a static initializer in the final +// machine code (and a corresponding static finalizer). +struct SkBaseMutex { + void acquire() { + SkDEBUGCODE(int rc = ) pthread_mutex_lock(&fMutex); + SkASSERT(0 == rc); + SkDEBUGCODE(fOwner = pthread_self();) + } + void release() { + this->assertHeld(); // Usually redundant, but not for static mutexes on Macs (see below). + SkDEBUGCODE(fOwner = kNoOwner;) + SkDEBUGCODE(int rc = ) pthread_mutex_unlock(&fMutex); + SkASSERT(0 == rc); + } + void assertHeld() { + SkASSERT(0 != pthread_equal(fOwner, pthread_self())); + } + + pthread_mutex_t fMutex; + SkDEBUGCODE(pthread_t fOwner;) // Read and write only when holding fMutex. +}; + +// A normal mutex that's required to be initialized through normal C++ construction, +// i.e. when it's a member of another class, or allocated on the heap. +class SkMutex : public SkBaseMutex { +public: + SkMutex() { +#ifdef SK_DEBUG + pthread_mutexattr_t attr; + SkASSERT(0 == pthread_mutexattr_init(&attr)); + SkASSERT(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)); + SkASSERT(0 == pthread_mutex_init(&fMutex, &attr)); + SkASSERT(0 == pthread_mutexattr_destroy(&attr)); + fOwner = kNoOwner; +#else + (void)pthread_mutex_init(&fMutex, NULL); +#endif + } + + ~SkMutex() { + SkDEBUGCODE(int rc = )pthread_mutex_destroy(&fMutex); + SkASSERT(0 == rc); + } + +private: + SkMutex(const SkMutex&); + SkMutex& operator=(const SkMutex&); +}; + +#if defined(SK_DEBUG) && defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) + // When possible we want to use error-check mutexes in Debug builds. See the note at the top. + #define SK_BASE_MUTEX_INIT { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER, kNoOwner } +#elif defined(SK_DEBUG) + // Macs don't support PTHREAD_ERRORCHECK_MUTEX_INITIALIZER when targeting <10.7. We target 10.6. + #define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, kNoOwner } +#else + #define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER } +#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 + +#endif diff --git a/include/ports/SkMutex_win.h b/include/ports/SkMutex_win.h new file mode 100644 index 0000000000..fe06336a90 --- /dev/null +++ b/include/ports/SkMutex_win.h @@ -0,0 +1,79 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMutex_win_DEFINED +#define SkMutex_win_DEFINED + +/** Windows CriticalSection based mutex. */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# define WIN32_IS_MEAN_WAS_LOCALLY_DEFINED +#endif +#ifndef NOMINMAX +# define NOMINMAX +# define NOMINMAX_WAS_LOCALLY_DEFINED +#endif +# +#include <windows.h> +# +#ifdef WIN32_IS_MEAN_WAS_LOCALLY_DEFINED +# undef WIN32_IS_MEAN_WAS_LOCALLY_DEFINED +# undef WIN32_LEAN_AND_MEAN +#endif +#ifdef NOMINMAX_WAS_LOCALLY_DEFINED +# undef NOMINMAX_WAS_LOCALLY_DEFINED +# undef NOMINMAX +#endif + +// On Windows, SkBaseMutex and SkMutex are the same thing, +// we can't easily get rid of static initializers. However, +// we preserve the same inheritance pattern as other platforms +// so that we can forward-declare cleanly. +struct SkBaseMutex { +public: + SkBaseMutex() { + InitializeCriticalSection(&fStorage); + SkDEBUGCODE(fOwner = 0;) + } + + ~SkBaseMutex() { + SkASSERT(0 == fOwner); + DeleteCriticalSection(&fStorage); + } + + void acquire() { + EnterCriticalSection(&fStorage); + SkDEBUGCODE(fOwner = GetCurrentThreadId();) + } + + void release() { + this->assertHeld(); + SkDEBUGCODE(fOwner = 0;) + LeaveCriticalSection(&fStorage); + } + + void assertHeld() { + SkASSERT(GetCurrentThreadId() == fOwner); + } + +protected: + CRITICAL_SECTION fStorage; + SkDEBUGCODE(DWORD fOwner;) + +private: + SkBaseMutex(const SkBaseMutex&); + SkBaseMutex& operator=(const SkBaseMutex&); +}; + +class SkMutex : public SkBaseMutex { }; + +// Windows currently provides no documented means of POD initializing a CRITICAL_SECTION. +// As a result, it is illegal to SK_DECLARE_STATIC_MUTEX in a function. +#define SK_DECLARE_STATIC_MUTEX(name) namespace{} static SkBaseMutex name + +#endif |