MySQL 9.1.0
Source Code Documentation
weak_service_reference.h
Go to the documentation of this file.
1/* Copyright (c) 2024, Oracle and/or its affiliates.
2
3This program is free software; you can redistribute it and/or modify
4it under the terms of the GNU General Public License, version 2.0,
5as published by the Free Software Foundation.
6
7This program is designed to work with certain software (including
8but not limited to OpenSSL) that is licensed under separate terms,
9as designated in a particular file or component or in included license
10documentation. The authors of MySQL hereby grant you an additional
11permission to link the program and your derivative works with the
12separately licensed software that they have either included with
13the program or referenced in the documentation.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License, version 2.0, for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24#ifndef WEAK_REQUIRES_SERVICE_GUARD
25#define WEAK_REQUIRES_SERVICE_GUARD
26
27#include <atomic>
28#include <cassert>
29#include <functional>
30#include <string>
36/**
37 @brief A utility class to implement a delayed service reference
38
39 This class allows a component to have a "weak" reference to a component
40 service.
41
42 @par Operation in default mode (keep references = true)
43 In its default mode (keep references) when the weak reference is initialized
44 the class checks if the service required already has implementations. If it
45 does, the class takes a reference to the default one, calls the supplied
46 function and keeps the reference. If there's no implementation of the service
47 the class registers a listener to the
48 dynamic_loader_services_loaded_notification broadcast service by implementing
49 a dynamic_loader_services_loaded_notification service that, when called by the
50 dynamic loader, will take a reference to the desired service, call the
51 function supplied and keep the reference until deinit. And then it sets a flag
52 preventing any further calls to the function.
53
54 @par
55 At deinit time, if there's an active reference, deinit calls the supplied
56 function and passes it as a parameter. And then releases the reference.
57 Otherwise, no call of the supplied deinit function is done.
58 It also unregisters the dynamic_loader_services_loaded_notification callback,
59 if registered.
60
61 @par Operation in do-not-keep-references mode.
62 When the weak reference is initialized the class checks if the service
63 required already has implementations. If it does, then the class takes a
64 reference to the default implementation, calls the supplied init function and
65 <b>releases</b> the reference. It then proceeds to <b>unconditioanlly</b>
66 register a listener to the dynamic_loader_services_loaded_notification
67 broadcast service by implementing a
68 dynamic_loader_services_loaded_notification service that, when called by the
69 dynamic loader, will take a reference to the desired service, call the
70 function supplied and then release the reference.
71 Every time a new implementation is registered, the notification callback will
72 be called so the weak reference can re-register itself again.
73
74 @par
75 At deinit time, deinit tries to acquire the required service and, if
76 successful, calls the supplied deinit function and passes it as a parameter.
77 Note that if the service implementation has been undefined in the meanwhile no
78 call of the supplied deinit function is done.
79
80 @warning
81 Do not use the do-not-keep-references mode for anyting but the server!
82 It is only justified for the server component (aka the bootstrap component)
83 because if it doesn't use it no one will be able to unload the component
84 implementing the service once captured by the bootstrap component.
85 Yes, unloading service implementation component would be impossible, but
86 that's a desired side effect since there is state that needs to be
87 destroyed properly before the service implementation can be unloaded.
88
89 Normal usage pattern is that the @ref weak_service_reference::init() is called
90 during component initialization.
91
92 And @ref weak_service_reference::deinit() is called during the component
93 deinitialization.
94
95
96 @warning Please pass the _no_lock registry variants to the deinit() call! It's
97 because component deinit function is called while the registry lock is held.
98 So trying to take the lock again (which is what the normal registry functions
99 do) is going to lead to a deadlock!
100
101 One can expect that the function argument is called either at init() time or
102 asyncronously, possibly from anoher thread, when an implementation of a
103 service is registered.
104
105 Typical usage:
106 @code
107 #include "mysql/components/util/weak_service_reference.h"
108 ...
109 #include "mysql/components/services/foo.h"
110 #include "mysql/components/services/registry.h"
111 ...
112
113 REQUIRES_SERVICE_PLACEHOLDER(registry_registration);
114 REQUIRES_SERVICE_PLACEHOLDER_AS(registry, mysql_service_registry_no_lock);
115 REQUIRES_SERVICE_PLACEHOLDER_AS(registry_registration,
116 mysql_service_registration_no_lock);
117
118 const std::string c_name(component_foo), s_name("foo");
119 typedef weak_service_reference<SERVICE_TYPE(foo), c_name, s_name>
120 weak_foo_service;
121
122
123 BEGIN_COMPONENT_REQUIRES(component_foo)
124 ...
125 REQUIRES_SERVICE(registry_registration),
126 REQUIRES_SERVICE_IMPLEMENTATION_AS(registry_registration,
127 mysql_minimal_chassis_no_lock,
128 mysql_service_registration_no_lock),
129 REQUIRES_SERVICE_IMPLEMENTATION_AS(registry, mysql_minimal_chassis_no_lock,
130 mysql_service_registry_no_lock),
131 ...
132 END_COMPONENT_REQUIRES();
133
134 bool component_init() {
135 ...
136 if (weak_foo_service::init(SERVICE_PLACEHOLDER(registry),
137 SERVICE_PLACEHOLDER(registry_registration),
138 [&](SERVICE_TYPE(foo) * foo_svc) {
139 return 0 != foo_svc->define(12);
140 }))
141 return 1;
142 ...
143 }
144
145 bool component_deinit() {
146 ...
147 if (weak_option::deinit(
148 mysql_service_registry_no_lock, mysql_service_registration_no_lock,
149 [&](SERVICE_TYPE(foo) * foo_svc) {
150 return 0 != foo_svc->undefine(12);
151 }))
152 return 1;
153 @endcode
154
155 @tparam Service This is the type of the service to be called. E.g.
156 SERVICE_TYPE(foo)
157 @tparam container The name of the "container". Usually a component name.
158 It has to be a rvalue ref since you would need a distinct set of the
159 static members of the template class for every service/component combo.
160 @tparam service_name The name of the service to try to call.
161 It has to be a rvalue ref since you would need a distinct set of the
162 static members of the template class for every service/component combo.
163*/
164template <typename Service, const std::string &container,
165 const std::string &service_name>
167 /**
168 A single instance of the class to hold (and initialize) some data
169 at init time.
170 */
172 nullptr};
173
174 /**
175 We need to store a reference to the registry since the init callback needs
176 it.
177 */
178 inline static SERVICE_TYPE(registry) * registry{nullptr};
179 /**
180 A flag stating if the callback service implementation listening
181 for new implementations of the service has been registered.
182 */
183 inline static bool callback_registered{false};
184
185 /**
186 true when the weak reference class is to keep the reference
187 acquired for reuse until deinit is called.
188 */
189 inline static bool keep_active_reference{true};
190
191 /**
192 A flag if the init callback function has been called.
193 This is to prevent multiple calls to the init callback.
194 Ideally we'd unregister the callback altogether, but the callback
195 is called while a reference to it is held, so it can't unregister
196 itself due to an active reference.
197 Hence we raise the flag to prevent further action and deregister
198 at deinit()
199 */
200 std::atomic<bool> function_called{false};
201 /**
202 The init callback reference.
203 */
204 const std::function<bool(Service *)> function;
205
206 /**
207 A service_loaded listener implementation name of the following format:
208 dynamic_loader_services_loaded_notification.&lt;conainer_name&gt;_&lt;service_name&gt;
209 */
210 std::string listener_name;
211
213
214 /**
215 @brief A private constructor for the hton
216
217 @param func_arg The function to call when there's an implementation.
218 active reference until deinit.
219 */
220 weak_service_reference(std::function<bool(Service *)> &func_arg)
221 : function(func_arg) {
223 std::string("dynamic_loader_services_loaded_notification.") +
224 container + std::string("_") + service_name;
225 }
226
227 /**
228 @brief Helper function to take a reference to the service needed
229 and call the init callback function.
230
231 @retval true failure
232 @retval false success
233 */
234 static bool call_function() {
236 if (!hton->service_reference.is_valid())
237 hton->service_reference.acquire(service_name.c_str(), registry);
238
239 if (hton->service_reference.is_valid()) {
240 if (hton->function(hton->service_reference)) return true;
241 hton->function_called = true;
242 }
243 } else {
244 my_service<Service> svc(service_name.c_str(), registry);
245 if (svc.is_valid()) {
246 if (hton->function(svc)) return true;
247 hton->function_called = true;
248 }
249 }
250 return false;
251 }
252
253 /**
254 @brief An implementation for the
255 dynamic_loader_services_loaded_notification::notify service method
256
257 This is called by the dynamic loader when a new service implementation
258 is registered.
259 */
261 (const char **services, unsigned int count)) try {
262 if (!keep_active_reference || !hton->function_called) {
263 for (unsigned idx = 0; idx < count; idx++) {
264 std::string svc(services[idx]);
265 if (svc.length() > service_name.length() &&
266 svc[service_name.length()] == '.' && 0 == svc.find(service_name))
267 return call_function() ? 1 : 0;
268 }
269 }
270 return 0;
271 } catch (...) {
272 return 1;
273 }
274
275 public:
276 /**
277 @brief Initialize the weak reference class
278
279 @param reg_arg A reference to the registry service implementation
280 @param reg_reg_arg A reference to the registry_registration service
281 implementation
282 @param func_arg A function to be called when an implementation of the service
283 is available. Typically used to initialize some state, e.g. allocate
284 instance handles or register some features in registries.
285 @param keep_active_reference_arg True if weak_reference is to keep an active
286 reference until deinit.
287
288 This is typically called by the component initialization.
289 If there's already an implementation of the service required a reference to it
290 is obtained and is passed to the function callback from the argument.
291
292 If no implementations are available a listener for new implementation
293 registration (an implementation of the
294 dynamic_loader_services_loaded_notifications service) is registered into the
295 registry and the function returns.
296
297 @note Pass the "normal" references to the registry and the registry
298 registration services here. init() is called without any locks being held to
299 the registry.
300
301 @retval true Failure
302 @retval false Success
303 */
304 static bool init(SERVICE_TYPE(registry) * reg_arg,
305 SERVICE_TYPE(registry_registration) * reg_reg_arg,
306 std::function<bool(Service *)> func_arg,
307 bool keep_active_reference_arg = true) {
308 registry = reg_arg;
309 keep_active_reference = keep_active_reference_arg;
310 assert(hton == nullptr);
311 hton =
313 if (call_function()) return true;
314
315 if (!hton->function_called || !keep_active_reference) {
317 x, dynamic_loader_services_loaded_notification) notify
319 if (reg_reg_arg->register_service(
320 hton->listener_name.c_str(),
321 (my_h_service) const_cast<void *>(
322 (const void *)&SERVICE_IMPLEMENTATION(
323 x, dynamic_loader_services_loaded_notification))))
324 return true;
325 callback_registered = true;
326 }
327 return false;
328 }
329
330 /**
331 @brief Deinitializes a weak reference caller class
332
333 If the init callback was called it will try to acquire a reference to the
334 service and call the deinit callback if the reference is acquired.
335
336 Then it will deregister the dynamic_loader_services_loaded_notification
337 implementation, if it's been registered by init().
338
339 And it will then proceed to delete the state in hton and reset the class.
340
341 @param registry_arg A reference to the registry service implementation
342 @param registry_registration_arg A reference to the registry_registration
343 service implementation
344 @param deinit_func_arg A (deinit) function to call if an implementation of
345 the service required is definied. One typically reverses the action taken by
346 the registration callback here, e.g. diposes of state, deregisters features
347 etc.
348
349 @retval true failure
350 @retval false success
351 */
352 static bool deinit(SERVICE_TYPE(registry) * registry_arg,
354 registry_registration_arg,
355 std::function<bool(Service *)> deinit_func_arg) {
356 // the server may exit before init was called
357 if (hton == nullptr) return false;
358
360 if (hton->function_called) {
361 assert(hton->service_reference.is_valid());
362 if (deinit_func_arg(hton->service_reference)) return true;
363 }
364 /*
365 We need to release explicitly here becase it was acquired with the
366 registry at init but we need to release it with the registry argument
367 supplied.
368 */
369 if (hton->service_reference.is_valid()) {
370 my_service<Service> svc(hton->service_reference, registry_arg);
371 svc.release();
372 hton->service_reference.untie();
373 }
374 } else {
375 if (hton->function_called) {
376 my_service<Service> svc(service_name.c_str(), registry_arg);
377 if (svc.is_valid() && deinit_func_arg(svc)) return true;
378 }
379 }
381 registry_registration_arg->unregister(hton->listener_name.c_str()))
382 return true;
383 delete hton;
384 hton = nullptr;
385 registry = nullptr;
386 callback_registered = false;
387 return false;
388 }
389};
390
391#endif /* WEAK_REQUIRES_SERVICE_GUARD */
Wraps my_h_service struct conforming ABI into RAII C++ object with ability to cast to desired service...
Definition: my_service.h:35
bool is_valid() const
Definition: my_service.h:127
void release()
Releases the reference, if any, and cleans the instance up.
Definition: my_service.h:104
A utility class to implement a delayed service reference.
Definition: weak_service_reference.h:166
static weak_service_reference< Service, container, service_name > * hton
A single instance of the class to hold (and initialize) some data at init time.
Definition: weak_service_reference.h:171
static bool keep_active_reference
true when the weak reference class is to keep the reference acquired for reuse until deinit is called...
Definition: weak_service_reference.h:189
static bool init(const mysql_service_registry_t *reg_arg, const mysql_service_registry_registration_t *reg_reg_arg, std::function< bool(Service *)> func_arg, bool keep_active_reference_arg=true)
Initialize the weak reference class.
Definition: weak_service_reference.h:304
std::string listener_name
A service_loaded listener implementation name of the following format: dynamic_loader_services_loaded...
Definition: weak_service_reference.h:210
static mysql_service_status_t notify(const char **services, unsigned int count) noexcept
An implementation for the dynamic_loader_services_loaded_notification::notify service method.
Definition: weak_service_reference.h:261
std::atomic< bool > function_called
A flag if the init callback function has been called.
Definition: weak_service_reference.h:200
my_service< Service > service_reference
Definition: weak_service_reference.h:212
static bool call_function()
Helper function to take a reference to the service needed and call the init callback function.
Definition: weak_service_reference.h:234
static bool callback_registered
A flag stating if the callback service implementation listening for new implementations of the servic...
Definition: weak_service_reference.h:183
static const mysql_service_registry_t * registry
We need to store a reference to the registry since the init callback needs it.
Definition: weak_service_reference.h:178
weak_service_reference(std::function< bool(Service *)> &func_arg)
A private constructor for the hton.
Definition: weak_service_reference.h:220
const std::function< bool(Service *)> function
The init callback reference.
Definition: weak_service_reference.h:204
static bool deinit(const mysql_service_registry_t *registry_arg, const mysql_service_registry_registration_t *registry_registration_arg, std::function< bool(Service *)> deinit_func_arg)
Deinitializes a weak reference caller class.
Definition: weak_service_reference.h:352
Connection event tracking.
struct my_h_service_imp * my_h_service
A handle type for acquired Service.
Definition: registry.h:33
static int count
Definition: myisam_ftdump.cc:45
const mysql_service_registry_registration_t * registry_registration
Definition: atomics_array.h:39
Definition: services.cc:40
#define SERVICE_TYPE(name)
Generates the standard Service type name.
Definition: service.h:76
Specifies macros to define Service Implementations.
#define BEGIN_SERVICE_IMPLEMENTATION(component, service)
Declares a Service Implementation.
Definition: service_implementation.h:62
#define END_SERVICE_IMPLEMENTATION()
A macro to end the last declaration of a Service Implementation.
Definition: service_implementation.h:67
#define DEFINE_BOOL_METHOD(name, args)
A short macro to define method that returns bool, which is the most common case.
Definition: service_implementation.h:88
#define SERVICE_IMPLEMENTATION(component, service)
Reference to the name of the service implementation variable.
Definition: service_implementation.h:51