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
diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp
index 8d6156c..56c1256 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 @@
///////////////////////////////////////////////////////////////////////////////
+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 41c0f88..5bcb35b 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1894,14 +1894,15 @@
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 d5dd9d9..2c14942 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)
+struct SkOnceFlag;
-// 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)
-
+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 @@
// 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 @@
// 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 @@
// 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 aeef227..f635c2a 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 @@
}
//////////////////////////////////////////////////////////////////////////////
+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) {