SkLazyPtr, mk. 2

SK_DECLARE_STATIC_LAZY_PTR and
SK_DECLARE_STATIC_LAZY_PTR_ARRAY let you declare a single or
array of static pointers that are lazily initialized.

You can think of this as a restricted, lighter-weight
version of SkOnce.  There's no guarantee that Create will be
called exactly once, but we do guarantee all threads will
agree on the resulting pointer.

We'll clean up any other extra pointers we Create()ed by
calling Destroy(), which defaults to SkDELETE.  In debug
mode, we also clean up the winning pointer at process exit,
so we can make sure we didn't leak it or free it early.

I've ported SkData (singleton) and SkXfermode (array) as
examples.  Once this lands I'll port most other users of
SkOnce.

BUG=skia:
R=bungeman@google.com, mtklein@google.com, reed@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/306943003

git-svn-id: http://skia.googlecode.com/svn/trunk@14976 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp
index 2f07914..c653287 100644
--- a/src/core/SkData.cpp
+++ b/src/core/SkData.cpp
@@ -6,10 +6,10 @@
  */
 
 #include "SkData.h"
+#include "SkLazyPtr.h"
+#include "SkOSFile.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
-#include "SkOSFile.h"
-#include "SkOnce.h"
 
 SkData::SkData(const void* ptr, size_t size, ReleaseProc proc, void* context) {
     fPtr = ptr;
@@ -49,18 +49,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkData* gEmptyDataRef = NULL;
-static void cleanup_gEmptyDataRef() { gEmptyDataRef->unref(); }
-
-void SkData::NewEmptyImpl(int) {
-    gEmptyDataRef = new SkData(NULL, 0, NULL, NULL);
+SkData* SkData::NewEmptyImpl() {
+    return new SkData(NULL, 0, NULL, NULL);
 }
+void SkData::DeleteEmpty(SkData* ptr) { SkDELETE(ptr); }
 
 SkData* SkData::NewEmpty() {
-    SK_DECLARE_STATIC_ONCE(once);
-    SkOnce(&once, SkData::NewEmptyImpl, 0, cleanup_gEmptyDataRef);
-    gEmptyDataRef->ref();
-    return gEmptyDataRef;
+    SK_DECLARE_STATIC_LAZY_PTR(SkData, empty, NewEmptyImpl, DeleteEmpty);
+    return SkRef(empty.get());
 }
 
 // assumes fPtr was allocated via sk_malloc
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
index 95a8166..a89237f 100644
--- a/src/core/SkGraphics.cpp
+++ b/src/core/SkGraphics.cpp
@@ -129,7 +129,6 @@
 void SkGraphics::Term() {
     PurgeFontCache();
     SkPaint::Term();
-    SkXfermode::Term();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkLazyPtr.h b/src/core/SkLazyPtr.h
new file mode 100644
index 0000000..5150868
--- /dev/null
+++ b/src/core/SkLazyPtr.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkLazyPtr_DEFINED
+#define SkLazyPtr_DEFINED
+
+/** Declare a lazily-chosen static pointer (or array of pointers) of type F.
+ *
+ *  Example usage:
+ *
+ *  Foo* CreateFoo() { return SkNEW(Foo); }
+ *  Foo* GetSingletonFoo() {
+ *      SK_DECLARE_STATIC_LAZY_PTR(Foo, singleton, CreateFoo);  // Clean up with SkDELETE.
+ *      return singleton.get();
+ *  }
+ *
+ *  These macros take an optional void (*Destroy)(T*) at the end. If not given, we'll use SkDELETE.
+ *  This option is most useful when T doesn't have a public destructor.
+ *
+ *  void CustomCleanup(Foo* ptr) { ... }
+ *  Foo* GetSingletonFooWithCustomCleanup() {
+ *      SK_DECLARE_STATIC_LAZY_PTR(Foo, singleton, CreateFoo, CustomCleanup);
+ *      return singleton.get();
+ *  }
+ *
+ *  If you have a bunch of related static pointers of the same type, you can
+ *  declare an array of lazy pointers together:
+ *
+ *  Foo* CreateFoo(int i) { return ...; }
+ *  Foo* GetCachedFoo(Foo::Enum enumVal) {
+ *      SK_DECLARE_STATIC_LAZY_PTR_ARRAY(Foo, Foo::kEnumCount, cachedFoos, CreateFoo);
+ *      return cachedFoos[enumVal];
+ *  }
+ *
+ *
+ *  You can think of SK_DECLARE_STATIC_LAZY_PTR as a cheaper specialization of
+ *  SkOnce.  There is no mutex or extra storage used past the pointer itself.
+ *  In debug mode, each lazy pointer will be cleaned up at process exit so we
+ *  can check that we've not leaked or freed them early.
+ *
+ *  We may call Create more than once, but all threads will see the same pointer
+ *  returned from get().  Any extra calls to Create will be cleaned up.
+ *
+ *  These macros must be used in a global or function scope, not as a class member.
+ */
+
+#define SK_DECLARE_STATIC_LAZY_PTR(T, name, Create, ...) \
+    static Private::SkLazyPtr<T, Create, ##__VA_ARGS__> name
+
+#define SK_DECLARE_STATIC_LAZY_PTR_ARRAY(T, name, N, Create, ...) \
+    static Private::SkLazyPtrArray<T, N, Create, ##__VA_ARGS__> name
+
+
+
+// Everything below here is private implementation details.  Don't touch, don't even look.
+
+#include "SkDynamicAnnotations.h"
+#include "SkThread.h"
+#include "SkThreadPriv.h"
+
+// See FIXME below.
+class SkFontConfigInterface;
+class SkTypeface;
+
+namespace Private {
+
+template <typename T> void sk_delete(T* ptr) { SkDELETE(ptr); }
+
+// Set *dst to ptr if *dst is NULL.  Returns value of *dst, destroying ptr if not swapped in.
+// Issues the same memory barriers as sk_atomic_cas: acquire on failure, release on success.
+template <typename P, void (*Destroy)(P)>
+static P try_cas(void** dst, P ptr) {
+    P prev = (P)sk_atomic_cas(dst, NULL, ptr);
+
+    if (prev) {
+        // We need an acquire barrier before returning prev, which sk_atomic_cas provided.
+        Destroy(ptr);
+        return prev;
+    } else {
+        // We need a release barrier before returning ptr, which sk_atomic_cas provided.
+        return ptr;
+    }
+}
+
+// This has no constructor and must be zero-initalized (the macro above does this).
+template <typename T, T* (*Create)(), void (*Destroy)(T*) = sk_delete<T> >
+class SkLazyPtr {
+public:
+    T* get() {
+        // If fPtr has already been filled, we need an acquire barrier when loading it.
+        // If not, we need a release barrier when setting it.  try_cas will do that.
+        T* ptr = (T*)sk_acquire_load(&fPtr);
+        return ptr ? ptr : try_cas<T*, Destroy>(&fPtr, Create());
+    }
+
+#ifdef SK_DEBUG
+    // FIXME: We know we leak refs on some classes.  For now, let them leak.
+    void cleanup(SkFontConfigInterface*) {}
+    void cleanup(SkTypeface*) {}
+    template <typename U> void cleanup(U* ptr) { Destroy(ptr); }
+
+    ~SkLazyPtr() {
+        this->cleanup((T*)fPtr);
+        fPtr = NULL;
+    }
+#endif
+
+private:
+    void* fPtr;
+};
+
+// This has no constructor and must be zero-initalized (the macro above does this).
+template <typename T, int N, T* (*Create)(int), void (*Destroy)(T*) = sk_delete<T> >
+class SkLazyPtrArray {
+public:
+    T* operator[](int i) {
+        SkASSERT(i >= 0 && i < N);
+        // If fPtr has already been filled, we need an acquire barrier when loading it.
+        // If not, we need a release barrier when setting it.  try_cas will do that.
+        T* ptr = (T*)sk_acquire_load(&fArray[i]);
+        return ptr ? ptr : try_cas<T*, Destroy>(&fArray[i], Create(i));
+    }
+
+#ifdef SK_DEBUG
+    ~SkLazyPtrArray() {
+        for (int i = 0; i < N; i++) {
+            Destroy((T*)fArray[i]);
+            fArray[i] = NULL;
+        }
+    }
+#endif
+
+private:
+    void* fArray[N];
+};
+
+}  // namespace Private
+
+#endif//SkLazyPtr_DEFINED
diff --git a/src/core/SkThreadPriv.h b/src/core/SkThreadPriv.h
index a972921..c44cca5 100644
--- a/src/core/SkThreadPriv.h
+++ b/src/core/SkThreadPriv.h
@@ -14,7 +14,7 @@
 
 /** Atomic compare and set, for pointers.
  *  If *addr == before, set *addr to after.  Always returns previous value of *addr.
- *  This must act as a compiler barrier.
+ *  This must issue a release barrier on success, acquire on failure, and always a compiler barrier.
  */
 static void* sk_atomic_cas(void** addr, void* before, void* after);
 
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 182d3b7..b5f95da 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -11,8 +11,8 @@
 #include "SkXfermode_opts_SSE2.h"
 #include "SkXfermode_proccoeff.h"
 #include "SkColorPriv.h"
+#include "SkLazyPtr.h"
 #include "SkMathPriv.h"
-#include "SkOnce.h"
 #include "SkReadBuffer.h"
 #include "SkString.h"
 #include "SkUtilsArm.h"
@@ -1651,26 +1651,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SK_DECLARE_STATIC_MUTEX(gCachedXfermodesMutex);
-static SkXfermode* gCachedXfermodes[SkXfermode::kLastMode + 1];  // All NULL to start.
-static bool gXfermodeCached[SK_ARRAY_COUNT(gCachedXfermodes)];  // All false to start.
-
-void SkXfermode::Term() {
-    SkAutoMutexAcquire ac(gCachedXfermodesMutex);
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gCachedXfermodes); ++i) {
-        SkSafeUnref(gCachedXfermodes[i]);
-        gCachedXfermodes[i] = NULL;
-    }
-}
-
-extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec,
-                                                      SkXfermode::Mode mode);
+extern SkProcCoeffXfermode* SkPlatformXfermodeFactory(const ProcCoeff& rec, SkXfermode::Mode mode);
 extern SkXfermodeProc SkPlatformXfermodeProcFactory(SkXfermode::Mode mode);
 
-
-static void create_mode(SkXfermode::Mode mode) {
-    SkASSERT(NULL == gCachedXfermodes[mode]);
+// Technically, can't be static and passed as a template parameter.  So we use anonymous namespace.
+namespace {
+SkXfermode* create_mode(int iMode) {
+    SkXfermode::Mode mode = (SkXfermode::Mode)iMode;
 
     ProcCoeff rec = gProcCoeffs[mode];
     SkXfermodeProc pp = SkPlatformXfermodeProcFactory(mode);
@@ -1709,28 +1696,27 @@
                 break;
         }
     }
-    gCachedXfermodes[mode] = xfer;
+    return xfer;
 }
+}  // namespace
+
 
 SkXfermode* SkXfermode::Create(Mode mode) {
     SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount);
-    SkASSERT(SK_ARRAY_COUNT(gCachedXfermodes) == kModeCount);
 
     if ((unsigned)mode >= kModeCount) {
         // report error
         return NULL;
     }
 
-    // Skia's "defaut" mode is srcover. NULL in SkPaint is interpreted as srcover
+    // Skia's "default" mode is srcover. NULL in SkPaint is interpreted as srcover
     // so we can just return NULL from the factory.
     if (kSrcOver_Mode == mode) {
         return NULL;
     }
 
-    SkOnce(&gXfermodeCached[mode], &gCachedXfermodesMutex, create_mode, mode);
-    SkXfermode* xfer = gCachedXfermodes[mode];
-    SkASSERT(xfer != NULL);
-    return SkSafeRef(xfer);
+    SK_DECLARE_STATIC_LAZY_PTR_ARRAY(SkXfermode, cached, kModeCount, create_mode);
+    return SkSafeRef(cached[mode]);
 }
 
 SkXfermodeProc SkXfermode::GetProc(Mode mode) {