blob: 2a0b1dfd83cfd3be94b50e6de6d6348d3df83064 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2018 The Chromium Authors
Gabriel Charettef297f012018-01-17 20:59:072// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Lei Zhang6e174342021-04-21 03:57:205#ifndef BASE_LAZY_INSTANCE_HELPERS_H_
6#define BASE_LAZY_INSTANCE_HELPERS_H_
Gabriel Charettef297f012018-01-17 20:59:077
Benoit Lizeb82171a62022-06-17 13:37:588#include <atomic>
9#include <cstdint>
Peter Kasting134ef9af2024-12-28 02:30:0910
Gabriel Charettef297f012018-01-17 20:59:0711#include "base/base_export.h"
Hans Wennborg7b533712020-06-22 20:52:2712#include "base/check.h"
Gabriel Charettef297f012018-01-17 20:59:0713
14// Helper methods used by LazyInstance and a few other base APIs for thread-safe
15// lazy construction.
16
17namespace base {
18namespace internal {
19
20// Our AtomicWord doubles as a spinlock, where a value of
21// kLazyInstanceStateCreating means the spinlock is being held for creation.
Benoit Lizeb82171a62022-06-17 13:37:5822constexpr uintptr_t kLazyInstanceStateCreating = 1;
Gabriel Charettef297f012018-01-17 20:59:0723
24// Helper for GetOrCreateLazyPointer(). Checks if instance needs to be created.
25// If so returns true otherwise if another thread has beat us, waits for
26// instance to be created and returns false.
Benoit Lizeb82171a62022-06-17 13:37:5827BASE_EXPORT bool NeedsLazyInstance(std::atomic<uintptr_t>& state);
Gabriel Charettef297f012018-01-17 20:59:0728
29// Helper for GetOrCreateLazyPointer(). After creating an instance, this is
30// called to register the dtor to be called at program exit and to update the
31// atomic state to hold the |new_instance|
Benoit Lizeb82171a62022-06-17 13:37:5832BASE_EXPORT void CompleteLazyInstance(std::atomic<uintptr_t>& state,
33 uintptr_t new_instance,
Gabriel Charettef297f012018-01-17 20:59:0734 void (*destructor)(void*),
35 void* destructor_arg);
36
Gabriel Charetted55aad502018-01-26 18:07:5137} // namespace internal
38
39namespace subtle {
40
41// If |state| is uninitialized (zero), constructs a value using
42// |creator_func(creator_arg)|, stores it into |state| and registers
43// |destructor(destructor_arg)| to be called when the current AtExitManager goes
44// out of scope. Then, returns the value stored in |state|. It is safe to have
Gabriel Charettef297f012018-01-17 20:59:0745// concurrent calls to this function with the same |state|. |creator_func| may
46// return nullptr if it doesn't want to create an instance anymore (e.g. on
47// shutdown), it is from then on required to return nullptr to all callers (ref.
Gabriel Charetted55aad502018-01-26 18:07:5148// StaticMemorySingletonTraits). In that case, callers need to synchronize
49// before |creator_func| may return a non-null instance again (ref.
Gabriel Charettef297f012018-01-17 20:59:0750// StaticMemorySingletonTraits::ResurectForTesting()).
Gabriel Charetted55aad502018-01-26 18:07:5151// Implementation note on |creator_func/creator_arg|. It makes for ugly adapters
52// but it avoids redundant template instantiations (e.g. saves 27KB in
53// chrome.dll) because linker is able to fold these for multiple Types but
54// couldn't with the more advanced CreatorFunc template type which in turn
55// improves code locality (and application startup) -- ref.
56// https://chromium-review.googlesource.com/c/chromium/src/+/530984/5/base/lazy_instance.h#140,
57// worsened by https://chromium-review.googlesource.com/c/chromium/src/+/868013
58// and caught then as https://crbug.com/804034.
59template <typename Type>
Benoit Lizeb82171a62022-06-17 13:37:5860Type* GetOrCreateLazyPointer(std::atomic<uintptr_t>& state,
Gabriel Charetted55aad502018-01-26 18:07:5161 Type* (*creator_func)(void*),
62 void* creator_arg,
Gabriel Charettef297f012018-01-17 20:59:0763 void (*destructor)(void*),
64 void* destructor_arg) {
Gabriel Charetted55aad502018-01-26 18:07:5165 DCHECK(creator_func);
Gabriel Charettef297f012018-01-17 20:59:0766
67 // If any bit in the created mask is true, the instance has already been
68 // fully constructed.
Benoit Lizeb82171a62022-06-17 13:37:5869 constexpr uintptr_t kLazyInstanceCreatedMask =
Gabriel Charettef297f012018-01-17 20:59:0770 ~internal::kLazyInstanceStateCreating;
71
72 // We will hopefully have fast access when the instance is already created.
73 // Since a thread sees |state| == 0 or kLazyInstanceStateCreating at most
74 // once, the load is taken out of NeedsLazyInstance() as a fast-path. The load
75 // has acquire memory ordering as a thread which sees |state| > creating needs
76 // to acquire visibility over the associated data. Pairing Release_Store is in
77 // CompleteLazyInstance().
Benoit Lizeb82171a62022-06-17 13:37:5878 uintptr_t instance = state.load(std::memory_order_acquire);
Gabriel Charetted55aad502018-01-26 18:07:5179 if (!(instance & kLazyInstanceCreatedMask)) {
80 if (internal::NeedsLazyInstance(state)) {
81 // This thread won the race and is now responsible for creating the
82 // instance and storing it back into |state|.
Benoit Lizeb82171a62022-06-17 13:37:5883 instance = reinterpret_cast<uintptr_t>((*creator_func)(creator_arg));
Gabriel Charetted55aad502018-01-26 18:07:5184 internal::CompleteLazyInstance(state, instance, destructor,
85 destructor_arg);
86 } else {
87 // This thread lost the race but now has visibility over the constructed
88 // instance (NeedsLazyInstance() doesn't return until the constructing
89 // thread releases the instance via CompleteLazyInstance()).
Benoit Lizeb82171a62022-06-17 13:37:5890 instance = state.load(std::memory_order_acquire);
Gabriel Charetted55aad502018-01-26 18:07:5191 DCHECK(instance & kLazyInstanceCreatedMask);
92 }
Gabriel Charettef297f012018-01-17 20:59:0793 }
Gabriel Charetted55aad502018-01-26 18:07:5194 return reinterpret_cast<Type*>(instance);
Gabriel Charettef297f012018-01-17 20:59:0795}
96
Gabriel Charetted55aad502018-01-26 18:07:5197} // namespace subtle
98
Gabriel Charettef297f012018-01-17 20:59:0799} // namespace base
100
Lei Zhang6e174342021-04-21 03:57:20101#endif // BASE_LAZY_INSTANCE_HELPERS_H_