| Index: include/core/SkOnce.h
|
| diff --git a/include/core/SkOnce.h b/include/core/SkOnce.h
|
| index d39a05b7bb10fd5c0c898be12e6f3d53d78453dc..87bb277800e7d1ba89d808781be7231a12dd4dd3 100644
|
| --- a/include/core/SkOnce.h
|
| +++ b/include/core/SkOnce.h
|
| @@ -8,77 +8,71 @@
|
| #ifndef SkOnce_DEFINED
|
| #define SkOnce_DEFINED
|
|
|
| +// Before trying SkOnce, see if SkLazyPtr or SkLazyFnPtr will work for you.
|
| +// They're smaller and faster, if slightly less versatile.
|
| +
|
| +
|
| // SkOnce.h defines SK_DECLARE_STATIC_ONCE and SkOnce(), which you can use
|
| -// together to create a threadsafe way to call a function just once. This
|
| -// is particularly useful for lazy singleton initialization. E.g.
|
| +// together to create a threadsafe way to call a function just once. E.g.
|
| //
|
| -// static void set_up_my_singleton(Singleton** singleton) {
|
| -// *singleton = new Singleton(...);
|
| +// static void register_my_stuff(GlobalRegistry* registry) {
|
| +// registry->register(...);
|
| // }
|
| // ...
|
| -// const Singleton& GetSingleton() {
|
| -// static Singleton* singleton = NULL;
|
| +// void EnsureRegistered() {
|
| // SK_DECLARE_STATIC_ONCE(once);
|
| -// SkOnce(&once, set_up_my_singleton, &singleton);
|
| -// SkASSERT(NULL != singleton);
|
| -// return *singleton;
|
| +// SkOnce(&once, register_my_stuff, GetGlobalRegistry());
|
| // }
|
| //
|
| +// No matter how many times you call EnsureRegistered(), register_my_stuff will be called just once.
|
| // OnceTest.cpp also should serve as a few other simple examples.
|
| -//
|
| -// You may optionally pass SkOnce a second function to be called at exit for cleanup.
|
|
|
| #include "SkDynamicAnnotations.h"
|
| #include "SkThread.h"
|
| #include "SkTypes.h"
|
|
|
| -#define SK_ONCE_INIT { false, { 0, SkDEBUGCODE(0) } }
|
| -#define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name = SK_ONCE_INIT
|
| +// This must be used in a global or function scope, not as a class member.
|
| +#define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name
|
|
|
| -struct SkOnceFlag; // If manually created, initialize with SkOnceFlag once = SK_ONCE_INIT
|
| +class SkOnceFlag;
|
|
|
| -template <typename Func, typename Arg>
|
| -inline void SkOnce(SkOnceFlag* once, Func f, Arg arg, void(*atExit)() = NULL);
|
| +inline void SkOnce(SkOnceFlag* once, void (*f)());
|
| +
|
| +template <typename Arg>
|
| +inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg);
|
|
|
| // 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);
|
| +template <typename Lock>
|
| +inline void SkOnce(bool* done, Lock* lock, void (*f)());
|
| +
|
| +template <typename Lock, typename Arg>
|
| +inline void SkOnce(bool* done, Lock* lock, void (*f)(Arg), Arg arg);
|
|
|
| // ---------------------- Implementation details below here. -----------------------------
|
|
|
| -// This is POD and must be zero-initialized.
|
| -struct SkSpinlock {
|
| +// This class has no constructor and must be zero-initialized (the macro above does this).
|
| +class SkOnceFlag {
|
| +public:
|
| + bool* mutableDone() { return &fDone; }
|
| +
|
| 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)) {
|
| + // To act as a mutex, this needs an acquire barrier on success.
|
| + // sk_atomic_cas doesn't guarantee this ...
|
| + while (!sk_atomic_cas(&fSpinlock, 0, 1)) {
|
| // spin
|
| }
|
| + // ... so make sure to issue one of our own.
|
| + SkAssertResult(sk_acquire_load(&fSpinlock));
|
| }
|
|
|
| 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));
|
| + // To act as a mutex, this needs a release barrier. sk_atomic_cas guarantees this.
|
| + SkAssertResult(sk_atomic_cas(&fSpinlock, 1, 0));
|
| }
|
|
|
| - int32_t thisIsPrivate;
|
| - SkDEBUGCODE(int32_t shouldBeZero;)
|
| -};
|
| -
|
| -struct SkOnceFlag {
|
| - bool done;
|
| - SkSpinlock lock;
|
| -};
|
| -
|
| -// 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;
|
| + bool fDone;
|
| + int32_t fSpinlock;
|
| };
|
|
|
| // We've pulled a pretty standard double-checked locking implementation apart
|
| @@ -88,14 +82,11 @@ private:
|
| // 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 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);
|
| +template <typename Lock, typename Arg>
|
| +static void sk_once_slow(bool* done, Lock* lock, void (*f)(Arg), Arg arg) {
|
| + lock->acquire();
|
| if (!*done) {
|
| f(arg);
|
| - if (atExit != NULL) {
|
| - atexit(atExit);
|
| - }
|
| // Also known as a store-store/load-store barrier, this makes sure that the writes
|
| // done before here---in particular, those done by calling f(arg)---are observable
|
| // before the writes after the line, *done = true.
|
| @@ -107,13 +98,14 @@ static void sk_once_slow(bool* done, Lock* lock, Func f, Arg arg, void (*atExit)
|
| // observable whenever we observe *done == true.
|
| sk_release_store(done, true);
|
| }
|
| + lock->release();
|
| }
|
|
|
| // This is our fast path, called all the time. We do really want it to be inlined.
|
| -template <typename Lock, typename Func, typename Arg>
|
| -inline void SkOnce(bool* done, Lock* lock, Func f, Arg arg, void(*atExit)()) {
|
| +template <typename Lock, typename Arg>
|
| +inline void SkOnce(bool* done, Lock* lock, void (*f)(Arg), Arg arg) {
|
| if (!SK_ANNOTATE_UNPROTECTED_READ(*done)) {
|
| - sk_once_slow(done, lock, f, arg, atExit);
|
| + sk_once_slow(done, lock, f, arg);
|
| }
|
| // 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
|
| @@ -128,11 +120,25 @@ inline void SkOnce(bool* done, Lock* lock, Func f, Arg arg, void(*atExit)()) {
|
| SkAssertResult(sk_acquire_load(done));
|
| }
|
|
|
| -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);
|
| +template <typename Arg>
|
| +inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg) {
|
| + return SkOnce(once->mutableDone(), once, f, arg);
|
| +}
|
| +
|
| +// Calls its argument.
|
| +// This lets us use functions that take no arguments with SkOnce methods above.
|
| +// (We pass _this_ as the function and the no-arg function as its argument. Cute eh?)
|
| +static void sk_once_no_arg_adaptor(void (*f)()) {
|
| + f();
|
| +}
|
| +
|
| +inline void SkOnce(SkOnceFlag* once, void (*func)()) {
|
| + return SkOnce(once, sk_once_no_arg_adaptor, func);
|
| }
|
|
|
| -#undef SK_ANNOTATE_BENIGN_RACE
|
| +template <typename Lock>
|
| +inline void SkOnce(bool* done, Lock* lock, void (*func)()) {
|
| + return SkOnce(done, lock, sk_once_no_arg_adaptor, func);
|
| +}
|
|
|
| #endif // SkOnce_DEFINED
|
|
|