ryujin 2.1.1 revision ee5cbcbf2346c1299c942d0e1f13b46449973c18
Loading...
Searching...
No Matches
lazy.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2023 - 2024 by Matthias Maier
4// Copyright (C) 2024 - 2026 by the ryujin authors
5//
6
7#pragma once
8
9#include <compile_time_options.h>
10
11#include <deal.II/base/exceptions.h>
12#include <deal.II/base/memory_consumption.h>
13#include <deal.II/base/mutex.h>
14
15#include <atomic>
16#include <mutex>
17#include <optional>
18
19
20namespace ryujin
21{
28 template <typename T>
29 class Lazy
30 {
31 public:
33 Lazy(const Lazy &other);
34 Lazy(Lazy &&other) noexcept;
35
36 Lazy &operator=(const Lazy &other);
37 Lazy &operator=(Lazy &&other) noexcept;
38
39 void reset() noexcept;
40
41 template <typename Callable>
42 void ensure_initialized(const Callable &creator) const;
43
44 bool has_value() const;
45
46 const T &value() const;
47 T &value();
48
49 private:
50 mutable std::optional<T> object;
51 mutable std::atomic<bool> object_is_initialized;
52 mutable dealii::Threads::Mutex initialization_mutex;
53 };
54
55
56 // ------------------------------- inline functions --------------------------
57
58
59 template <typename T>
60 inline Lazy<T>::Lazy()
61 : object_is_initialized(false)
62 {
63 }
64
65
66 template <typename T>
67 inline Lazy<T>::Lazy(const Lazy &other)
68 : object(other.object)
69 {
70 object_is_initialized.store(other.object_is_initialized.load());
71 }
72
73
74 template <typename T>
75 inline Lazy<T>::Lazy(Lazy &&other) noexcept
76 : object(std::move(other.object))
77 {
78 object_is_initialized.store(other.object_is_initialized.load());
79
80 other.object_is_initialized.store(false);
81 other.object.reset();
82 }
83
84
85 template <typename T>
86 inline Lazy<T> &Lazy<T>::operator=(const Lazy &other)
87 {
88 object = other.object;
89 object_is_initialized.store(other.object_is_initialized.load());
90
91 return *this;
92 }
93
94
95 template <typename T>
96 inline Lazy<T> &Lazy<T>::operator=(Lazy &&other) noexcept
97 {
98 object = std::move(other.object);
99 object_is_initialized.store(other.object_is_initialized.load());
100
101 other.object_is_initialized.store(false);
102 other.object.reset();
103
104 return *this;
105 }
106
107
108 template <typename T>
109 inline void Lazy<T>::reset() noexcept
110 {
111 object_is_initialized.store(false);
112 object.reset();
113 }
114
115
116 template <typename T>
117 template <typename Callable>
118 inline DEAL_II_ALWAYS_INLINE void
119 Lazy<T>::ensure_initialized(const Callable &creator) const
120 {
121 // Check the object_is_initialized atomic with "acquire" semantics.
122 if (!object_is_initialized.load(std::memory_order_acquire))
123#ifdef DEAL_II_HAVE_CXX20
124 [[unlikely]]
125#endif
126 {
127 std::lock_guard<std::mutex> lock(initialization_mutex);
128
129 //
130 // Check again. If this thread won the race to the lock then we
131 // initialize the object. Otherwise another thread has already
132 // initialized the object and flipped the object_is_initialized
133 // bit. (Here, the initialization_mutex ensures consistent ordering
134 // with a memory fence, so we will observe the updated bool without
135 // acquire semantics.)
136 //
137 if (!object_is_initialized.load(std::memory_order_relaxed)) {
138 Assert(object.has_value() == false, dealii::ExcInternalError());
139 object.emplace(std::move(creator()));
140
141 // Flip the object_is_initialized boolean with "release" semantics.
142 object_is_initialized.store(true, std::memory_order_release);
143 }
144 }
145 }
146
147
148 template <typename T>
149 inline DEAL_II_ALWAYS_INLINE bool Lazy<T>::has_value() const
150 {
151 return (object_is_initialized && object.has_value());
152 }
153
154
155 template <typename T>
156 inline DEAL_II_ALWAYS_INLINE const T &Lazy<T>::value() const
157 {
158 Assert(object_is_initialized && object.has_value(),
159 dealii::ExcMessage(
160 "value() has been called but the contained object has not been "
161 "initialized. Did you forget to call 'ensure_initialized()' "
162 "first?"));
163
164 return object.value();
165 }
166
167
168 template <typename T>
169 inline DEAL_II_ALWAYS_INLINE T &Lazy<T>::value()
170 {
171 Assert(object_is_initialized && object.has_value(),
172 dealii::ExcMessage(
173 "value() has been called but the contained object has not been "
174 "initialized. Did you forget to call 'ensure_initialized()' "
175 "first?"));
176
177 return object.value();
178 }
179
180} // namespace ryujin
Lazy & operator=(const Lazy &other)
Definition lazy.h:86
Lazy & operator=(Lazy &&other) noexcept
Definition lazy.h:96
void reset() noexcept
Definition lazy.h:109
Lazy(const Lazy &other)
Definition lazy.h:67
const T & value() const
Definition lazy.h:156
Lazy(Lazy &&other) noexcept
Definition lazy.h:75
bool has_value() const
Definition lazy.h:149
void ensure_initialized(const Callable &creator) const