| OLD | NEW |
| (Empty) |
| 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 #define SK_ONCE_INIT { false, { 0, SkDEBUGCODE(0) } } | |
| 33 #define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name = SK_ONCE_INIT | |
| 34 | |
| 35 struct SkOnceFlag; // If manually created, initialize with SkOnceFlag once = SK
_ONCE_INIT | |
| 36 | |
| 37 template <typename Func, typename Arg> | |
| 38 inline void SkOnce(SkOnceFlag* once, Func f, Arg arg); | |
| 39 | |
| 40 // ---------------------- Implementation details below here. -----------------
------------ | |
| 41 | |
| 42 struct SkOnceFlag { | |
| 43 bool done; | |
| 44 SkSpinlock lock; | |
| 45 }; | |
| 46 | |
| 47 // TODO(bungeman, mtklein): move all these *barrier* functions to SkThread when
refactoring lands. | |
| 48 | |
| 49 #ifdef SK_BUILD_FOR_WIN | |
| 50 # include <intrin.h> | |
| 51 inline static void compiler_barrier() { | |
| 52 _ReadWriteBarrier(); | |
| 53 } | |
| 54 #else | |
| 55 inline static void compiler_barrier() { | |
| 56 asm volatile("" : : : "memory"); | |
| 57 } | |
| 58 #endif | |
| 59 | |
| 60 inline static void full_barrier_on_arm() { | |
| 61 #ifdef SK_CPU_ARM | |
| 62 # if SK_ARM_ARCH >= 7 | |
| 63 asm volatile("dmb" : : : "memory"); | |
| 64 # else | |
| 65 asm volatile("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory"); | |
| 66 # endif | |
| 67 #endif | |
| 68 } | |
| 69 | |
| 70 // On every platform, we issue a compiler barrier to prevent it from reordering | |
| 71 // code. That's enough for platforms like x86 where release and acquire | |
| 72 // barriers are no-ops. On other platforms we may need to be more careful; | |
| 73 // ARM, in particular, needs real code for both acquire and release. We use a | |
| 74 // full barrier, which acts as both, because that the finest precision ARM | |
| 75 // provides. | |
| 76 | |
| 77 inline static void release_barrier() { | |
| 78 compiler_barrier(); | |
| 79 full_barrier_on_arm(); | |
| 80 } | |
| 81 | |
| 82 inline static void acquire_barrier() { | |
| 83 compiler_barrier(); | |
| 84 full_barrier_on_arm(); | |
| 85 } | |
| 86 | |
| 87 // We've pulled a pretty standard double-checked locking implementation apart | |
| 88 // into its main fast path and a slow path that's called when we suspect the | |
| 89 // one-time code hasn't run yet. | |
| 90 | |
| 91 // This is the guts of the code, called when we suspect the one-time code hasn't
been run yet. | |
| 92 // This should be rarely called, so we separate it from SkOnce and don't mark it
as inline. | |
| 93 // (We don't mind if this is an actual function call, but odds are it'll be inli
ned anyway.) | |
| 94 template <typename Func, typename Arg> | |
| 95 static void sk_once_slow(SkOnceFlag* once, Func f, Arg arg) { | |
| 96 const SkAutoSpinlock lock(&once->lock); | |
| 97 if (!once->done) { | |
| 98 f(arg); | |
| 99 // Also known as a store-store/load-store barrier, this makes sure that
the writes | |
| 100 // done before here---in particular, those done by calling f(arg)---are
observable | |
| 101 // before the writes after the line, *done = true. | |
| 102 // | |
| 103 // In version control terms this is like saying, "check in the work up | |
| 104 // to and including f(arg), then check in *done=true as a subsequent cha
nge". | |
| 105 // | |
| 106 // We'll use this in the fast path to make sure f(arg)'s effects are | |
| 107 // observable whenever we observe *done == true. | |
| 108 release_barrier(); | |
| 109 once->done = true; | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 // We nabbed this code from the dynamic_annotations library, and in their honor | |
| 114 // we check the same define. If you find yourself wanting more than just | |
| 115 // ANNOTATE_BENIGN_RACE, it might make sense to pull that in as a dependency | |
| 116 // rather than continue to reproduce it here. | |
| 117 | |
| 118 #if DYNAMIC_ANNOTATIONS_ENABLED | |
| 119 // TSAN provides this hook to supress a known-safe apparent race. | |
| 120 extern "C" { | |
| 121 void AnnotateBenignRace(const char* file, int line, const volatile void* mem, co
nst char* desc); | |
| 122 } | |
| 123 #define ANNOTATE_BENIGN_RACE(mem, desc) AnnotateBenignRace(__FILE__, __LINE__, m
em, desc) | |
| 124 #else | |
| 125 #define ANNOTATE_BENIGN_RACE(mem, desc) | |
| 126 #endif | |
| 127 | |
| 128 // This is our fast path, called all the time. We do really want it to be inlin
ed. | |
| 129 template <typename Func, typename Arg> | |
| 130 inline void SkOnce(SkOnceFlag* once, Func f, Arg arg) { | |
| 131 ANNOTATE_BENIGN_RACE(&(once->done), "Don't worry TSAN, we're sure this is sa
fe."); | |
| 132 if (!once->done) { | |
| 133 sk_once_slow(once, f, arg); | |
| 134 } | |
| 135 // Also known as a load-load/load-store barrier, this acquire barrier makes | |
| 136 // sure that anything we read from memory---in particular, memory written by | |
| 137 // calling f(arg)---is at least as current as the value we read from once->d
one. | |
| 138 // | |
| 139 // In version control terms, this is a lot like saying "sync up to the | |
| 140 // commit where we wrote once->done = true". | |
| 141 // | |
| 142 // The release barrier in sk_once_slow guaranteed that once->done = true | |
| 143 // happens after f(arg), so by syncing to once->done = true here we're | |
| 144 // forcing ourselves to also wait until the effects of f(arg) are readble. | |
| 145 acquire_barrier(); | |
| 146 } | |
| 147 | |
| 148 #undef ANNOTATE_BENIGN_RACE | |
| 149 | |
| 150 #endif // SkOnce_DEFINED | |
| OLD | NEW |