diff options
-rw-r--r-- | include/core/SkInstCnt.h | 4 | ||||
-rw-r--r-- | include/core/SkOnce.h | 116 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_chromium.cpp | 5 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 5 | ||||
-rw-r--r-- | src/utils/SkEventTracer.cpp | 19 | ||||
-rw-r--r-- | src/utils/win/SkDWrite.cpp | 14 | ||||
-rw-r--r-- | tests/OnceTest.cpp | 29 |
7 files changed, 91 insertions, 101 deletions
diff --git a/include/core/SkInstCnt.h b/include/core/SkInstCnt.h index 7e5b454b15..1839ee1dbd 100644 --- a/include/core/SkInstCnt.h +++ b/include/core/SkInstCnt.h @@ -47,11 +47,11 @@ extern bool gPrintInstCount; public: \ SkInstanceCountHelper() { \ SK_DECLARE_STATIC_ONCE(once); \ - SkOnce(&once, init, 0); \ + SkOnce(&once, init); \ sk_atomic_inc(GetInstanceCountPtr()); \ } \ \ - static void init(int) { \ + static void init() { \ initStep \ } \ \ diff --git a/include/core/SkOnce.h b/include/core/SkOnce.h index d39a05b7bb..87bb277800 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 diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp index 37e5450e19..f42b272f18 100644 --- a/src/ports/SkGlobalInitialization_chromium.cpp +++ b/src/ports/SkGlobalInitialization_chromium.cpp @@ -62,7 +62,7 @@ #include "SkMatrixImageFilter.h" #include "SkXfermodeImageFilter.h" -static void InitializeFlattenables(int*) { +static void InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader) @@ -122,7 +122,6 @@ static void InitializeFlattenables(int*) { } void SkFlattenable::InitializeFlattenablesIfNeeded() { - int dummy; SK_DECLARE_STATIC_ONCE(once); - SkOnce(&once, InitializeFlattenables, &dummy); + SkOnce(&once, InitializeFlattenables); } diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 949decd018..ce42ca5aad 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -62,7 +62,7 @@ #include "SkMatrixImageFilter.h" #include "SkXfermodeImageFilter.h" -static void InitializeFlattenables(int*) { +static void InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader) @@ -123,7 +123,6 @@ static void InitializeFlattenables(int*) { } void SkFlattenable::InitializeFlattenablesIfNeeded() { - int dummy; SK_DECLARE_STATIC_ONCE(once); - SkOnce(&once, InitializeFlattenables, &dummy); + SkOnce(&once, InitializeFlattenables); } diff --git a/src/utils/SkEventTracer.cpp b/src/utils/SkEventTracer.cpp index 529fca3519..65e8372395 100644 --- a/src/utils/SkEventTracer.cpp +++ b/src/utils/SkEventTracer.cpp @@ -36,25 +36,24 @@ class SkDefaultEventTracer: public SkEventTracer { }; }; -SkEventTracer *SkEventTracer::gInstance; +SkEventTracer* SkEventTracer::gInstance; -static void intialiize_default_tracer(void *current_instance) { +static void cleanup_tracer() { + // calling SetInstance will delete the existing instance. + SkEventTracer::SetInstance(NULL); +} + +static void intialize_default_tracer(SkEventTracer* current_instance) { if (NULL == current_instance) { SkEventTracer::SetInstance(SkNEW(SkDefaultEventTracer)); } + atexit(cleanup_tracer); } -static void cleanup_tracer() { - // calling SetInstance will delete the existing instance. - SkEventTracer::SetInstance(NULL); -} SkEventTracer* SkEventTracer::GetInstance() { SK_DECLARE_STATIC_ONCE(once); - SkOnce(&once, - intialiize_default_tracer, - SkEventTracer::gInstance, - cleanup_tracer); + SkOnce(&once, intialize_default_tracer, SkEventTracer::gInstance); SkASSERT(NULL != SkEventTracer::gInstance); return SkEventTracer::gInstance; } diff --git a/src/utils/win/SkDWrite.cpp b/src/utils/win/SkDWrite.cpp index 16e8ddc4d0..87826b5194 100644 --- a/src/utils/win/SkDWrite.cpp +++ b/src/utils/win/SkDWrite.cpp @@ -14,6 +14,12 @@ static IDWriteFactory* gDWriteFactory = NULL; +static void release_dwrite_factory() { + if (gDWriteFactory) { + gDWriteFactory->Release(); + } +} + static void create_dwrite_factory(IDWriteFactory** factory) { typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; DWriteCreateFactoryProc dWriteCreateFactoryProc = reinterpret_cast<DWriteCreateFactoryProc>( @@ -31,17 +37,13 @@ static void create_dwrite_factory(IDWriteFactory** factory) { __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(factory)), "Could not create DirectWrite factory."); + atexit(release_dwrite_factory); } -static void release_dwrite_factory() { - if (gDWriteFactory) { - gDWriteFactory->Release(); - } -} IDWriteFactory* sk_get_dwrite_factory() { SK_DECLARE_STATIC_ONCE(once); - SkOnce(&once, create_dwrite_factory, &gDWriteFactory, release_dwrite_factory); + SkOnce(&once, create_dwrite_factory, &gDWriteFactory); return gDWriteFactory; } diff --git a/tests/OnceTest.cpp b/tests/OnceTest.cpp index 3a99c39d53..389d257b73 100644 --- a/tests/OnceTest.cpp +++ b/tests/OnceTest.cpp @@ -27,20 +27,6 @@ DEF_TEST(SkOnce_Singlethreaded, r) { REPORTER_ASSERT(r, 5 == x); } -struct AddFour { void operator()(int* x) { *x += 4; } }; - -DEF_TEST(SkOnce_MiscFeatures, r) { - // Tests that we support functors and explicit SkOnceFlags. - int x = 0; - - SkOnceFlag once = SK_ONCE_INIT; - SkOnce(&once, AddFour(), &x); - SkOnce(&once, AddFour(), &x); - SkOnce(&once, AddFour(), &x); - - REPORTER_ASSERT(r, 4 == x); -} - static void add_six(int* x) { *x += 6; } @@ -78,14 +64,13 @@ DEF_TEST(SkOnce_Multithreaded, r) { REPORTER_ASSERT(r, 6 == x); } -// Test that the atExit option works. -static int gToDecrement = 1; -static void noop(int) {} -static void decrement() { gToDecrement--; } -static void checkDecremented() { SkASSERT(gToDecrement == 0); } +static int gX = 0; +static void inc_gX() { gX++; } -DEF_TEST(SkOnce_atExit, r) { - atexit(checkDecremented); +DEF_TEST(SkOnce_NoArg, r) { SK_DECLARE_STATIC_ONCE(once); - SkOnce(&once, noop, 0, decrement); + SkOnce(&once, inc_gX); + SkOnce(&once, inc_gX); + SkOnce(&once, inc_gX); + REPORTER_ASSERT(r, 1 == gX); } |