aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-10-23 14:44:08 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-10-23 14:44:08 +0000
commit1f81fd6546c111e21bc665657e976b9d842192df (patch)
tree00bb18a731ed166d187c864f2e5542a4996e5767
parentfbcd415aa01801df5f554963d7463649b49ead73 (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.h3
-rw-r--r--include/core/SkPathRef.h14
-rw-r--r--src/core/SkData.cpp10
-rw-r--r--src/core/SkMatrix.cpp5
-rw-r--r--src/core/SkOnce.h83
-rw-r--r--src/core/SkPathRef.cpp13
-rw-r--r--tests/OnceTest.cpp21
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;
}