Use a spinlock in SkOnce.
SkOnceFlag is now statically initializable on all platforms. Also adds sk_atomic_cas, used to implement new SkSpinlock. Going to punt on making SkOnceFlag any smaller (for now, it's 8 bytes). We could conceivably get it down to two bits, one for done and one for a one-bit spinlock (we'd need atomic-& and atomic-| to make that work, but they appear to be available everywhere). BUG=skia:1929 R=bungeman@google.com, reed@google.com Author: mtklein@google.com Review URL: https://codereview.chromium.org/123093002 git-svn-id: http://skia.googlecode.com/svn/trunk@12968 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
d4ba6e7781
commit
4b5fba5a3c
@ -33,6 +33,12 @@ static int32_t sk_atomic_dec(int32_t* addr);
|
||||
*/
|
||||
static int32_t sk_atomic_conditional_inc(int32_t* addr);
|
||||
|
||||
/** Atomic compare and set.
|
||||
* If *addr == before, set *addr to after and return true, otherwise return false.
|
||||
* This must act as a release (SL/S) memory barrier and as a compiler barrier.
|
||||
*/
|
||||
static bool sk_atomic_cas(int32_t* addr, int32_t before, int32_t after);
|
||||
|
||||
/** If sk_atomic_dec does not act as an acquire (L/SL) barrier,
|
||||
* this must act as an acquire (L/SL) memory barrier and as a compiler barrier.
|
||||
*/
|
||||
@ -45,6 +51,34 @@ static void sk_membar_acquire__after_atomic_conditional_inc();
|
||||
|
||||
#include SK_ATOMICS_PLATFORM_H
|
||||
|
||||
// This is POD and must be zero-initialized.
|
||||
struct SkSpinlock {
|
||||
void acquire() {
|
||||
SkASSERT(shouldBeZero == 0);
|
||||
// No memory barrier needed, but sk_atomic_cas gives us at least release anyway.
|
||||
while (!sk_atomic_cas(&thisIsPrivate, 0, 1)) {
|
||||
// spin
|
||||
}
|
||||
}
|
||||
|
||||
void release() {
|
||||
SkASSERT(shouldBeZero == 0);
|
||||
// This requires a release memory barrier before storing, which sk_atomic_cas guarantees.
|
||||
SkAssertResult(sk_atomic_cas(&thisIsPrivate, 1, 0));
|
||||
}
|
||||
|
||||
int32_t thisIsPrivate;
|
||||
SkDEBUGCODE(int32_t shouldBeZero;)
|
||||
};
|
||||
|
||||
class SkAutoSpinlock : SkNoncopyable {
|
||||
public:
|
||||
explicit SkAutoSpinlock(SkSpinlock* lock) : fLock(lock) { fLock->acquire(); }
|
||||
~SkAutoSpinlock() { fLock->release(); }
|
||||
private:
|
||||
SkSpinlock* fLock;
|
||||
};
|
||||
#define SkAutoSpinlock(...) SK_REQUIRE_LOCAL_VAR(SkAutoSpinlock)
|
||||
|
||||
/** SK_MUTEX_PLATFORM_H must provide the following (or equivalent) declarations.
|
||||
|
||||
|
@ -29,12 +29,7 @@
|
||||
#include "SkThread.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
#ifdef SK_USE_POSIX_THREADS
|
||||
# define SK_ONCE_INIT { false, { PTHREAD_MUTEX_INITIALIZER } }
|
||||
#else
|
||||
# define SK_ONCE_INIT { false, SkBaseMutex() }
|
||||
#endif
|
||||
|
||||
#define SK_ONCE_INIT { false, { 0, SkDEBUGCODE(0) } }
|
||||
#define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name = SK_ONCE_INIT
|
||||
|
||||
struct SkOnceFlag; // If manually created, initialize with SkOnceFlag once = SK_ONCE_INIT
|
||||
@ -46,13 +41,13 @@ inline void SkOnce(SkOnceFlag* once, Func f, Arg arg);
|
||||
|
||||
struct SkOnceFlag {
|
||||
bool done;
|
||||
SkBaseMutex mutex;
|
||||
SkSpinlock lock;
|
||||
};
|
||||
|
||||
// TODO(bungeman, mtklein): move all these *barrier* functions to SkThread when refactoring lands.
|
||||
|
||||
#ifdef SK_BUILD_FOR_WIN
|
||||
#include <intrin.h>
|
||||
# include <intrin.h>
|
||||
inline static void compiler_barrier() {
|
||||
_ReadWriteBarrier();
|
||||
}
|
||||
@ -64,11 +59,11 @@ inline static void compiler_barrier() {
|
||||
|
||||
inline static void full_barrier_on_arm() {
|
||||
#ifdef SK_CPU_ARM
|
||||
#if SK_ARM_ARCH >= 7
|
||||
# if SK_ARM_ARCH >= 7
|
||||
asm volatile("dmb" : : : "memory");
|
||||
#else
|
||||
# else
|
||||
asm volatile("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory");
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -98,7 +93,7 @@ inline static void acquire_barrier() {
|
||||
// (We don't mind if this is an actual function call, but odds are it'll be inlined anyway.)
|
||||
template <typename Func, typename Arg>
|
||||
static void sk_once_slow(SkOnceFlag* once, Func f, Arg arg) {
|
||||
const SkAutoMutexAcquire lock(once->mutex);
|
||||
const SkAutoSpinlock lock(&once->lock);
|
||||
if (!once->done) {
|
||||
f(arg);
|
||||
// Also known as a store-store/load-store barrier, this makes sure that the writes
|
||||
|
@ -36,6 +36,13 @@ static inline __attribute__((always_inline)) int32_t sk_atomic_conditional_inc(i
|
||||
}
|
||||
}
|
||||
|
||||
static inline __attribute___((always_inline)) bool sk_atomic_cas(int32_t* addr,
|
||||
int32_t before,
|
||||
int32_t after) {
|
||||
// android_atomic_release_cas returns 0 for success (if *addr == before and it wrote after).
|
||||
return android_atomic_release_cas(before, after, addr) == 0;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) void sk_membar_acquire__after_atomic_conditional_inc() {
|
||||
//HACK: Android is actually using full memory barriers.
|
||||
// Should this change, uncomment below.
|
||||
|
@ -38,6 +38,12 @@ static inline int32_t sk_atomic_conditional_inc(int32_t* addr) {
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline bool sk_atomic_cas(int32_t* addr, int32_t before, int32_t after) {
|
||||
if (*addr != before) return false;
|
||||
*addr = after;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void sk_membar_acquire__after_atomic_conditional_inc() { }
|
||||
|
||||
#endif
|
||||
|
@ -44,6 +44,12 @@ static inline __attribute__((always_inline)) int32_t sk_atomic_conditional_inc(i
|
||||
}
|
||||
}
|
||||
|
||||
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_membar_acquire__after_atomic_conditional_inc() { }
|
||||
|
||||
#endif
|
||||
|
@ -53,6 +53,10 @@ static inline int32_t sk_atomic_conditional_inc(int32_t* addr) {
|
||||
}
|
||||
}
|
||||
|
||||
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_membar_acquire__after_atomic_conditional_inc() { }
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user