diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-23 14:44:08 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-23 14:44:08 +0000 |
commit | 1f81fd6546c111e21bc665657e976b9d842192df (patch) | |
tree | 00bb18a731ed166d187c864f2e5542a4996e5767 | |
parent | fbcd415aa01801df5f554963d7463649b49ead73 (diff) |
SK_ONCE for SkData and SkPathRef
Adds SK_ONCE_FRIEND, to allow SK_DEF_ONCE code to be friends with a class. This had to go in include/core to be visible to headers there.
BUG=
R=reed@google.com, bungeman@google.com
Author: mtklein@google.com
Review URL: https://codereview.chromium.org/26491003
git-svn-id: http://skia.googlecode.com/svn/trunk@11914 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | include/core/SkData.h | 3 | ||||
-rw-r--r-- | include/core/SkPathRef.h | 14 | ||||
-rw-r--r-- | src/core/SkData.cpp | 10 | ||||
-rw-r--r-- | src/core/SkMatrix.cpp | 5 | ||||
-rw-r--r-- | src/core/SkOnce.h | 83 | ||||
-rw-r--r-- | src/core/SkPathRef.cpp | 13 | ||||
-rw-r--r-- | tests/OnceTest.cpp | 21 |
7 files changed, 82 insertions, 67 deletions
diff --git a/include/core/SkData.h b/include/core/SkData.h index c244cb8a92..b5dc66fd56 100644 --- a/include/core/SkData.h +++ b/include/core/SkData.h @@ -136,6 +136,9 @@ private: SkData(const void* ptr, size_t size, ReleaseProc, void* context); virtual ~SkData(); + // Called the first time someone calls NewEmpty to initialize the singleton. + static void NewEmptyImpl(SkData**); + typedef SkRefCnt INHERITED; }; diff --git a/include/core/SkPathRef.h b/include/core/SkPathRef.h index 22287894d9..d832944ec3 100644 --- a/include/core/SkPathRef.h +++ b/include/core/SkPathRef.h @@ -108,14 +108,7 @@ public: /** * Gets a path ref with no verbs or points. */ - static SkPathRef* CreateEmpty() { - static SkPathRef* gEmptyPathRef; - if (!gEmptyPathRef) { - gEmptyPathRef = SkNEW(SkPathRef); // leak! - gEmptyPathRef->computeBounds(); // Premptively avoid a race to clear fBoundsIsDirty. - } - return SkRef(gEmptyPathRef); - } + static SkPathRef* CreateEmpty(); /** * Returns true if all of the points in this path are finite, meaning there @@ -397,6 +390,11 @@ private: SkDEBUGCODE(void validate() const;) + /** + * Called the first time someone calls CreateEmpty to actually create the singleton. + */ + static void CreateEmptyImpl(SkPathRef** empty); + enum { kMinSize = 256, }; diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp index 8d6156c7f2..56c1256d75 100644 --- a/src/core/SkData.cpp +++ b/src/core/SkData.cpp @@ -8,6 +8,7 @@ #include "SkData.h" #include "SkFlattenableBuffers.h" #include "SkOSFile.h" +#include "SkOnce.h" SK_DEFINE_INST_COUNT(SkData) @@ -49,11 +50,14 @@ size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const { /////////////////////////////////////////////////////////////////////////////// +void SkData::NewEmptyImpl(SkData** empty) { + *empty = new SkData(NULL, 0, NULL, NULL); +} + SkData* SkData::NewEmpty() { static SkData* gEmptyRef; - if (NULL == gEmptyRef) { - gEmptyRef = new SkData(NULL, 0, NULL, NULL); - } + SK_DECLARE_STATIC_ONCE(once); + SkOnce(&once, SkData::NewEmptyImpl, &gEmptyRef); gEmptyRef->ref(); return gEmptyRef; } diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp index 41c0f883a7..5bcb35b298 100644 --- a/src/core/SkMatrix.cpp +++ b/src/core/SkMatrix.cpp @@ -1894,14 +1894,15 @@ SkScalar SkMatrix::getMaxStretch() const { return SkScalarSqrt(largerRoot); } -DEF_SK_ONCE(reset_identity_matrix, SkMatrix* identity) { +static void reset_identity_matrix(SkMatrix* identity) { identity->reset(); } const SkMatrix& SkMatrix::I() { // If you can use C++11 now, you might consider replacing this with a constexpr constructor. static SkMatrix gIdentity; - SK_ONCE(reset_identity_matrix, &gIdentity); + SK_DECLARE_STATIC_ONCE(once); + SkOnce(&once, reset_identity_matrix, &gIdentity); return gIdentity; } diff --git a/src/core/SkOnce.h b/src/core/SkOnce.h index d5dd9d925b..2c14942077 100644 --- a/src/core/SkOnce.h +++ b/src/core/SkOnce.h @@ -8,54 +8,46 @@ #ifndef SkOnce_DEFINED #define SkOnce_DEFINED -// SkOnce.h defines two macros, DEF_SK_ONCE and SK_ONCE. -// You can use these macros together to create a threadsafe block of code that -// runs at most once, no matter how many times you call it. This is -// particularly useful for lazy singleton initialization. E.g. +// 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. // -// DEF_SK_ONCE(set_up_my_singleton, SingletonType* singleton) { -// // Code in this block will run at most once. +// static void set_up_my_singleton(Singleton** singleton) { // *singleton = new Singleton(...); // } // ... -// const Singleton& getSingleton() { +// const Singleton& GetSingleton() { // static Singleton* singleton = NULL; -// // Always call SK_ONCE. It's very cheap to call after the first time. -// SK_ONCE(set_up_my_singleton, singleton); +// SK_DECLARE_STATIC_ONCE(once); +// SkOnce(&once, set_up_my_singleton, &singleton); // SkASSERT(NULL != singleton); // return *singleton; // } // -// OnceTest.cpp also should serve as another simple example. +// OnceTest.cpp also should serve as a few other simple examples. #include "SkThread.h" #include "SkTypes.h" +#ifdef SK_USE_POSIX_THREADS +#define SK_DECLARE_STATIC_ONCE(name) \ + static SkOnceFlag name = { false, { PTHREAD_MUTEX_INITIALIZER } } +#else +#define SK_DECLARE_STATIC_ONCE(name) \ + static SkOnceFlag name = { false, SkBaseMutex() } +#endif -// Pass a unique name (at least in this scope) for name, and a type and name -// for arg (as if writing a function declaration). -// E.g. -// DEF_SK_ONCE(my_onetime_setup, int* foo) { -// *foo += 5; -// } -#define DEF_SK_ONCE(name, arg) \ - static bool sk_once_##name##_done = false; \ - SK_DECLARE_STATIC_MUTEX(sk_once_##name##_mutex); \ - static void sk_once_##name##_function(arg) - -// Call this anywhere you need to guarantee that the corresponding DEF_SK_ONCE -// block of code has run. name should match the DEF_SK_ONCE, and here you pass -// the actual value of the argument. -// E.g -// int foo = 0; -// SK_ONCE(my_onetime_setup, &foo); -// SkASSERT(5 == foo); -#define SK_ONCE(name, arg) \ - sk_once(&sk_once_##name##_done, &sk_once_##name##_mutex, sk_once_##name##_function, arg) +struct SkOnceFlag; +template <typename Arg> +inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg); // ---------------------- Implementation details below here. ----------------------------- +struct SkOnceFlag { + bool done; + SkBaseMutex mutex; +}; // TODO(bungeman, mtklein): move all these *barrier* functions to SkThread when refactoring lands. @@ -98,13 +90,13 @@ inline static void acquire_barrier() { // one-time code hasn't run yet. // 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 sk_once and don't mark it as inline. +// 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 Arg> -static void sk_once_slow(bool* done, SkBaseMutex* mutex, void (*once)(Arg), Arg arg) { - const SkAutoMutexAcquire lock(*mutex); - if (!*done) { - once(arg); +static void sk_once_slow(SkOnceFlag* once, void (*f)(Arg), Arg arg) { + const SkAutoMutexAcquire lock(once->mutex); + if (!once->done) { + f(arg); // Also known as a store-store/load-store barrier, this makes sure that the writes // done before here---in particular, those done by calling once(arg)---are observable // before the writes after the line, *done = true. @@ -115,7 +107,7 @@ static void sk_once_slow(bool* done, SkBaseMutex* mutex, void (*once)(Arg), Arg // We'll use this in the fast path to make sure once(arg)'s effects are // observable whenever we observe *done == true. release_barrier(); - *done = true; + once->done = true; } } @@ -136,25 +128,24 @@ void AnnotateBenignRace(const char* file, int line, const volatile void* mem, co // This is our fast path, called all the time. We do really want it to be inlined. template <typename Arg> -inline static void sk_once(bool* done, SkBaseMutex* mutex, void (*once)(Arg), Arg arg) { - ANNOTATE_BENIGN_RACE(done, "Don't worry TSAN, we're sure this is safe."); - if (!*done) { - sk_once_slow(done, mutex, once, arg); +inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg) { + ANNOTATE_BENIGN_RACE(once->done, "Don't worry TSAN, we're sure this is safe."); + if (!once->done) { + sk_once_slow(once, 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 - // calling once(arg)---is at least as current as the value we read from done. + // calling f(arg)---is at least as current as the value we read from once->done. // // In version control terms, this is a lot like saying "sync up to the - // commit where we wrote *done = true". + // commit where we wrote once->done = true". // - // The release barrier in sk_once_slow guaranteed that *done = true - // happens after once(arg), so by syncing to *done = true here we're - // forcing ourselves to also wait until the effects of once(arg) are readble. + // The release barrier in sk_once_slow guaranteed that once->done = true + // happens after f(arg), so by syncing to once->done = true here we're + // forcing ourselves to also wait until the effects of f(arg) are readble. acquire_barrier(); } #undef ANNOTATE_BENIGN_RACE - #endif // SkOnce_DEFINED diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index aeef227d70..f635c2a3e5 100644 --- a/src/core/SkPathRef.cpp +++ b/src/core/SkPathRef.cpp @@ -6,6 +6,7 @@ */ #include "SkBuffer.h" +#include "SkOnce.h" #include "SkPath.h" #include "SkPathRef.h" @@ -36,6 +37,18 @@ SkPoint* SkPathRef::Editor::growForConic(SkScalar w) { } ////////////////////////////////////////////////////////////////////////////// +void SkPathRef::CreateEmptyImpl(SkPathRef** empty) { + *empty = SkNEW(SkPathRef); + (*empty)->computeBounds(); // Preemptively avoid a race to clear fBoundsIsDirty. +} + +SkPathRef* SkPathRef::CreateEmpty() { + static SkPathRef* gEmptyPathRef; + SK_DECLARE_STATIC_ONCE(once); + SkOnce(&once, SkPathRef::CreateEmptyImpl, &gEmptyPathRef); + return SkRef(gEmptyPathRef); +} + void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, const SkPathRef& src, const SkMatrix& matrix) { diff --git a/tests/OnceTest.cpp b/tests/OnceTest.cpp index 6c485a0f0a..31c6a85a44 100644 --- a/tests/OnceTest.cpp +++ b/tests/OnceTest.cpp @@ -10,32 +10,35 @@ #include "Test.h" #include "TestClassDef.h" -DEF_SK_ONCE(add_five, int* x) { +static void add_five(int* x) { *x += 5; } DEF_TEST(SkOnce_Singlethreaded, r) { int x = 0; + SK_DECLARE_STATIC_ONCE(once); // No matter how many times we do this, x will be 5. - SK_ONCE(add_five, &x); - SK_ONCE(add_five, &x); - SK_ONCE(add_five, &x); - SK_ONCE(add_five, &x); - SK_ONCE(add_five, &x); + SkOnce(&once, add_five, &x); + SkOnce(&once, add_five, &x); + SkOnce(&once, add_five, &x); + SkOnce(&once, add_five, &x); + SkOnce(&once, add_five, &x); REPORTER_ASSERT(r, 5 == x); } -DEF_SK_ONCE(add_six, int* x) { +static void add_six(int* x) { *x += 6; } class Racer : public SkRunnable { public: + SkOnceFlag* once; int* ptr; + virtual void run() SK_OVERRIDE { - SK_ONCE(add_six, ptr); + SkOnce(once, add_six, ptr); } }; @@ -44,8 +47,10 @@ DEF_TEST(SkOnce_Multithreaded, r) { // Make a bunch of tasks that will race to be the first to add six to x. Racer racers[kTasks]; + SK_DECLARE_STATIC_ONCE(once); int x = 0; for (int i = 0; i < kTasks; i++) { + racers[i].once = &once; racers[i].ptr = &x; } |