| Index: include/core/SkOnce.h
|
| diff --git a/include/core/SkOnce.h b/include/core/SkOnce.h
|
| index 59eaf598bca1b3a49257cbcf856b5b248c79063e..daeb819d428408d36ef4cb7b72407d2a12fc01e9 100644
|
| --- a/include/core/SkOnce.h
|
| +++ b/include/core/SkOnce.h
|
| @@ -40,8 +40,32 @@ struct SkOnceFlag; // If manually created, initialize with SkOnceFlag once = SK
|
| template <typename Func, typename Arg>
|
| inline void SkOnce(SkOnceFlag* once, Func f, Arg arg, void(*atExit)() = NULL);
|
|
|
| +// If you've already got a lock and a flag to use, this variant lets you avoid an extra SkOnceFlag.
|
| +template <typename Lock, typename Func, typename Arg>
|
| +inline void SkOnce(bool* done, Lock* lock, Func f, Arg arg, void(*atExit)() = NULL);
|
| +
|
| // ---------------------- Implementation details below here. -----------------------------
|
|
|
| +// 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;)
|
| +};
|
| +
|
| struct SkOnceFlag {
|
| bool done;
|
| SkSpinlock lock;
|
| @@ -87,6 +111,16 @@ inline static void acquire_barrier() {
|
| full_barrier_on_arm();
|
| }
|
|
|
| +// Works with SkSpinlock or SkMutex.
|
| +template <typename Lock>
|
| +class SkAutoLockAcquire {
|
| +public:
|
| + explicit SkAutoLockAcquire(Lock* lock) : fLock(lock) { fLock->acquire(); }
|
| + ~SkAutoLockAcquire() { fLock->release(); }
|
| +private:
|
| + Lock* fLock;
|
| +};
|
| +
|
| // We've pulled a pretty standard double-checked locking implementation apart
|
| // into its main fast path and a slow path that's called when we suspect the
|
| // one-time code hasn't run yet.
|
| @@ -94,10 +128,10 @@ inline static void acquire_barrier() {
|
| // This is the guts of the code, called when we suspect the one-time code hasn't been run yet.
|
| // This should be rarely called, so we separate it from SkOnce and don't mark it as inline.
|
| // (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, void (*atExit)()) {
|
| - const SkAutoSpinlock lock(&once->lock);
|
| - if (!once->done) {
|
| +template <typename Lock, typename Func, typename Arg>
|
| +static void sk_once_slow(bool* done, Lock* lock, Func f, Arg arg, void (*atExit)()) {
|
| + const SkAutoLockAcquire<Lock> locked(lock);
|
| + if (!*done) {
|
| f(arg);
|
| if (atExit != NULL) {
|
| atexit(atExit);
|
| @@ -112,15 +146,15 @@ static void sk_once_slow(SkOnceFlag* once, Func f, Arg arg, void (*atExit)()) {
|
| // We'll use this in the fast path to make sure f(arg)'s effects are
|
| // observable whenever we observe *done == true.
|
| release_barrier();
|
| - once->done = true;
|
| + *done = true;
|
| }
|
| }
|
|
|
| // This is our fast path, called all the time. We do really want it to be inlined.
|
| -template <typename Func, typename Arg>
|
| -inline void SkOnce(SkOnceFlag* once, Func f, Arg arg, void(*atExit)()) {
|
| - if (!SK_ANNOTATE_UNPROTECTED_READ(once->done)) {
|
| - sk_once_slow(once, f, arg, atExit);
|
| +template <typename Lock, typename Func, typename Arg>
|
| +inline void SkOnce(bool* done, Lock* lock, Func f, Arg arg, void(*atExit)()) {
|
| + if (!SK_ANNOTATE_UNPROTECTED_READ(*done)) {
|
| + sk_once_slow(done, lock, f, arg, atExit);
|
| }
|
| // Also known as a load-load/load-store barrier, this acquire barrier makes
|
| // sure that anything we read from memory---in particular, memory written by
|
| @@ -135,6 +169,11 @@ inline void SkOnce(SkOnceFlag* once, Func f, Arg arg, void(*atExit)()) {
|
| acquire_barrier();
|
| }
|
|
|
| +template <typename Func, typename Arg>
|
| +inline void SkOnce(SkOnceFlag* once, Func f, Arg arg, void(*atExit)()) {
|
| + return SkOnce(&once->done, &once->lock, f, arg, atExit);
|
| +}
|
| +
|
| #undef SK_ANNOTATE_BENIGN_RACE
|
|
|
| #endif // SkOnce_DEFINED
|
|
|