Line data Source code
1 : // Copyright 2012 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : // emulates google3/base/once.h
6 : //
7 : // This header is intended to be included only by v8's internal code. Users
8 : // should not use this directly.
9 : //
10 : // This is basically a portable version of pthread_once().
11 : //
12 : // This header declares:
13 : // * A type called OnceType.
14 : // * A macro V8_DECLARE_ONCE() which declares a (global) variable of type
15 : // OnceType.
16 : // * A function CallOnce(OnceType* once, void (*init_func)()).
17 : // This function, when invoked multiple times given the same OnceType object,
18 : // will invoke init_func on the first call only, and will make sure none of
19 : // the calls return before that first call to init_func has finished.
20 : //
21 : // Additionally, the following features are supported:
22 : // * A macro V8_ONCE_INIT which is expanded into the expression used to
23 : // initialize a OnceType. This is only useful when clients embed a OnceType
24 : // into a structure of their own and want to initialize it statically.
25 : // * The user can provide a parameter which CallOnce() forwards to the
26 : // user-provided function when it is called. Usage example:
27 : // CallOnce(&my_once, &MyFunctionExpectingIntArgument, 10);
28 : // * This implementation guarantees that OnceType is a POD (i.e. no static
29 : // initializer generated).
30 : //
31 : // This implements a way to perform lazy initialization. It's more efficient
32 : // than using mutexes as no lock is needed if initialization has already
33 : // happened.
34 : //
35 : // Example usage:
36 : // void Init();
37 : // V8_DECLARE_ONCE(once_init);
38 : //
39 : // // Calls Init() exactly once.
40 : // void InitOnce() {
41 : // CallOnce(&once_init, &Init);
42 : // }
43 : //
44 : // Note that if CallOnce() is called before main() has begun, it must
45 : // only be called by the thread that will eventually call main() -- that is,
46 : // the thread that performs dynamic initialization. In general this is a safe
47 : // assumption since people don't usually construct threads before main() starts,
48 : // but it is technically not guaranteed. Unfortunately, Win32 provides no way
49 : // whatsoever to statically-initialize its synchronization primitives, so our
50 : // only choice is to assume that dynamic initialization is single-threaded.
51 :
52 : #ifndef V8_BASE_ONCE_H_
53 : #define V8_BASE_ONCE_H_
54 :
55 : #include <stddef.h>
56 : #include <atomic>
57 : #include <functional>
58 :
59 : #include "src/base/base-export.h"
60 :
61 : namespace v8 {
62 : namespace base {
63 :
64 : using OnceType = std::atomic<uint8_t>;
65 :
66 : #define V8_ONCE_INIT \
67 : { 0 }
68 :
69 : #define V8_DECLARE_ONCE(NAME) ::v8::base::OnceType NAME
70 :
71 : enum : uint8_t {
72 : ONCE_STATE_UNINITIALIZED = 0,
73 : ONCE_STATE_EXECUTING_FUNCTION = 1,
74 : ONCE_STATE_DONE = 2
75 : };
76 :
77 : using PointerArgFunction = void (*)(void* arg);
78 :
79 : template <typename T>
80 : struct OneArgFunction {
81 : using type = void (*)(T);
82 : };
83 :
84 : V8_BASE_EXPORT void CallOnceImpl(OnceType* once,
85 : std::function<void()> init_func);
86 :
87 127844 : inline void CallOnce(OnceType* once, std::function<void()> init_func) {
88 127844 : if (once->load(std::memory_order_acquire) != ONCE_STATE_DONE) {
89 145137 : CallOnceImpl(once, init_func);
90 : }
91 127843 : }
92 :
93 : template <typename Arg>
94 83293338 : inline void CallOnce(OnceType* once,
95 : typename OneArgFunction<Arg*>::type init_func, Arg* arg) {
96 83293338 : if (once->load(std::memory_order_acquire) != ONCE_STATE_DONE) {
97 1396623 : CallOnceImpl(once, [=]() { init_func(arg); });
98 : }
99 83293338 : }
100 :
101 : } // namespace base
102 : } // namespace v8
103 :
104 : #endif // V8_BASE_ONCE_H_
|