MySQL 26.7.0
Source Code Documentation
task_registry.h
Go to the documentation of this file.
1// Copyright (c) 2026, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
24#ifndef MYSQL_SCHEDULER_TASK_REGISTRY_H
25#define MYSQL_SCHEDULER_TASK_REGISTRY_H
26
27#include <optional>
28#include <vector>
34
35namespace mysql::scheduler {
36
37/// @brief This template maintains the registry of tasks, with specific
38/// object types. Those objects can be accessed simultaneously by different
39/// threads, as long as different threads access different task ids.
40template <typename Task_id_type, typename Task_object_type>
42 public:
43 using Task_object = Task_object_type;
44 using Task_object_ptr = std::unique_ptr<Task_object>;
47
48 static constexpr std::size_t cache_line_size =
50
51 static constexpr std::size_t default_capacity = 16384;
52
55 psi_params.memory_resource)),
56 m_psi(psi_params) {}
57
58 void init(std::size_t capacity) {
59 if (m_initialized) {
60 deinit();
61 m_initialized = false;
62 }
63 m_capacity = capacity;
64 init_tasks();
65 }
66
67 Task_registry(std::size_t capacity, Task_registry_psi psi_params = {})
68 : m_capacity(capacity),
70 psi_params.memory_resource)),
71 m_psi(psi_params) {
72 init_tasks();
73 }
74
75 void deinit() {
76 if (m_initialized) {
77 m_initialized = false;
78 m_tasks.clear();
79 }
80 }
81
83
84 /// @brief Calls 'func' on a given task object
85 /// @param id Task id
86 /// @param func function to be applied on a given registered task
87 /// @return true in case task was active and apply succeeded, false otherwise
88 template <typename T>
89 [[nodiscard]] bool apply(Task_id_type id, T &&func) {
90 assert(m_initialized);
91 auto &entry_ptr = get_entry(id);
92 std::scoped_lock scope_guard(entry_ptr->m_lock);
93 if (!entry_ptr->m_active.test()) {
94 return false;
95 }
96 func(entry_ptr->m_obj);
97 return true;
98 }
99
100 /// @brief Unconditionally activates and calls 'func' on a given task object
101 /// @param id Task id
102 /// @param func function to be applied on a given registered task
103 template <typename T>
104 void activate_and_apply(Task_id_type id, T &&func) {
105 assert(m_initialized);
106 auto &entry_ptr = get_entry(id);
107 std::scoped_lock scope_guard(entry_ptr->m_lock);
108 entry_ptr->m_active.test_and_set();
109 func(entry_ptr->m_obj);
110 }
111
112 [[nodiscard]] bool check_active(Task_id_type id) {
113 assert(m_initialized);
114 auto &entry_ptr = get_entry(id);
115 return entry_ptr->m_active.test();
116 }
117
118 /// @brief Calls 'func' on each registered task object
119 /// @details Don't call when a new task can be activated, also, this is
120 /// ineffective, but we don't expect to call this
121 /// @param func function to be applied on each, active task
122 template <typename T>
123 void apply_on_active(T &&func) {
124 assert(m_initialized);
125 for (std::size_t id = 0; id < m_tasks.size(); ++id) {
126 auto &entry_ptr = m_tasks[id];
127 // here: we don't lock if inactive
128 if (entry_ptr->m_active.test()) {
129 func(entry_ptr->m_obj);
130 }
131 }
132 }
133
134 /// @brief Function that registers a task, making it active
135 [[nodiscard]] bool register_entry(Task_id_type id, Task_object &&obj) {
136 assert(m_initialized);
137 auto &entry_ptr = get_entry(id);
138 std::scoped_lock scope_guard(entry_ptr->m_lock);
139 if (entry_ptr->m_active.test()) {
140 return false;
141 }
142 entry_ptr->m_id = id;
143 entry_ptr->m_obj = std::move(obj);
144 entry_ptr->m_active.test_and_set();
145 return true;
146 }
147
148 /// @brief Function activates already registered entry
149 [[nodiscard]] bool activate_entry(Task_id_type id) {
150 return activate_or_wait(id);
151 }
152
153 /// @brief Function activates already registered entry, if entry is active
154 /// it waits until its state changes to inactive.
155 [[nodiscard]] bool activate_or_wait(Task_id_type id) {
156 assert(m_initialized);
157 auto &entry_ptr = get_entry(id);
158 do {
159 if (!entry_ptr->m_active.test_and_set()) break;
160 std::this_thread::yield();
161 } while (true);
162 entry_ptr->m_id = id;
163 return true;
164 }
165
166 /// @brief Gets ref of internal object
167 /// @param id Task id
168 /// @return copy of internal task object
169 [[nodiscard]] const Task_object &get_ref(Task_id_type id) {
170 assert(m_initialized);
171 auto &entry_ptr = get_entry(id);
172 entry_ptr->m_lock.lock();
173 const Task_object &res = entry_ptr->m_obj;
174 entry_ptr->m_lock.unlock();
175 return res;
176 }
177
178 /// @brief Unprotected access to task object. Firstly obtain the guard
179 [[nodiscard]] Task_object &get(Task_id_type id) {
180 assert(m_initialized);
181 auto &entry_ptr = get_entry(id);
182 return entry_ptr->m_obj;
183 }
184
185 [[nodiscard]] auto scoped_lock(Task_id_type id) {
186 assert(m_initialized);
187 auto &entry_ptr = get_entry(id);
188 return std::scoped_lock(entry_ptr->m_lock);
189 }
190
191 [[nodiscard]] Task_object &lock(Task_id_type id) {
192 assert(m_initialized);
193 auto &entry_ptr = get_entry(id);
194 entry_ptr->m_lock.lock();
195 return entry_ptr->m_obj;
196 }
197
198 void unlock(Task_id_type id) {
199 assert(m_initialized);
200 auto &entry_ptr = get_entry(id);
201 entry_ptr->m_lock.unlock();
202 }
203
204 /// @brief Function that unregisters a task, making it inactive
205 [[nodiscard]] bool deactivate_entry(Task_id_type id) {
206 return deactivate_entry(id, [](Task_object &) -> bool { return true; });
207 }
208
209 /// @brief Function that unregisters a task, making it inactive, with functor
210 /// @param id Id of the task
211 /// @param func Functor to call on the task object before deactivation,
212 /// returns bool indicating whether to deactivate
213 template <typename Functor>
214 [[nodiscard]] bool deactivate_entry(Task_id_type id, Functor &&func) {
215 assert(m_initialized);
216 auto &entry_ptr = get_entry(id);
217 if (!entry_ptr->m_active.test()) {
218 return false;
219 }
220 bool should_deactivate = func(entry_ptr->m_obj);
221 if (should_deactivate) {
222 entry_ptr->m_active.clear();
223 return true;
224 }
225 return false;
226 }
227
228 /// @brief Function that unregisters a task, making it inactive, returns
229 /// handled object if deactivation succeeded
230 /// @return Handled object if deactivation succeeded, empty object otherwise.
231 [[nodiscard]] std::optional<Task_object> get_and_deactivate_entry(
232 Task_id_type id) {
233 assert(m_initialized);
234 auto &entry_ptr = get_entry(id);
235 if (!entry_ptr->m_active.test() || entry_ptr->m_id != id) {
236 return {};
237 }
238 auto object(entry_ptr->m_obj);
239 entry_ptr->m_active.clear();
240 return object;
241 }
242
243 private:
244 void init_tasks() {
245 assert(!m_initialized);
246 for (std::size_t idx = 0; idx < m_capacity; ++idx) {
247 m_tasks.emplace_back(new Entry(m_psi.key_mutex_entry));
248 }
249 assert(m_tasks.size() == m_capacity);
250 m_initialized = true;
251 }
252
253 struct alignas(cache_line_size) Entry {
254 Entry([[maybe_unused]] Mt_key key_mt_entry = 0)
255 : m_lock(MYSQL_CONCURRENCY_DEFINE_MT_PSI_KEY(key_mt_entry)) {}
256 Task_id_type m_id{0};
259 std::atomic_flag m_active = ATOMIC_FLAG_INIT;
260 char padding[(cache_line_size - (sizeof(m_id) + sizeof(m_obj) +
261 sizeof(m_lock) + sizeof(m_active)) %
264 };
265
266 using Entry_ptr = std::unique_ptr<Entry>;
267
268 [[nodiscard]] Entry_ptr &get_entry(Task_id_type id) {
269 assert(m_initialized);
270 auto hash_value = id.get() % m_tasks.size();
271 assert(m_tasks[hash_value]);
272 return m_tasks[hash_value];
273 }
274
275 std::size_t m_capacity{0};
276 std::vector<Entry_ptr, mysql::allocators::Allocator<Entry_ptr>> m_tasks;
277 bool m_initialized{false};
279};
280
281} // namespace mysql::scheduler
282
283#endif // MYSQL_SCHEDULER_TASK_REGISTRY_H
Allocator using a Memory_resource to do the allocation.
Definition: allocator.h:52
MySQL wrapper for a mutex, template which may be specialized with a specific implementation of a mute...
Definition: mutex_wrapper.h:38
This template maintains the registry of tasks, with specific object types.
Definition: task_registry.h:41
Task_registry_psi m_psi
Definition: task_registry.h:278
Task_object & get(Task_id_type id)
Unprotected access to task object. Firstly obtain the guard.
Definition: task_registry.h:179
static constexpr std::size_t cache_line_size
Definition: task_registry.h:48
Task_object_type Task_object
Definition: task_registry.h:43
void deinit()
Definition: task_registry.h:75
bool m_initialized
Definition: task_registry.h:277
bool activate_entry(Task_id_type id)
Function activates already registered entry.
Definition: task_registry.h:149
bool apply(Task_id_type id, T &&func)
Calls 'func' on a given task object.
Definition: task_registry.h:89
Task_registry(Task_registry_psi psi_params={})
Definition: task_registry.h:53
std::vector< Entry_ptr, mysql::allocators::Allocator< Entry_ptr > > m_tasks
Definition: task_registry.h:276
const Task_object & get_ref(Task_id_type id)
Gets ref of internal object.
Definition: task_registry.h:169
void unlock(Task_id_type id)
Definition: task_registry.h:198
~Task_registry()
Definition: task_registry.h:82
std::unique_ptr< Entry > Entry_ptr
Definition: task_registry.h:266
bool deactivate_entry(Task_id_type id)
Function that unregisters a task, making it inactive.
Definition: task_registry.h:205
Task_registry(std::size_t capacity, Task_registry_psi psi_params={})
Definition: task_registry.h:67
void apply_on_active(T &&func)
Calls 'func' on each registered task object.
Definition: task_registry.h:123
void init(std::size_t capacity)
Definition: task_registry.h:58
void init_tasks()
Definition: task_registry.h:244
std::size_t m_capacity
Definition: task_registry.h:275
void activate_and_apply(Task_id_type id, T &&func)
Unconditionally activates and calls 'func' on a given task object.
Definition: task_registry.h:104
bool activate_or_wait(Task_id_type id)
Function activates already registered entry, if entry is active it waits until its state changes to i...
Definition: task_registry.h:155
Entry_ptr & get_entry(Task_id_type id)
Definition: task_registry.h:268
bool deactivate_entry(Task_id_type id, Functor &&func)
Function that unregisters a task, making it inactive, with functor.
Definition: task_registry.h:214
auto scoped_lock(Task_id_type id)
Definition: task_registry.h:185
std::unique_ptr< Task_object > Task_object_ptr
Definition: task_registry.h:44
bool check_active(Task_id_type id)
Definition: task_registry.h:112
static constexpr std::size_t default_capacity
Definition: task_registry.h:51
bool register_entry(Task_id_type id, Task_object &&obj)
Function that registers a task, making it active.
Definition: task_registry.h:135
Task_object & lock(Task_id_type id)
Definition: task_registry.h:191
concurrency::Mutex_key Mt_key
Definition: task_registry.h:45
std::optional< Task_object > get_and_deactivate_entry(Task_id_type id)
Function that unregisters a task, making it inactive, returns handled object if deactivation succeede...
Definition: task_registry.h:231
#define T
Definition: jit_executor_value.cc:373
Allocator class that uses a polymorphic Memory_resource to allocate memory.
#define MYSQL_CONCURRENCY_DEFINE_MT_PSI_KEY(key)
Definition: mutex_srv.h:35
constexpr std::size_t hardware_destructive_interference_size
Definition: cache_line_size.h:42
Mutex_wrapper Mutex
Definition: mutex_srv.h:40
PSI_mutex_key Mutex_key
Definition: mutex_srv.h:41
Definition: base_dependency_tracker.h:41
Definition: task_registry.h:253
Mutex m_lock
Definition: task_registry.h:258
Task_object m_obj
Definition: task_registry.h:257
Entry(Mt_key key_mt_entry=0)
Definition: task_registry.h:254
Task_registry instrumentation parameters, packed into this structure to simplify construction of a Ta...
Definition: task_registry_psi.h:38
concurrency::Mutex_key key_mutex_entry
mutex: One key for mutex protecting each entry (1 mutex per 1 entry)
Definition: task_registry_psi.h:40
unsigned long id[MAX_DEAD]
Definition: xcom_base.cc:510