MySQL 9.0.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 When the weak reference is initialized the class checks if the service
42 required already has implementations. If it does, the class takes a reference
43 to the default one and calls the supplied function. If there's no
44 implementation of the service the class registers a listener to the
45 dynamic_loader_services_loaded_notification broadcast service by implementing
46 a dynamic_loader_services_loaded_notification service that, when called by the
47 dynamic loader, will take a refernece to the desired service and call the
48 function supplied. And then it sets a flag preventing any further calls to the
49 function.
50
51 At deinit time, deinit tries to acquire the foo service and, if successful,
52 calls the supplied function and passes it as a parameter. Note that if the
53 service implementation has been undefined in the meanwhile no call of the
54 deinit supplied function is done.
55
56 Normal usage pattern is that the @ref weak_service_reference::init() is called
57 during component initialization.
58
59 And @ref weak_service_reference::deinit() is called during the component
60 deinitialization.
61
62
63 @warning Please pass the _no_lock registry variants to the deinit() call! It's
64 because component deinit function is called while the registry lock is held.
65 So trying to take the lock again (which is what the normal registry functions
66 do) is going to lead to a deadlock!
67
68 One can expect that the function argument is called either at init() time or
69 asyncronously, possibly from anoher thread, when an implementation of a
70 service is registered.
71
72 Typical usage:
73 @code
74 #include "mysql/components/util/weak_service_reference.h"
75 ...
76 #include "mysql/components/services/foo.h"
77 #include "mysql/components/services/registry.h"
78 ...
79
80 REQUIRES_SERVICE_PLACEHOLDER(registry_registration);
81 REQUIRES_SERVICE_PLACEHOLDER_AS(registry, mysql_service_registry_no_lock);
82 REQUIRES_SERVICE_PLACEHOLDER_AS(registry_registration,
83 mysql_service_registration_no_lock);
84
85 const std::string c_name(component_foo), s_name("foo");
86 typedef weak_service_reference<SERVICE_TYPE(foo), c_name, s_name>
87 weak_foo_service;
88
89
90 BEGIN_COMPONENT_REQUIRES(component_foo)
91 ...
92 REQUIRES_SERVICE(registry_registration),
93 REQUIRES_SERVICE_IMPLEMENTATION_AS(registry_registration,
94 mysql_minimal_chassis_no_lock,
95 mysql_service_registration_no_lock),
96 REQUIRES_SERVICE_IMPLEMENTATION_AS(registry, mysql_minimal_chassis_no_lock,
97 mysql_service_registry_no_lock),
98 ...
99 END_COMPONENT_REQUIRES();
100
101 bool component_init() {
102 ...
103 if (weak_foo_service::init(SERVICE_PLACEHOLDER(registry),
104 SERVICE_PLACEHOLDER(registry_registration),
105 [&](SERVICE_TYPE(foo) * foo_svc) {
106 return 0 != foo_svc->define(12);
107 }))
108 return 1;
109 ...
110 }
111
112 bool component_deinit() {
113 ...
114 if (weak_option::deinit(
115 mysql_service_registry_no_lock, mysql_service_registration_no_lock,
116 [&](SERVICE_TYPE(foo) * foo_svc) {
117 return 0 != foo_svc->undefine(12);
118 }))
119 return 1;
120 @endcode
121
122 @tparam Service This is the type of the service to be called. E.g.
123 SERVICE_TYPE(foo)
124 @tparam container The name of the "container". Usually a component name.
125 It has to be a rvalue ref since you would need a distinct set of the
126 static members of the template class for every service/component combo.
127 @tparam service_name The name of the service to try to call.
128 It has to be a rvalue ref since you would need a distinct set of the
129 static members of the template class for every service/component combo.
130*/
131template <typename Service, const std::string &container,
132 const std::string &service_name>
134 /**
135 A single instance of the class to hold (and initialize) some data
136 at init time.
137 */
139 nullptr};
140
141 /**
142 We need to store a reference to the registry since the init callback needs
143 it.
144 */
145 inline static SERVICE_TYPE(registry) * registry{nullptr};
146 /**
147 A flag stating if the callback service implementation listening
148 for new implementations of the service has been registered.
149 */
150 inline static bool callback_registered{false};
151
152 /**
153 A flag if the init callback function has been called.
154 This is to prevent multiple calls to the init callback.
155 Ideally we'd unregister the callback altogether, but the callback
156 is called while a reference to it is held, so it can't unregister
157 itself due to an active reference.
158 Hence we raise the flag to prevent further action and deregister
159 at deinit()
160 */
161 std::atomic<bool> function_called{false};
162 /**
163 The init callback reference.
164 */
165 const std::function<bool(Service *)> function;
166
167 /**
168 A service_loaded listener implementation name of the following format:
169 dynamic_loader_services_loaded_notification.&lt;conainer_name&gt;_&lt;service_name&gt;
170 */
171 std::string listener_name;
172
173 /**
174 @brief A private constructor for the hton
175
176 @param func_arg The function to call when there's an implementation.
177 */
178 weak_service_reference(std::function<bool(Service *)> &func_arg)
179 : function(func_arg) {
181 std::string("dynamic_loader_services_loaded_notification.") +
182 container + std::string("_") + service_name;
183 }
184
185 /**
186 @brief Helper function to take a reference to the service needed
187 and call the init callback function.
188
189 @retval true failure
190 @retval false success
191 */
192 static bool call_function() {
193 my_service<Service> svc(service_name.c_str(), registry);
194 if (svc.is_valid()) {
195 if (hton->function(svc)) return true;
196 hton->function_called = true;
197 }
198 return false;
199 }
200
201 /**
202 @brief An implementation for the
203 dynamic_loader_services_loaded_notification::notify service method
204
205 This is called by the dynamic loader when a new service implementation
206 is registered.
207 */
209 (const char **services, unsigned int count)) try {
210 if (!hton->function_called) {
211 for (unsigned idx = 0; idx < count; idx++) {
212 std::string svc(services[idx]);
213 if (svc.starts_with(service_name) &&
214 svc.length() > service_name.length() &&
215 svc[service_name.length()] == '.')
216 return call_function() ? 1 : 0;
217 }
218 }
219 return 0;
220 } catch (...) {
221 return 1;
222 }
223
224 public:
225 /**
226 @brief Initialize the weak reference class
227
228 @param reg_arg A reference to the registry service implementation
229 @param reg_reg_arg A reference to the registry_registration service
230 implementation
231 @param func_arg A function to be called when an implementation of the service
232 is available. Typically used to initialize some state, e.g. allocate
233 instance handles or register some features in registries.
234
235 This is typically called by the component initialization.
236 If there's already an implementation of the service required a reference to it
237 is obtained and is passed to the function callback from the argument.
238
239 If no implementations are available a listener for new implementation
240 registration (an implementation of the
241 dynamic_loader_services_loaded_notifications service) is registered into the
242 registry and the function returns.
243
244 @note Pass the "normal" references to the registry and the registry
245 registration services here. init() is called without any locks being held to
246 the registry.
247
248 @retval true Failure
249 @retval false Success
250 */
251 static bool init(SERVICE_TYPE(registry) * reg_arg,
252 SERVICE_TYPE(registry_registration) * reg_reg_arg,
253 std::function<bool(Service *)> func_arg) {
254 registry = reg_arg;
255 assert(hton == nullptr);
256 hton =
258 if (call_function() && hton->function_called) return true;
259
260 if (!hton->function_called) {
262 x, dynamic_loader_services_loaded_notification) notify
264 if (reg_reg_arg->register_service(
265 hton->listener_name.c_str(),
266 (my_h_service) const_cast<void *>(
267 (const void *)&SERVICE_IMPLEMENTATION(
268 x, dynamic_loader_services_loaded_notification))))
269 return true;
270 callback_registered = true;
271 }
272 return false;
273 }
274
275 /**
276 @brief Deinitializes a weak reference caller class
277
278 If the init callback was called it will try to acquire a reference to the
279 service and call the deinit callback if the reference is acquired.
280
281 Then it will deregister the dynamic_loader_services_loaded_notification
282 implementation, if it's been registered by init().
283
284 And it will then proceed to delete the state in hton and reset the class.
285
286 @param registry_arg A reference to the registry service implementation
287 @param registry_registration_arg A reference to the registry_registration
288 service implementation
289 @param deinit_func_arg A (deinit) function to call if an implementation of the
290 service required is definied. One typically reverses the action taken by the
291 registration callback here, e.g. diposes of state, deregisters features etc.
292
293 @return true
294 @return false
295 */
296 static bool deinit(SERVICE_TYPE(registry) * registry_arg,
298 registry_registration_arg,
299 std::function<bool(Service *)> deinit_func_arg) {
300 if (hton) {
301 if (hton->function_called) {
302 my_service<Service> svc(service_name.c_str(), registry_arg);
303 if (svc.is_valid() && deinit_func_arg(svc)) return true;
304 }
306 registry_registration_arg->unregister(hton->listener_name.c_str()))
307 return true;
308 delete hton;
309 hton = nullptr;
310 }
311 registry = nullptr;
312 callback_registered = false;
313 return false;
314 }
315};
316
317#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:104
A utility class to implement a delayed service reference.
Definition: weak_service_reference.h:133
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:138
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)
Initialize the weak reference class.
Definition: weak_service_reference.h:251
std::string listener_name
A service_loaded listener implementation name of the following format: dynamic_loader_services_loaded...
Definition: weak_service_reference.h:171
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:209
std::atomic< bool > function_called
A flag if the init callback function has been called.
Definition: weak_service_reference.h:161
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:192
static bool callback_registered
A flag stating if the callback service implementation listening for new implementations of the servic...
Definition: weak_service_reference.h:150
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:145
weak_service_reference(std::function< bool(Service *)> &func_arg)
A private constructor for the hton.
Definition: weak_service_reference.h:178
const std::function< bool(Service *)> function
The init callback reference.
Definition: weak_service_reference.h:165
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:296
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