Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef SkOnce_DEFINED
      9 #define SkOnce_DEFINED
     10 
     11 // SkOnce.h defines SK_DECLARE_STATIC_ONCE and SkOnce(), which you can use
     12 // together to create a threadsafe way to call a function just once.  This
     13 // is particularly useful for lazy singleton initialization. E.g.
     14 //
     15 // static void set_up_my_singleton(Singleton** singleton) {
     16 //     *singleton = new Singleton(...);
     17 // }
     18 // ...
     19 // const Singleton& GetSingleton() {
     20 //     static Singleton* singleton = NULL;
     21 //     SK_DECLARE_STATIC_ONCE(once);
     22 //     SkOnce(&once, set_up_my_singleton, &singleton);
     23 //     SkASSERT(NULL != singleton);
     24 //     return *singleton;
     25 // }
     26 //
     27 // OnceTest.cpp also should serve as a few other simple examples.
     28 
     29 #include "SkThread.h"
     30 #include "SkTypes.h"
     31 
     32 #ifdef SK_USE_POSIX_THREADS
     33 #  define SK_ONCE_INIT { false, { PTHREAD_MUTEX_INITIALIZER } }
     34 #else
     35 #  define SK_ONCE_INIT { false, SkBaseMutex() }
     36 #endif
     37 
     38 #define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name = SK_ONCE_INIT
     39 
     40 struct SkOnceFlag;  // If manually created, initialize with SkOnceFlag once = SK_ONCE_INIT
     41 
     42 template <typename Func, typename Arg>
     43 inline void SkOnce(SkOnceFlag* once, Func f, Arg arg);
     44 
     45 //  ----------------------  Implementation details below here. -----------------------------
     46 
     47 struct SkOnceFlag {
     48     bool done;
     49     SkBaseMutex mutex;
     50 };
     51 
     52 // TODO(bungeman, mtklein): move all these *barrier* functions to SkThread when refactoring lands.
     53 
     54 #ifdef SK_BUILD_FOR_WIN
     55 #include <intrin.h>
     56 inline static void compiler_barrier() {
     57     _ReadWriteBarrier();
     58 }
     59 #else
     60 inline static void compiler_barrier() {
     61     asm volatile("" : : : "memory");
     62 }
     63 #endif
     64 
     65 inline static void full_barrier_on_arm() {
     66 #ifdef SK_CPU_ARM
     67 #if SK_ARM_ARCH >= 7
     68     asm volatile("dmb" : : : "memory");
     69 #else
     70     asm volatile("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory");
     71 #endif
     72 #endif
     73 }
     74 
     75 // On every platform, we issue a compiler barrier to prevent it from reordering
     76 // code.  That's enough for platforms like x86 where release and acquire
     77 // barriers are no-ops.  On other platforms we may need to be more careful;
     78 // ARM, in particular, needs real code for both acquire and release.  We use a
     79 // full barrier, which acts as both, because that the finest precision ARM
     80 // provides.
     81 
     82 inline static void release_barrier() {
     83     compiler_barrier();
     84     full_barrier_on_arm();
     85 }
     86 
     87 inline static void acquire_barrier() {
     88     compiler_barrier();
     89     full_barrier_on_arm();
     90 }
     91 
     92 // We've pulled a pretty standard double-checked locking implementation apart
     93 // into its main fast path and a slow path that's called when we suspect the
     94 // one-time code hasn't run yet.
     95 
     96 // This is the guts of the code, called when we suspect the one-time code hasn't been run yet.
     97 // This should be rarely called, so we separate it from SkOnce and don't mark it as inline.
     98 // (We don't mind if this is an actual function call, but odds are it'll be inlined anyway.)
     99 template <typename Func, typename Arg>
    100 static void sk_once_slow(SkOnceFlag* once, Func f, Arg arg) {
    101     const SkAutoMutexAcquire lock(once->mutex);
    102     if (!once->done) {
    103         f(arg);
    104         // Also known as a store-store/load-store barrier, this makes sure that the writes
    105         // done before here---in particular, those done by calling f(arg)---are observable
    106         // before the writes after the line, *done = true.
    107         //
    108         // In version control terms this is like saying, "check in the work up
    109         // to and including f(arg), then check in *done=true as a subsequent change".
    110         //
    111         // We'll use this in the fast path to make sure f(arg)'s effects are
    112         // observable whenever we observe *done == true.
    113         release_barrier();
    114         once->done = true;
    115     }
    116 }
    117 
    118 // We nabbed this code from the dynamic_annotations library, and in their honor
    119 // we check the same define.  If you find yourself wanting more than just
    120 // ANNOTATE_BENIGN_RACE, it might make sense to pull that in as a dependency
    121 // rather than continue to reproduce it here.
    122 
    123 #if DYNAMIC_ANNOTATIONS_ENABLED
    124 // TSAN provides this hook to supress a known-safe apparent race.
    125 extern "C" {
    126 void AnnotateBenignRace(const char* file, int line, const volatile void* mem, const char* desc);
    127 }
    128 #define ANNOTATE_BENIGN_RACE(mem, desc) AnnotateBenignRace(__FILE__, __LINE__, mem, desc)
    129 #else
    130 #define ANNOTATE_BENIGN_RACE(mem, desc)
    131 #endif
    132 
    133 // This is our fast path, called all the time.  We do really want it to be inlined.
    134 template <typename Func, typename Arg>
    135 inline void SkOnce(SkOnceFlag* once, Func f, Arg arg) {
    136     ANNOTATE_BENIGN_RACE(&(once->done), "Don't worry TSAN, we're sure this is safe.");
    137     if (!once->done) {
    138         sk_once_slow(once, f, arg);
    139     }
    140     // Also known as a load-load/load-store barrier, this acquire barrier makes
    141     // sure that anything we read from memory---in particular, memory written by
    142     // calling f(arg)---is at least as current as the value we read from once->done.
    143     //
    144     // In version control terms, this is a lot like saying "sync up to the
    145     // commit where we wrote once->done = true".
    146     //
    147     // The release barrier in sk_once_slow guaranteed that once->done = true
    148     // happens after f(arg), so by syncing to once->done = true here we're
    149     // forcing ourselves to also wait until the effects of f(arg) are readble.
    150     acquire_barrier();
    151 }
    152 
    153 #undef ANNOTATE_BENIGN_RACE
    154 
    155 #endif  // SkOnce_DEFINED
    156