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 // Before trying SkOnce, see if SkLazyPtr or SkLazyFnPtr will work for you.
     12 // They're smaller and faster, if slightly less versatile.
     13 
     14 
     15 // SkOnce.h defines SK_DECLARE_STATIC_ONCE and SkOnce(), which you can use
     16 // together to create a threadsafe way to call a function just once.  E.g.
     17 //
     18 // static void register_my_stuff(GlobalRegistry* registry) {
     19 //     registry->register(...);
     20 // }
     21 // ...
     22 // void EnsureRegistered() {
     23 //     SK_DECLARE_STATIC_ONCE(once);
     24 //     SkOnce(&once, register_my_stuff, GetGlobalRegistry());
     25 // }
     26 //
     27 // No matter how many times you call EnsureRegistered(), register_my_stuff will be called just once.
     28 // OnceTest.cpp also should serve as a few other simple examples.
     29 
     30 #include "SkDynamicAnnotations.h"
     31 #include "SkThread.h"
     32 #include "SkTypes.h"
     33 
     34 // This must be used in a global or function scope, not as a class member.
     35 #define SK_DECLARE_STATIC_ONCE(name) static SkOnceFlag name
     36 
     37 class SkOnceFlag;
     38 
     39 inline void SkOnce(SkOnceFlag* once, void (*f)());
     40 
     41 template <typename Arg>
     42 inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg);
     43 
     44 // If you've already got a lock and a flag to use, this variant lets you avoid an extra SkOnceFlag.
     45 template <typename Lock>
     46 inline void SkOnce(bool* done, Lock* lock, void (*f)());
     47 
     48 template <typename Lock, typename Arg>
     49 inline void SkOnce(bool* done, Lock* lock, void (*f)(Arg), Arg arg);
     50 
     51 //  ----------------------  Implementation details below here. -----------------------------
     52 
     53 // This class has no constructor and must be zero-initialized (the macro above does this).
     54 class SkOnceFlag {
     55 public:
     56     bool* mutableDone() { return &fDone; }
     57 
     58     void acquire() {
     59         // To act as a mutex, this needs an acquire barrier on success.
     60         // sk_atomic_cas doesn't guarantee this ...
     61         while (!sk_atomic_cas(&fSpinlock, 0, 1)) {
     62             // spin
     63         }
     64         // ... so make sure to issue one of our own.
     65         SkAssertResult(sk_acquire_load(&fSpinlock));
     66     }
     67 
     68     void release() {
     69         // To act as a mutex, this needs a release barrier.  sk_atomic_cas guarantees this.
     70         SkAssertResult(sk_atomic_cas(&fSpinlock, 1, 0));
     71     }
     72 
     73 private:
     74     bool fDone;
     75     int32_t fSpinlock;
     76 };
     77 
     78 // We've pulled a pretty standard double-checked locking implementation apart
     79 // into its main fast path and a slow path that's called when we suspect the
     80 // one-time code hasn't run yet.
     81 
     82 // This is the guts of the code, called when we suspect the one-time code hasn't been run yet.
     83 // This should be rarely called, so we separate it from SkOnce and don't mark it as inline.
     84 // (We don't mind if this is an actual function call, but odds are it'll be inlined anyway.)
     85 template <typename Lock, typename Arg>
     86 static void sk_once_slow(bool* done, Lock* lock, void (*f)(Arg), Arg arg) {
     87     lock->acquire();
     88     if (!*done) {
     89         f(arg);
     90         // Also known as a store-store/load-store barrier, this makes sure that the writes
     91         // done before here---in particular, those done by calling f(arg)---are observable
     92         // before the writes after the line, *done = true.
     93         //
     94         // In version control terms this is like saying, "check in the work up
     95         // to and including f(arg), then check in *done=true as a subsequent change".
     96         //
     97         // We'll use this in the fast path to make sure f(arg)'s effects are
     98         // observable whenever we observe *done == true.
     99         sk_release_store(done, true);
    100     }
    101     lock->release();
    102 }
    103 
    104 // This is our fast path, called all the time.  We do really want it to be inlined.
    105 template <typename Lock, typename Arg>
    106 inline void SkOnce(bool* done, Lock* lock, void (*f)(Arg), Arg arg) {
    107     if (!SK_ANNOTATE_UNPROTECTED_READ(*done)) {
    108         sk_once_slow(done, lock, f, arg);
    109     }
    110     // Also known as a load-load/load-store barrier, this acquire barrier makes
    111     // sure that anything we read from memory---in particular, memory written by
    112     // calling f(arg)---is at least as current as the value we read from done.
    113     //
    114     // In version control terms, this is a lot like saying "sync up to the
    115     // commit where we wrote done = true".
    116     //
    117     // The release barrier in sk_once_slow guaranteed that done = true
    118     // happens after f(arg), so by syncing to done = true here we're
    119     // forcing ourselves to also wait until the effects of f(arg) are readble.
    120     SkAssertResult(sk_acquire_load(done));
    121 }
    122 
    123 template <typename Arg>
    124 inline void SkOnce(SkOnceFlag* once, void (*f)(Arg), Arg arg) {
    125     return SkOnce(once->mutableDone(), once, f, arg);
    126 }
    127 
    128 // Calls its argument.
    129 // This lets us use functions that take no arguments with SkOnce methods above.
    130 // (We pass _this_ as the function and the no-arg function as its argument.  Cute eh?)
    131 static void sk_once_no_arg_adaptor(void (*f)()) {
    132     f();
    133 }
    134 
    135 inline void SkOnce(SkOnceFlag* once, void (*func)()) {
    136     return SkOnce(once, sk_once_no_arg_adaptor, func);
    137 }
    138 
    139 template <typename Lock>
    140 inline void SkOnce(bool* done, Lock* lock, void (*func)()) {
    141     return SkOnce(done, lock, sk_once_no_arg_adaptor, func);
    142 }
    143 
    144 #endif  // SkOnce_DEFINED
    145