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:
commit-bot@chromium.org 2014-01-08 21:15:56 +00:00
parent d4ba6e7781
commit 4b5fba5a3c
6 changed files with 64 additions and 12 deletions

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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