posix: Avoid static initializers in static/global mutexes

This patch removes static initializers related to static and
global mutexes from the final library's machine code when
building on a pthread-capable system.

We use PTHREAD_MUTEX_INITIALIZER to perform POD-style
initialization. You need a line like the following to declare
a global mutex with it:

    SkBaseMutex gMutex = { PTHREAD_MUTEX_INITIALIZER };

We introduce the SK_DECLARE_STATIC_MUTEX and SK_DECLARE_GLOBAL_MUTEX
macros to be able to declare static/global mutexes in the source tree
uniformly.

SkMutex is now defined as a sub-class of SkBaseMutex, with standard
construction/destruction semantics. This is useful if the mutex
object is a member of another C++ class, or allocated dynamically.

We also modify a few places to refer to SkBaseMutex instead of a
SkMutex, where it makes sense. Generally speaking, client code
should hold and use pointers to SkBaseMutex whenever they can
now.

We defined a new built-time macro named SK_USE_POSIX_THREADS
to indicate that we're using a pthread-based SkThread.h
interface. The macro will also be used in future patches
to implement other helper thread synchronization classes.

Finally, we inline the acquire() and release() functions in the
case of Posix to improve performance a bit.

Running: 'bench -repeat 10 -match mutex' on an Android device or
a 2.4GHz Xeon Linux desktop shows the following improvements:

                      Before     After

        Galaxy Nexus    1.64      1.45
        Nexus S         1.47      1.16
        Xoom            1.86      1.66
        Xeon            0.36      0.31

This removes 5 static mutex initializers from the library
Review URL: https://codereview.appspot.com/5501066

git-svn-id: http://skia.googlecode.com/svn/trunk@3091 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/MutexBench.cpp b/bench/MutexBench.cpp
index d9b427b..af8a840 100644
--- a/bench/MutexBench.cpp
+++ b/bench/MutexBench.cpp
@@ -23,7 +23,7 @@
 
     virtual void onDraw(SkCanvas* canvas) {
         for (int i = 0; i < N; i++) {
-            SkMutex mu;
+            SK_DECLARE_STATIC_MUTEX(mu);
             for (int j = 0; j < M; j++) {
                 mu.acquire();
                 mu.release();
diff --git a/gyp/common_conditions.gypi b/gyp/common_conditions.gypi
index 99cabc9..faa48fb 100644
--- a/gyp/common_conditions.gypi
+++ b/gyp/common_conditions.gypi
@@ -205,6 +205,14 @@
       },
     ],
 
+    # We can POD-style initialization of static mutexes to avoid generating
+    # static initializers if we're using a pthread-compatible thread interface.
+    [ 'skia_os != "win"', {
+      'defines': [
+        'SK_USE_POSIX_THREADS'
+      ],
+    }],
+
   ], # end 'conditions'
 }
 
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index e247479..3f07844 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -52,7 +52,7 @@
 */
 class SkPixelRef : public SkRefCnt {
 public:
-    explicit SkPixelRef(SkMutex* mutex = NULL);
+    explicit SkPixelRef(SkBaseMutex* mutex = NULL);
 
     /** Return the pixel memory returned from lockPixels, or null if the
         lockCount is 0.
@@ -201,16 +201,16 @@
     /** Return the mutex associated with this pixelref. This value is assigned
         in the constructor, and cannot change during the lifetime of the object.
     */
-    SkMutex* mutex() const { return fMutex; }
+    SkBaseMutex* mutex() const { return fMutex; }
 
-    SkPixelRef(SkFlattenableReadBuffer&, SkMutex*);
+    SkPixelRef(SkFlattenableReadBuffer&, SkBaseMutex*);
 
 private:
 #if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
     static void InitializeFlattenables();
 #endif
 
-    SkMutex*        fMutex; // must remain in scope for the life of this object
+    SkBaseMutex*    fMutex; // must remain in scope for the life of this object
     void*           fPixels;
     SkColorTable*   fColorTable;    // we do not track ownership, subclass does
     int             fLockCount;
diff --git a/include/core/SkThread.h b/include/core/SkThread.h
index 5f2da4a..1495a16 100644
--- a/include/core/SkThread.h
+++ b/include/core/SkThread.h
@@ -31,7 +31,7 @@
 
 class SkAutoMutexAcquire : SkNoncopyable {
 public:
-    explicit SkAutoMutexAcquire(SkMutex& mutex) : fMutex(&mutex)
+    explicit SkAutoMutexAcquire(SkBaseMutex& mutex) : fMutex(&mutex)
     {
         SkASSERT(fMutex != NULL);
         mutex.acquire();
@@ -55,7 +55,7 @@
     }
         
 private:
-    SkMutex* fMutex;
+    SkBaseMutex* fMutex;
 };
 
 #endif
diff --git a/include/core/SkThread_platform.h b/include/core/SkThread_platform.h
index cbb4891..94a4a1e 100644
--- a/include/core/SkThread_platform.h
+++ b/include/core/SkThread_platform.h
@@ -78,7 +78,44 @@
 */
 SK_API int32_t sk_atomic_dec(int32_t* addr);
 
-class SkMutex {
+#endif
+
+#ifdef SK_USE_POSIX_THREADS
+
+#include <pthread.h>
+
+// A SkBaseMutex is a POD structure that can be directly initialized
+// at declaration time with SK_DECLARE_STATIC/GLOBAL_MUTEX. This avoids the
+// generation of a static initializer in the final machine code (and
+// a corresponding static finalizer).
+//
+struct SkBaseMutex {
+    void    acquire() { pthread_mutex_lock(&fMutex); }
+    void    release() { pthread_mutex_unlock(&fMutex); }
+    pthread_mutex_t  fMutex;
+};
+
+// Using POD-style initialization prevents the generation of a static initializer
+// and keeps the acquire() implementation small and fast.
+#define SK_DECLARE_STATIC_MUTEX(name)   static SkBaseMutex  name = { PTHREAD_MUTEX_INITIALIZER }
+
+// Special case used when the static mutex must be available globally.
+#define SK_DECLARE_GLOBAL_MUTEX(name)   SkBaseMutex  name = { PTHREAD_MUTEX_INITIALIZER }
+
+// A normal mutex that requires to be initialized through normal C++ construction,
+// i.e. when it's a member of another class, or allocated on the heap.
+class SkMutex : public SkBaseMutex, SkNoncopyable {
+public:
+    SkMutex();
+    ~SkMutex();
+};
+
+#else // !SK_USE_POSIX_THREADS
+
+// In the generic case, SkBaseMutex and SkMutex are the same thing, and we
+// can't easily get rid of static initializers.
+//
+class SkMutex : SkNoncopyable {
 public:
     SkMutex();
     ~SkMutex();
@@ -94,6 +131,12 @@
     uint32_t    fStorage[kStorageIntCount];
 };
 
-#endif
+typedef SkMutex SkBaseMutex;
+
+#define SK_DECLARE_STATIC_MUTEX(name)  static SkBaseMutex  name
+#define SK_DECLARE_GLOBAL_MUTEX(name)  SkBaseMutex  name
+
+#endif // !SK_USE_POSIX_THREADS
+
 
 #endif
diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h
index b884017..2ebdec7 100644
--- a/include/pdf/SkPDFFont.h
+++ b/include/pdf/SkPDFFont.h
@@ -196,7 +196,7 @@
 
     // This should be made a hash table if performance is a problem.
     static SkTDArray<FontRec>& CanonicalFonts();
-    static SkMutex& CanonicalFontsMutex();
+    static SkBaseMutex& CanonicalFontsMutex();
 };
 
 #endif
diff --git a/include/pdf/SkPDFGraphicState.h b/include/pdf/SkPDFGraphicState.h
index 9420405..af01737 100644
--- a/include/pdf/SkPDFGraphicState.h
+++ b/include/pdf/SkPDFGraphicState.h
@@ -86,7 +86,7 @@
 
     // This should be made a hash table if performance is a problem.
     static SkTDArray<GSCanonicalEntry>& CanonicalPaints();
-    static SkMutex& CanonicalPaintsMutex();
+    static SkBaseMutex& CanonicalPaintsMutex();
 
     SkPDFGraphicState();
     explicit SkPDFGraphicState(const SkPaint& paint);
diff --git a/include/pdf/SkPDFShader.h b/include/pdf/SkPDFShader.h
index 6b6ae03..439d83b 100644
--- a/include/pdf/SkPDFShader.h
+++ b/include/pdf/SkPDFShader.h
@@ -56,7 +56,7 @@
     };
     // This should be made a hash table if performance is a problem.
     static SkTDArray<ShaderCanonicalEntry>& CanonicalShaders();
-    static SkMutex& CanonicalShadersMutex();
+    static SkBaseMutex& CanonicalShadersMutex();
     static void RemoveShader(SkPDFObject* shader);
 
     SkPDFShader();
diff --git a/obsolete/SkGLDevice.cpp b/obsolete/SkGLDevice.cpp
index ec819d2..d4606d9 100644
--- a/obsolete/SkGLDevice.cpp
+++ b/obsolete/SkGLDevice.cpp
@@ -867,7 +867,7 @@
 #include "SkTextureCache.h"
 #include "SkThread.h"
 
-static SkMutex gTextureCacheMutex;
+SK_DECLARE_STATIC_MUTEX(gTextureCacheMutex);
 static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
 
 SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
diff --git a/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
index df87359..033b331 100644
--- a/src/core/SkMemory_stdlib.cpp
+++ b/src/core/SkMemory_stdlib.cpp
@@ -29,7 +29,7 @@
 #define kByteFill 0xCD
 #define kDeleteFill 0xEF
 
-static SkMutex& get_block_mutex() {
+static SkBaseMutex& get_block_mutex() {
     static SkMutex* gBlockMutex;
     if (NULL == gBlockMutex) {
         gBlockMutex = new SkMutex;
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index 1f28ae7..d5e1b81 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -9,7 +9,7 @@
 #include "SkFlattenable.h"
 #include "SkThread.h"
 
-static SkMutex  gPixelRefMutex;
+SK_DECLARE_STATIC_MUTEX(gPixelRefMutex);
 
 extern int32_t SkNextPixelRefGenerationID();
 int32_t SkNextPixelRefGenerationID() {
@@ -24,7 +24,7 @@
 }
 
 
-SkPixelRef::SkPixelRef(SkMutex* mutex) {
+SkPixelRef::SkPixelRef(SkBaseMutex* mutex) {
     if (NULL == mutex) {
         mutex = &gPixelRefMutex;
     }
@@ -36,7 +36,7 @@
     fIsImmutable = false;
 }
 
-SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkMutex* mutex) {
+SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) {
     if (NULL == mutex) {
         mutex = &gPixelRefMutex;
     }
diff --git a/src/core/SkTypefaceCache.cpp b/src/core/SkTypefaceCache.cpp
index 5a0b36c..929b795 100644
--- a/src/core/SkTypefaceCache.cpp
+++ b/src/core/SkTypefaceCache.cpp
@@ -82,7 +82,7 @@
     return sk_atomic_inc(&gFontID) + 1;
 }
 
-static SkMutex gMutex;
+SK_DECLARE_STATIC_MUTEX(gMutex);
 
 void SkTypefaceCache::Add(SkTypeface* face, SkTypeface::Style requestedStyle) {
     SkAutoMutexAcquire ama(gMutex);
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
index 240a7a4..e32acb2 100644
--- a/src/effects/SkGradientShader.cpp
+++ b/src/effects/SkGradientShader.cpp
@@ -755,7 +755,7 @@
 
     ///////////////////////////////////
 
-    static SkMutex gMutex;
+    SK_DECLARE_STATIC_MUTEX(gMutex);
     static SkBitmapCache* gCache;
     // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
     static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
diff --git a/src/gpu/SkGrTexturePixelRef.cpp b/src/gpu/SkGrTexturePixelRef.cpp
index 045ddab..4b546d6 100644
--- a/src/gpu/SkGrTexturePixelRef.cpp
+++ b/src/gpu/SkGrTexturePixelRef.cpp
@@ -16,7 +16,7 @@
 
 // since we call lockPixels recursively on fBitmap, we need a distinct mutex,
 // to avoid deadlock with the default one provided by SkPixelRef.
-static SkMutex  gROLockPixelsPixelRefMutex;
+SK_DECLARE_STATIC_MUTEX(gROLockPixelsPixelRefMutex);
 
 SkROLockPixelsPixelRef::SkROLockPixelsPixelRef() : INHERITED(&gROLockPixelsPixelRefMutex) {
 }
diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
index 2d53f7e..1d6b270 100644
--- a/src/images/SkImageRef.cpp
+++ b/src/images/SkImageRef.cpp
@@ -16,7 +16,7 @@
 //#define DUMP_IMAGEREF_LIFECYCLE
 
 // can't be static, as SkImageRef_Pool needs to see it
-SkMutex gImageRefMutex;
+SK_DECLARE_GLOBAL_MUTEX(gImageRefMutex);
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
index 6ea42c1..93e7535 100644
--- a/src/images/SkImageRef_GlobalPool.cpp
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -9,7 +9,7 @@
 #include "SkImageRefPool.h"
 #include "SkThread.h"
 
-extern SkMutex gImageRefMutex;
+extern SkBaseMutex gImageRefMutex;
 
 static SkImageRefPool gGlobalImageRefPool;
 
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 465fbe1..3aea4b8 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -797,9 +797,10 @@
 }
 
 // static
-SkMutex& SkPDFFont::CanonicalFontsMutex() {
-    // This initialization is only thread safe with gcc.
-    static SkMutex gCanonicalFontsMutex;
+SkBaseMutex& SkPDFFont::CanonicalFontsMutex() {
+    // This initialization is only thread safe with gcc, or when
+    // POD-style mutex initialization is used.
+    SK_DECLARE_STATIC_MUTEX(gCanonicalFontsMutex);
     return gCanonicalFontsMutex;
 }
 
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index ad3f57b..ec9b0e7 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -85,9 +85,10 @@
 }
 
 // static
-SkMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
-    // This initialization is only thread safe with gcc.
-    static SkMutex gCanonicalPaintsMutex;
+SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
+    // This initialization is only thread safe with gcc or when
+    // POD-style mutex initialization is used.
+    SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
     return gCanonicalPaintsMutex;
 }
 
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index b6e0939..183a4ff 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -391,9 +391,10 @@
 }
 
 // static
-SkMutex& SkPDFShader::CanonicalShadersMutex() {
-    // This initialization is only thread safe with gcc.
-    static SkMutex gCanonicalShadersMutex;
+SkBaseMutex& SkPDFShader::CanonicalShadersMutex() {
+    // This initialization is only thread safe with gcc or when
+    // POD-style mutex initialization is used.
+    SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex);
     return gCanonicalShadersMutex;
 }
 
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index a58cd37..caca577 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -88,7 +88,7 @@
 
 struct SkFaceRec;
 
-static SkMutex      gFTMutex;
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
 static int          gFTCount;
 static FT_Library   gFTLibrary;
 static SkFaceRec*   gFaceRecHead;
diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
index 37c922c..a3bd79c 100644
--- a/src/ports/SkFontHost_android.cpp
+++ b/src/ports/SkFontHost_android.cpp
@@ -67,7 +67,7 @@
 static int32_t gUniqueFontID;
 
 // this is the mutex that protects these globals
-static SkMutex gFamilyMutex;
+SK_DECLARE_STATIC_MUTEX(gFamilyMutex);
 static FamilyRec* gFamilyHead;
 static SkTDArray<NameFamilyPair> gNameList;
 
diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp
index acc5ae0..454ded3 100644
--- a/src/ports/SkFontHost_fontconfig.cpp
+++ b/src/ports/SkFontHost_fontconfig.cpp
@@ -38,7 +38,7 @@
 // Although truetype fonts can support multiple faces in a single file, at the
 // moment Skia doesn't.
 // -----------------------------------------------------------------------------
-static SkMutex global_fc_map_lock;
+SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
 static std::map<std::string, unsigned> global_fc_map;
 static std::map<unsigned, std::string> global_fc_map_inverted;
 static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
index c87b036..fa1d2ec 100644
--- a/src/ports/SkFontHost_linux.cpp
+++ b/src/ports/SkFontHost_linux.cpp
@@ -60,7 +60,7 @@
 static int32_t gUniqueFontID;
 
 // this is the mutex that protects these globals
-static SkMutex gFamilyMutex;
+SK_DECLARE_STATIC_MUTEX(gFamilyMutex);
 static FamilyRec* gFamilyHead;
 static SkTDArray<NameFamilyPair> gNameList;
 
diff --git a/src/ports/SkFontHost_mac_atsui.cpp b/src/ports/SkFontHost_mac_atsui.cpp
index b90757c..ae32036 100644
--- a/src/ports/SkFontHost_mac_atsui.cpp
+++ b/src/ports/SkFontHost_mac_atsui.cpp
@@ -16,7 +16,7 @@
 #include "SkPoint.h"
 
 const char* gDefaultfont = "Arial"; // hard code for now
-static SkMutex      gFTMutex;
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
 
 static inline SkPoint F32PtToSkPoint(const Float32Point p) {
     SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
index 3ba11a8..9c69ed7 100644
--- a/src/ports/SkFontHost_mac_coretext.cpp
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -450,7 +450,7 @@
 }
 
 static SkTypeface* GetDefaultFace() {
-    static SkMutex gMutex;
+    SK_DECLARE_STATIC_MUTEX(gMutex);
     SkAutoMutexAcquire ma(gMutex);
 
     static SkTypeface* gDefaultFace;
diff --git a/src/ports/SkFontHost_simple.cpp b/src/ports/SkFontHost_simple.cpp
index 0624d35..7a06b10 100644
--- a/src/ports/SkFontHost_simple.cpp
+++ b/src/ports/SkFontHost_simple.cpp
@@ -59,7 +59,7 @@
 static int32_t gUniqueFontID;
 
 // this is the mutex that protects these globals
-static SkMutex gFamilyMutex;
+SK_DECLARE_STATIC_MUTEX(gFamilyMutex);
 static FamilyRec* gFamilyHead;
 static SkTDArray<NameFamilyPair> gNameList;
 
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 156f51c..e78bfbb 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -448,7 +448,7 @@
     return SkFixedToFIXED(SkFloatToFixed(x));
 }
 
-static SkMutex gFTMutex;
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
 
 #define HIRES_TEXTSIZE  2048
 #define HIRES_SHIFT     11
diff --git a/src/ports/SkThread_pthread.cpp b/src/ports/SkThread_pthread.cpp
index 638e522..4750d4f 100644
--- a/src/ports/SkThread_pthread.cpp
+++ b/src/ports/SkThread_pthread.cpp
@@ -55,7 +55,7 @@
 int32_t sk_atomic_dec(int32_t* addr)
 {
     SkAutoMutexAcquire ac(gAtomicMutex);
-    
+
     int32_t value = *addr;
     *addr = value - 1;
     return value;
@@ -83,6 +83,30 @@
     }
 }
 
+#ifdef SK_USE_POSIX_THREADS
+
+SkMutex::SkMutex() {
+    int status;
+
+    status = pthread_mutex_init(&fMutex, NULL);
+    if (status != 0) {
+        print_pthread_error(status);
+        SkASSERT(0 == status);
+    }
+}
+
+SkMutex::~SkMutex() {
+    int status = pthread_mutex_destroy(&fMutex);
+
+    // only report errors on non-global mutexes
+    if (status != 0) {
+        print_pthread_error(status);
+        SkASSERT(0 == status);
+    }
+}
+
+#else // !SK_USE_POSIX_THREADS
+
 SkMutex::SkMutex() {
     if (sizeof(pthread_mutex_t) > sizeof(fStorage)) {
         SkDEBUGF(("pthread mutex size = %d\n", sizeof(pthread_mutex_t)));
@@ -124,3 +148,4 @@
     SkASSERT(0 == status);
 }
 
+#endif // !SK_USE_POSIX_THREADS
diff --git a/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp
index 0b01669..20d8cdf 100644
--- a/src/views/SkEventSink.cpp
+++ b/src/views/SkEventSink.cpp
@@ -260,7 +260,7 @@
 #include "SkTDict.h"
 
 #define kMinStringBufferSize    128
-static SkMutex                  gNamedSinkMutex;
+SK_DECLARE_STATIC_MUTEX(gNamedSinkMutex);
 static SkTDict<SkEventSinkID>   gNamedSinkIDs(kMinStringBufferSize);
 
 /** Register a name/id pair with the system. If the name already exists,