MySQL 26.10.0
Source Code Documentation
loader.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2015, 2026, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is designed to work with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have either included with
14 the program or referenced in the documentation.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24*/
25
26/**
27 * @class mysql_harness::Loader
28 *
29 * @ingroup Loader
30 *
31 *
32 *
33 * ## Introduction
34 *
35 * The loader class is responsible for managing the life-cycle of
36 * plugins in the harness. Each plugin goes through seven steps in the
37 * life-cycle, of which steps #2, #3, #5 and #6 are optional:
38 *
39 * 1. Loading
40 * 2. Initialization
41 * 3. Starting
42 * 4. Running
43 * 5. Stopping
44 * 6. Deinitialization
45 * 7. Unloading
46 *
47 * ## Overview of Life-cycle Steps
48 *
49 * ### 1. Loading ###
50 *
51 * When *loading*, the plugin is loaded using the dynamic library
52 * support available on the operating system. Symbols are evaluated
53 * lazily (for example, the `RTLD_LAZY` flag is used for `dlopen`) to
54 * allow plugins to be loaded in any order. The symbols that are
55 * exported by the plugin are made available to all other plugins
56 * loaded (flag `RTLD_GLOBAL` to `dlopen`).
57 *
58 * As part of the loading procedure, the *plugin structure* (see
59 * Plugin class) is fetched from the module and used for the four
60 * optional steps below.
61 *
62 *
63 *
64 * ### 2. Initialization ###
65 *
66 * After all the plugins are successfully loaded, each plugin is given
67 * a chance to perform initialization. This step is only executed if
68 * the plugin structure defines an `init` function. Note that it is
69 * guaranteed that the init function of a plugin is called *after* the
70 * `init` function of all plugins it requires have been called. The
71 * list of these dependencies is specified via `requires` field of the
72 * `Plugin` struct.
73 *
74 * @note if some plugin `init()` function fails, any plugin `init()`
75 * functions schedulled to run after will not run, and harness will
76 * proceed straight to deinitialization step, bypassing calling
77 * `start()` and `stop()` functions.
78 *
79 *
80 *
81 * ### 3. Starting ###
82 * After all plugins have been successfully initialized, a thread is
83 * created for each plugin that has a non-NULL `start` field in the
84 * plugin structure. The threads are started in an arbitrary order,
85 * so you have to be careful about not assuming that, for example,
86 * other plugins required by the plugin have started their thread. If
87 * the plugin does not define a `start` function, no thread is created.
88 * There is a "running" flag associated with each such thread; this
89 * flag is set when the thread starts but before the `start` function
90 * is called. If necessary, the plugin can spawn more threads using
91 * standard C++11 thread calls, however, these threads should not
92 * call harness API functions.
93 *
94 *
95 *
96 * ### 4. Running ###
97 * After starting all plugins (that needed to be started), the harness
98 * will enter the *running step*. This is the "normal" phase, where the
99 * application spends most of its lifetime (application and plugins
100 * service requests or do whatever it is they do). Harness will remain
101 * in this step until one of two things happen:
102 *
103 * 1. shutdown signal is received by the harness
104 * 2. one of the plugins exits with error
105 *
106 * When one of these two events occurs, harness progresses to the
107 * next step.
108 *
109 *
110 *
111 * ### 5. Stopping ###
112 * In this step, harness "tells" plugins running `start()` to exit this
113 * function by clearing the "running" flag. It also invokes `stop()`
114 * function for all plugins that provided it. It then waits for all
115 * running plugin threads to exit.
116 *
117 * @note under certain circumstances, `stop()` may overlap execution
118 * with `start()`, or even be called before `start()`.
119 *
120 *
121 *
122 * ### 6. Deinitialization ###
123 * After all threads have stopped, regardless of whether they stopped
124 * with an error or not, the plugins are deinitialized in reverse order
125 * of initialization by calling the function in the `deinit` field of
126 * the `Plugin` structure. Regardless of whether the `deinit()` functions
127 * return an error or not, all plugins schedulled for deinitialisation
128 * will be deinitialized.
129 *
130 * @note for any `init()` functions that failed, `deinit()` functions
131 * will not run.
132 * @note plugins may have a `deinit()` function despite not having a
133 * corresponding `init()`. In such cases, the missing `init()` is
134 * treated as if it existed and ran successfully.
135 *
136 *
137 *
138 * ### 7. Unloading ###
139 * After a plugin has deinitialized, it can be unloaded. It is
140 * guaranteed that no module is unloaded before it has been
141 * deinitialized.
142 *
143 * @note This step is currently unimplemented - meaning, it does nothing.
144 * The plugins will remain loaded in memory until the process shuts
145 * down. This makes no practical difference on application behavior
146 * at present, but might be needed if Harness gained ability to
147 * reload plugins in the future.
148
149## Behavior Diagrams
150
151Previous section described quickly each step of the life-cycle process. In this
152section, two flow charts are presented which show the operation of all seven
153steps. First shows a high-level overview, and the second shows all 7 life-cycle
154steps in more detail. Discussion of details follows in the following sections.
155
156Some points to keep in mind while viewing the diagrams:
157
158- diagrams describe code behavior rather than implementation. So for example:
159 - pseudocode does not directly correspond 1:1 to real code. However, it
160 behaves exactly like the real code.
161
162- seven life-cycle functions shown are actual functions (@c Loader's methods,
163 to be more precise)
164 - load_all(), init_all(), start_all(), main_loop(), stop_all(), deinit_all()
165 are implemented functions (first 6 steps of life-cycle)
166 - unload_all() is the 7th step of life-cycle, but it's currently unimplemented
167
168- when plugin functions exit with error, they do so by calling
169 set_error() before exiting
170
171- some things are not shown to keep the diagram simple:
172 - first error returned by any of the 7 life-cycle functions is
173 saved and passed at the end of life-cycle flow to the calling code
174
175
176### Overview
177
178@verbatim
179
180\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
181\ \
182\ START \
183\ | \
184\ | \
185\ | \
186\ V \
187\ [load_all()] \
188\ | \
189\ V \
190\ <LOAD_OK?> \
191\ | | \
192\ +---N Y \
193\ | | \
194\ | v \
195\ | [init_all()] \
196\ | | \
197\ | v \
198\ | <INIT_OK?> ( each plugin runs ) \
199\ | | | (in a separate thread) \
200\ | N Y \
201\ | | | [plugin[1]->start()] \
202\ | | v start plugin threads [plugin[2]->start()] \
203\ | | [start_all()] - - - - - - - - - - - - - - - - ->[ .. .. ] \
204\ | | | [ .. .. ] \
205\ | | | + - - - - - - - - - - - - - - - - - - - - -[plugin[n]->start()] \
206\ | | | notification when each ^ \
207\ | | | | thread terminates \
208\ | | | | \
209\ | | | | stop plugin \
210\ | | | | threads \
211\ | | | | \
212\ | | | | \
213\ | | v v \
214\ | | [main_loop()]= call ==>[stop_all()] - - - - - - - - - - - + \
215\ | | | \
216\ | | | \ \
217\ | *<--+ \ \
218\ | | \__ waits for all plugin \
219\ | v threads to terminate \
220\ | [deinit_all()] \
221\ | | \
222\ | v \
223\ +-->* \
224\ | \
225\ v \
226\ [unload_all()] \
227\ | \
228\ | \ \
229\ | \ \
230\ v \__ currently not implemented \
231\ END \
232\ \
233\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
234
235@endverbatim
236
237
238### Detailed View
239
240@verbatim
241
242 START
243 |
244 |
245 v
246\\\\ load_all() \\\\\\\\\\\\\\\\\\\\\\
247\ \
248\ LOAD_OK = true \
249\ foreach plugin: \
250\ load plugin \
251\ if (exit_status != ok): \
252\ LOAD_OK = false \
253\ break loop \
254\ \
255\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
256 |
257 |
258 v
259 <LOAD_OK?>
260 | |
261 Y N----> unload_all() (see further down)
262 |
263 |
264\\\\\\\\\\\\\\|\\\ init_all() \\\\\\\\\\\\\\\\\
265\ | \
266\ v \
267\ [INIT_OK = true, i = 1] \
268\ | \
269\ v \
270\ +----><plugin[i] exists?> \
271\ | | | \
272\ [i++] Y N---------------------+ \
273\ ^ | | \
274\ | | | \
275\ | v | \
276\ | <plugin[i] has init()?> | \
277\ | | | | \
278\ | N Y---+ | \
279\ | | | | \
280\ | | | | \
281\ | | v | \
282\ | | [plugin[i]->init()] | \
283\ | | | | \
284\ | | | | \
285\ | | | | \
286\ | | | | \
287\ | | v | \
288\ | | <exit ok?> | \
289\ | v | | | \
290\ +-------*<------Y N | \
291\ | | \
292\ | | \
293\ v | \
294\ [INIT_OK = false] | \
295\ | | \
296\ v | \
297\ *<----------------+ \
298\ | \
299\ v \
300\ [LAST_PLUGIN = i-1] \
301\ | \
302\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\\\\\\\\\\\\\
303 |
304 |
305 v
306 <INIT_OK?>
307 | |
308 Y N----> deinit_all() (see further down)
309 |
310 |
311 v
312\\\\ start_all() \\\\\\\\\\\\\\\\\\\\\\\\\
313\ \
314\ for i = 1 to LAST_PLUGIN: \
315\ if plugin[i] has start(): \ start start() in new thread
316\ new thread(plugin[i]->start()) - - - - - - - - - - - - - - - - +
317\ \
318\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
319 |
320 | |
321 +-----------------+
322 | |
323\\\\|\\\ main_loop() \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\\\\\\\
324 | | \
325 v \
326+-->* | \
327| | \
328| v | \
329| <any plugin threads running?> \
330| | | | \
331| N Y---+ \
332| | | | \
333| | <shutdown signal received && stop_all() not called yet?> \
334| | | | | \
335| | N Y \
336| | | == call ==>[stop_all()]- - - - - - - - - - - - - + | \
337| | | | tell (each) start() to exit \
338| | *<--+ | | \
339| | | \
340| | | v v \
341| | | [plugin[1]->start()] \
342| | v (one) plugin thread exits [plugin[2]->start()] \
343| | [wait for (any)]<- - - - - - - - - - - - - - - -[ .. .. ] \
344| | [ thread exit ] [ .. .. ] \
345| | | [plugin[n]->start()] \
346| | | ^ \
347| | | \
348| | v | \
349| | <thread exit ok?> \
350| | | | | \
351| | Y N---+ \
352| | | | | \
353| | | v \
354| | | <stop_all() called already?> | \
355| | v | | \
356| | *<------Y N tell (each) \
357| | | = call ==+ start() to exit \
358| | | | | | \
359| | v | | \
360+---|-------*<----------+ *==>[stop_all()]- - - - - - - - + \
361 | | \
362 | | | \
363 v | | \
364 <stop_all() called already?> | | \
365 | | | | \
366 Y N | | \
367 | == call =================+ | \
368 | | | \
369 *---+ | \
370 | | \
371\\\\|\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
372 | |
373 | |
374 v |
375 *<---- init_all() (if !INIT_OK) |
376 | |
377 | |
378 v |
379\\\\ deinit_all() \\\\\\\\\\\\\\\\\\\\ |
380\ \ |
381\ for i = LAST_PLUGIN to 1: \ |
382\ if plugin[i] has deinit(): \ |
383\ plugin[i]->deinit() \ |
384\ if (exit_status != ok): \ |
385\ # ignore error \ |
386\ \ |
387\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
388 | |
389 | |
390 v |
391 *<---- load_all() (if !LOAD_OK) |
392 | |
393 v |
394\\\\ unload_all() \\\\\\\\\\\\\\\\\\\\ |
395\ \ |
396\ no-op (currently unimplemented) \ |
397\ \ |
398\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ |
399 | |
400 | |
401 v /
402 END /
403 /
404 /
405 / ( each plugin runs )
406 / (in a separate thread)
407\\\\ stop_all() \\\\\\\\\\\\\\\\\\\\\\ run_flag == false
408\ \ tells start() [plugin[1]->start()]
409\ for i = 1 to LAST_PLUGIN: \ to exit [plugin[2]->start()]
410\ run_flag[i] = false - - - - - - - - - - - - - - - - ->[ .. .. ]
411\ if plugin[i] has stop(): \ [ .. .. ]
412\ plugin[i]->stop() \ [ .. .. ]
413\ if (exit_status != ok): \ [plugin[n]->start()]
414\ # ignore error \
415\ \
416\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
417
418@endverbatim
419
420
421
422
423## Discussion
424
425### Persistence (definition)
426
427Before continuing, we need to define the word "persist", used later on. When we
428say "persist", we'll mean the opposite of "exit". So when we say a function or
429a thread persists, it means that it's not returning/exiting, but instead is
430running some forever-loop or is blocked on something, etc. What it's doing
431exactly doesn't matter, what matters, is that it hasn't terminated but
432"lives on" (in case of a function, to "persist" means the same as to "block",
433but for threads that might sound confusing, this is why we need a new word).
434So when we call start() in a new thread, it will either run and keep running
435(thread will persist), or it will run briefly and return (thread will exit).
436In short, "to persist" means the opposite of "to finish running and exit".
437
438### Plugin API functions
439
440Each plugin can define none, any or all of the following 4 callbacks.
441They're function pointers, that can be null if not implemented. They're
442typically called in the order as listed below (under certain circumstances,
443stop() may overlap execution with start(), or even be called before start()).
444
445- init() -- called inside of main thread
446- start() -- main thread creates a new thread, then calls this
447- stop() -- called inside of main thread
448- deinit() -- called inside of main thread
449
450### Starting and Stopping: Start() ###
451
452It is typical to implement start function in such a way that it will
453"persist" (i.e. it will run some forever-loop processing requests
454rather than exit briefly after being called). In such case, Harness
455must have a way to terminate it during shutdown operation.
456
457For this purpose, Harness exposes a boolean "running" flag to each plugin, which
458serves as means to communicate the need to shutdown; it is read by
459`is_running()` function. This function should be routinely polled by plugin's
460`start()` function to determine if it should shut down, and once it returns
461false, plugin `start()` should terminate as soon as possible. Failure to
462terminate will block Harness from progressing further in its shutdown procedure,
463resulting in application "hanging" during shutdown. Typically, `start()`
464would be implemented more-or-less like so:
465
466 void start()
467 {
468 // run-once code
469
470 while (is_running())
471 {
472 // forever-loop code
473 }
474
475 // clean-up code
476 }
477
478There is also an alternative blocking function available, `wait_for_stop()`,
479should that be better suited for the particular plugin design. Instead of
480quickly returning a boolean flag, it will block (with an optional timeout)
481until Harness flags to shut down this plugin. It is an efficient functional
482equivalent of:
483
484 while (is_running())
485 {
486 // sleep a little or break on timeout
487 }
488
489When entering shutdown phase, Harness will notify all plugins to shut down
490via mechanisms described above. It is also permitted for plugins to exit on
491their own, whether due to error or intended behavior, without consulting
492this "running" flag. Polling the "running" flag is only needed when `start()`
493"persists" and does not normally exit until told to do so.
494
495Also, in some designs, `start()` function might find it convenient to be able to
496set the "running" flag to false, in order to trigger its own shutdown in another
497piece of code. For such cases, `clear_running()` function is provided, which
498will do exactly that.
499
500IMPORTANT! Please note that all 3 functions described above (`is_running()`,
501`wait_for_stop()` and `clear_running()`) can only be called from a thread
502running `start()` function. If `start()` spawns more theads, these
503functions CANNOT be called from them. These functions also cannot be called
504from the other three plugin functions (`init()`, `stop()` and `deinit()`).
505
506### Starting and Stopping: Stop() ###
507
508During shutdown, or after plugin `start()` function exits (whichever comes
509first), plugin's `stop()` function will be called, if defined.
510
511IMPORTANT: `start()` function runs in a different thread than `stop()`
512function. By the time `stop()` runs, depending on the circumstances,
513`start()` thread may or may not exist.
514
515IMPORTANT: `stop()` will always be called during shutdown, regardless of whether
516start() exited with error, exited successfully or is still running. `stop()`
517must be able to deal with all 3 scenarios. The rationale for this design
518decision is given Error Handling section.
519
520
521
522### Persistence in Plugin Functions ###
523
524While start() may persist, the other three functions (init(), stop() and
525deinit()) must obviously not persist, since they run in the main thread.
526Any blocking behavior exhibited in these functions (caused by a bug or
527otherwise) will cause the entire application to hang, as will start() that
528does not poll and/or honor is_running() flag.
529
530
531
532### Returning Success/Failure from Plugin Function ###
533
534Harness expects all four plugin functions (`init(), `start()`, `stop()` and
535`deinit()`) to notify it in case of an error. This is done via function:
536
537 set_error(PluginFuncEnv* env, ErrorType error, const char* format, ...);
538
539Calling this function flags that the function has failed, and passes the
540error type and string back to Harness. The converse is also true: not
541calling this function prior to exiting the function implies success.
542This distinction is important, because Harness may take certain actions
543based on the status returned by each function.
544
545IMPORTANT! Throwing exceptions from these functions is not supported.
546If your plugin uses exceptions internally, that is fine, but please
547ensure they are handled before reaching the Harness-Plugin boundary.
548
549
550### Threading Concerns ###
551
552For each plugin (independent of other plugins):
553Of the 4 plugin functions, `init()` runs first. It is guaranteed that
554it will exit before `start()` and `stop()` are called. `start()` and
555`stop()` can be called in parallel to each other, in any order, with
556their lifetimes possibly overlapping. They are guaranteed to both have
557exited before `deinit()` is called.
558
559If any of the 4 plugin functions spawn any additional threads, Harness
560makes no provisions for interacting with them in any way: calling
561Harness functions from them is not supported in particular; also such
562threads should exit before their parent function finishes running.
563
564
565
566### Error Handling ###
567
568NOTE: WL#9558 HLD version of this section additionally discusses design,
569 rationale for the approach chosen, etc; look there if interested.
570
571When plugin functions encounter an error, they are expected to signal it via
572set_error(). What happens next, depends on the case, but in all four
573cases the error will be logged automatically by the harness. Also, the first
574error passed from any plugin will be saved until the end of life-cycle
575processing, then passed down to the code calling the harness. This will allow
576the application code to deal with it accordingly (probably do some of its own
577cleaning and shut down, but that's up to the application). In general, the first
578error from init() or start() will cause the harness to initiate shut down
579procedure, while the errors from stop() and deinit() will be ignored completely
580(except of course for being logged and possibly saved for passing on at the
581end). Actions taken for each plugin function are as follows:
582
583
584
585#### init() fails:
586
587 - skip init() for remaining plugins
588
589 - don't run any start() and stop() (proceed directly to deinitialisation)
590
591 - run deinit() only for plugins initialiased so far (excluding the failing
592 one), in reverse order of initialisation, and exit
593
594 - when init() is not provided (is null), it counts as if it ran, if it would
595 have run before the failing plugin (according to topological order)
596
597
598
599#### start() fails:
600
601 - proceed to stop all plugins, then deinit() all in reverse order of
602 initialisation and exit. Please note that ALL plugins will be flagged
603 to stop and have their stop() function called (not just the ones that
604 succeeded in starting - plugin's stop() must be able to deal with such
605 a situation)
606
607
608
609#### stop() or deinit() fails:
610
611 - log error and ignore, proceed as if it didn't happen
612
613*/
614
615#ifndef MYSQL_HARNESS_LOADER_INCLUDED
616#define MYSQL_HARNESS_LOADER_INCLUDED
617
618#include "config_parser.h"
619#include "filesystem.h"
622#include "mysql/harness/plugin.h"
624
625#include "harness_export.h"
626
627#include "mpsc_queue.h"
628#include "my_compiler.h"
629
630#include <csignal>
631#include <cstdarg> // va_list
632#include <exception>
633#include <future>
634#include <istream>
635#include <list>
636#include <map>
637#include <memory>
638#include <queue>
639#include <set>
640#include <stdexcept>
641#include <string>
642#include <thread>
643#include <tuple>
644
645#ifdef FRIEND_TEST
646class TestLoader;
647#endif
648
649namespace mysql_harness {
650
651struct Plugin;
652class Path;
653
654/**
655 * PluginFuncEnv object
656 *
657 * This object is the basis of all communication between Harness and plugin
658 * functions. It is passed to plugin functions (as an opaque pointer), and
659 * plugin functions return it back to Harness when calling Harness API
660 * functions. It has several functions:
661 *
662 * - maintains a "running" flag, which controls starting/stopping
663 * of plugins
664 *
665 * - conveys exit status back to Harness after each plugin function exits
666 *
667 * - conveys more information (AppInfo, ConfigSection, ...) to
668 * plugin functions. Note that not all fields are set for all functions -
669 * setting ConfigSection ptr when calling init() makes no sense, for example
670 *
671 * @note Below we only briefly document the methods. For more information, see
672 * Harness API documentation for their corresponding free-function proxies
673 * in plugin.h
674 */
675class HARNESS_EXPORT PluginFuncEnv {
676 public:
677 /**
678 * Constructor
679 *
680 * @param info AppInfo to pass to plugin function. Can be NULL.
681 * Pointer is owned by the caller and must outlive plugin function call.
682 * @param section ConfigSection to pass to plugin function. Can be NULL.
683 * Pointer is owned by the caller and must outlive plugin function call.
684 * @param running Set "running" flag. true = plugin should be running
685 */
686 PluginFuncEnv(const AppInfo *info, const ConfigSection *section,
687 bool running = false);
688
689 // further info getters
690 // (see also corresponding Harness API functions in plugin.h for more info)
691 const ConfigSection *get_config_section() const noexcept;
692 const AppInfo *get_app_info() const noexcept;
693
694 // running flag
695 // (see also corresponding Harness API functions in plugin.h for more info)
696 void set_running() noexcept;
697 void clear_running() noexcept;
698 bool is_running() const noexcept;
699 bool wait_for_stop(
700 uint32_t milliseconds) const noexcept; // 0 = infinite wait
701
702 // error handling
703 // (see also corresponding Harness API functions in plugin.h for more info)
704 bool exit_ok() const noexcept;
705 MY_ATTRIBUTE((format(printf, 3, 0)))
706 void set_error(ErrorType error_type, const char *fmt, va_list ap) noexcept;
707 std::tuple<std::string, std::exception_ptr> pop_error() noexcept;
708
709 private:
710 const AppInfo *app_info_; // \.
711 const ConfigSection *config_section_; // > initialized in ctor
712 bool running_; // /
713 std::string error_message_;
714 ErrorType error_type_ = kNoError;
715
716 mutable std::condition_variable cond_;
717 mutable std::mutex mutex_;
718};
719
720class HARNESS_EXPORT PluginThreads {
721 public:
722 void push_back(std::thread &&thr);
723
724 // wait for the first non-fatal exit from plugin or all plugins exited
725 // cleanly
726 void try_stopped(std::exception_ptr &first_exc);
727
728 void push_exit_status(std::exception_ptr &&eptr) {
729 plugin_stopped_events_.push(std::move(eptr));
730 }
731
732 size_t running() const { return running_; }
733
734 void wait_all_stopped(std::exception_ptr &first_exc);
735
736 void join();
737
738 private:
739 std::vector<std::thread> threads_;
740 size_t running_{0};
741
742 /**
743 * queue of events after plugin's start() function exited.
744 *
745 * nullptr if "finished without error", pointer to an exception otherwise
746 */
748};
749
750class HARNESS_EXPORT Loader {
751 public:
752 /**
753 * Constructor for Loader.
754 *
755 * @param program Name of our program
756 * @param config Router configuration
757 * @param can_run_only_on_builtins Allow to run only with internals
758 */
759 Loader(const std::string &program, LoaderConfig &config,
760 bool can_run_only_on_builtins = false);
761 Loader(const Loader &) = delete;
763
764 Loader &operator=(const Loader &) = delete;
765
766 /**
767 * Fetch available plugins.
768 *
769 * @return List of names of available plugins.
770 */
771
772 std::list<Config::SectionKey> available() const;
773
774 /**
775 * Initialize and start all loaded plugins.
776 *
777 * All registered plugins will be initialized in proper order and
778 * started (if they have a `start` callback).
779 *
780 * @throws first exception that was triggered by an error returned from any
781 * plugin function.
782 */
783 void start();
784
785 /**
786 * Get reference to configuration object.
787 *
788 * @note In production code we initialize Loader with LoaderConfig
789 * reference maintained by DIM, so this method will return this object.
790 */
791 LoaderConfig &get_config() { return config_; }
792
793 /**
794 * service names to wait on.
795 *
796 * add a service name and call on_service_ready() when the service ready().
797 *
798 * @see on_service_ready()
799 */
800 std::vector<std::string> &waitable_services() { return waitable_services_; }
801
802 /**
803 * service names to wait on.
804 *
805 * @see on_service_ready()
806 */
807 const std::vector<std::string> &waitable_services() const {
808 return waitable_services_;
809 }
810
811 /**
812 * set a function that's called after all plugins have been started.
813 *
814 * @see after_all_finished()
815 */
816 void after_all_started(std::function<void()> &&func) {
817 after_all_started_ = std::move(func);
818 }
819
820 /**
821 * set a function that's called after the first plugin exited.
822 *
823 * @see after_all_started()
824 */
825 void after_first_finished(std::function<void()> &&func) {
826 after_first_finished_ = std::move(func);
827 }
828
829 /**
830 * Register global configuration options supported by the application. Will be
831 * used by the Loader to verify the [DEFAULT] options in the configuration.
832 *
833 * @param options array of global options supported by the applications
834 */
835 template <size_t N>
837 const std::array<std::string_view, N> &options) {
838 supported_app_options_.clear();
839 for (const auto &option : options) {
840 supported_app_options_.emplace_back(std::string(option));
841 }
842 }
843
844 /**
845 * Register a callback that the Loader will call when exposing of the whole
846 * configuration is requested. This callback is supposed to expose the
847 * application-level configration options (not plugin specific ones).
848 *
849 * @param clb callback to register that takes 2 parameters
850 * - bool indicating if the requested configuration is the initial
851 * one (if true) or default (false)
852 * - object representing default section of the current
853 * configuration
854 */
856 std::function<void(const bool, const ConfigSection &)> clb) {
857 expose_app_config_clb_ = clb;
858 }
859
860 /**
861 * Load all the configured plugins.
862 */
863 void load_all(); // throws bad_plugin on load error
864
865 /**
866 * Request the application and all the configured plugins to expose their
867 * configuration in the DyncamicConfig object.
868 *
869 * @param initial if true initial configuration is to exposed, default
870 * configuration otherwise
871 */
872 void expose_config_all(const bool initial);
873
874 // IMPORTANT design note: start_all() will block until PluginFuncEnv objects
875 // have been created for all plugins. This guarantees that the required
876 // PluginFuncEnv will always exist when plugin stop() function is called.
877
878 // start() calls these, indents reflect call hierarchy
879 void setup_info();
880
881 private:
882 enum class Status { UNVISITED, ONGOING, VISITED };
883
884 /**
885 * Flags progress of Loader. The progress always proceeds from top to bottom
886 * order in this list.
887 */
888 enum class Stage {
889 // NOTE: do not alter order of these enums!
890 Unset,
891 Loading,
892 Initializing,
893 Starting,
894 Running,
895 Stopping,
896 Deinitializing,
897 Unloading,
898 };
899
900 /**
901 * Load the named plugin from a specific library.
902 *
903 * @param plugin_name Name of the plugin to be loaded.
904 *
905 * @param library_name Name of the library the plugin should be
906 * loaded from.
907 *
908 * @throws bad_plugin (std::runtime_error) on load error
909 */
910 const Plugin *load_from(const std::string &plugin_name,
911 const std::string &library_name);
912
913 const Plugin *load(const std::string &plugin_name);
914
915 /**
916 * Load the named plugin and all dependent plugins.
917 *
918 * @param plugin_name Name of the plugin to be loaded.
919 * @param key Key of the plugin to be loaded.
920 *
921 * @throws bad_plugin (std::runtime_error) on load error
922 * @throws bad_section (std::runtime_error) when section 'plugin_name' is not
923 * present in configuration
924 *
925 * @post After the execution of this procedure, the plugin and all
926 * plugins required by that plugin will be loaded.
927 */
928 /** @overload */
929 const Plugin *load(const std::string &plugin_name, const std::string &key);
930
931 std::exception_ptr
932 run(); // returns first exception returned from below harness functions
933 std::exception_ptr init_all(); // returns first exception triggered by init()
934
935 void
936 start_all(); // forwards first exception triggered by start() to main_loop()
937
938 std::exception_ptr
939 main_loop(); // returns first exception triggered by start() or stop()
940
941 // class stop_all() and waits for plugins the terminate
942 std::exception_ptr stop_and_wait_all();
943
944 std::exception_ptr stop_all(); // returns first exception triggered by stop()
945
946 std::exception_ptr
947 deinit_all(); // returns first exception triggered by deinit()
948
949 void unload_all();
950 size_t external_plugins_to_load_count();
951
952 /**
953 * Topological sort of all plugins and their dependencies.
954 *
955 * Will create a list of plugins in topological order from "top"
956 * to "bottom".
957 */
958 bool topsort();
959 bool visit(const std::string &name, std::map<std::string, Status> *seen,
960 std::list<std::string> *order);
961
962 /**
963 * Holds plugin's API call information
964 *
965 * @note There's 1 instance per plugin type (not plugin instance)
966 */
967 class HARNESS_EXPORT PluginInfo {
968 public:
969 PluginInfo(const std::string &folder, const std::string &libname);
970 PluginInfo(const Plugin *const plugin) : plugin_(plugin) {}
971
972 void load_plugin_descriptor(const std::string &name); // throws bad_plugin
973
974 const Plugin *plugin() const { return plugin_; }
975
976 const DynamicLibrary &library() const { return module_; }
977
978 void unload_library() { /*module_.unload();*/
979 }
980
981 private:
983 const Plugin *plugin_{};
984 };
985
986 using PluginMap = std::map<std::string, PluginInfo>;
987
988 // Init order is important, so keep config_ first.
989
990 /**
991 * Configuration sections for all plugins.
992 */
994
995 /**
996 * Map of all successfully-loaded plugins (without key name).
997 */
999
1000 /**
1001 * Map of all {plugin instance -> plugin start() PluginFuncEnv} objects.
1002 * Initially these objects are created in Loader::start_all() and then kept
1003 * around until the end of Loader::stop_all(). At the time of writing,
1004 * PluginFuncEnv objects for remaining plugin functions (init(), stop() and
1005 * deinit()) are not required to live beyond their respective functions calls,
1006 * and are therefore created on stack (automatic variables) as needed during
1007 * those calls.
1008 */
1009 std::map<const ConfigSection *, std::shared_ptr<PluginFuncEnv>>
1011
1012 /**
1013 * active plugin threads.
1014 */
1016
1017 /**
1018 * Initialization order.
1019 */
1020 std::list<std::string> order_;
1021
1022 std::string logging_folder_;
1023 std::string plugin_folder_;
1024 std::string runtime_folder_;
1025 std::string config_folder_;
1026 std::string data_folder_;
1027 std::string program_;
1028 AppInfo appinfo_;
1029
1031
1033 std::condition_variable signal_thread_ready_cond_;
1034 bool signal_thread_ready_{false};
1035 std::thread signal_thread_;
1036
1037 std::vector<std::string> supported_app_options_;
1038 /**
1039 * Checks if all the options in the configuration fed to the Loader are
1040 * supported.
1041 *
1042 * @throws std::runtime_error if there is unsupported option in the
1043 * configuration
1044 */
1045 void check_config_options_supported();
1046
1047 /**
1048 * Checks if all the options in the section [DEFAULT] in the configuration fed
1049 * to the Loader are supported.
1050 *
1051 * @throws std::runtime_error if there is unsupported option in the [DEFAULT]
1052 * section of the configuration
1053 */
1054 void check_default_config_options_supported();
1055
1056 std::vector<std::shared_ptr<PluginStateObserver>> default_observers_;
1057
1058 // service names that need to be waited on.
1059 //
1060 // @see on_service_ready()
1061 std::vector<std::string> waitable_services_;
1062
1063 // called after "start_all()" succeeded.
1064 std::function<void()> after_all_started_;
1065
1066 // called after "main_loop()" exited.
1067 std::function<void()> after_first_finished_;
1068
1069 // called as a part of expose_config_all()
1070 std::function<void(const bool, const ConfigSection &)> expose_app_config_clb_;
1071
1072#ifdef FRIEND_TEST
1073 friend class ::TestLoader;
1074#endif
1075
1076 bool can_run_only_on_builtins_{false};
1077}; // class Loader
1078
1079} // namespace mysql_harness
1080
1081#endif /* MYSQL_HARNESS_LOADER_INCLUDED */
Kerberos Client Authentication Plugin
Definition: auth_kerberos_client_plugin.cc:246
A helper class for handling file paths.
Definition: path.h:38
Configuration section.
Definition: config_parser.h:141
A DynamicLibrary.
Definition: dynamic_loader.h:73
Configuration file handler for the loader.
Definition: loader_config.h:46
Holds plugin's API call information.
Definition: loader.h:967
const Plugin * plugin() const
Definition: loader.h:974
DynamicLibrary module_
Definition: loader.h:982
void unload_library()
Definition: loader.h:978
const DynamicLibrary & library() const
Definition: loader.h:976
PluginInfo(const Plugin *const plugin)
Definition: loader.h:970
Definition: loader.h:750
std::list< std::string > order_
Initialization order.
Definition: loader.h:1020
Stage
Flags progress of Loader.
Definition: loader.h:888
std::string logging_folder_
Definition: loader.h:1022
const std::vector< std::string > & waitable_services() const
service names to wait on.
Definition: loader.h:807
std::vector< std::string > waitable_services_
Definition: loader.h:1061
std::string data_folder_
Definition: loader.h:1026
std::thread signal_thread_
Definition: loader.h:1035
LoaderConfig & config_
Configuration sections for all plugins.
Definition: loader.h:993
LoaderConfig & get_config()
Get reference to configuration object.
Definition: loader.h:791
void after_all_started(std::function< void()> &&func)
set a function that's called after all plugins have been started.
Definition: loader.h:816
std::function< void(const bool, const ConfigSection &)> expose_app_config_clb_
Definition: loader.h:1070
std::function< void()> after_all_started_
Definition: loader.h:1064
std::mutex signal_thread_ready_m_
Definition: loader.h:1032
void register_supported_app_options(const std::array< std::string_view, N > &options)
Register global configuration options supported by the application.
Definition: loader.h:836
std::function< void()> after_first_finished_
Definition: loader.h:1067
Loader & operator=(const Loader &)=delete
std::string config_folder_
Definition: loader.h:1025
std::condition_variable signal_thread_ready_cond_
Definition: loader.h:1033
void register_expose_app_config_callback(std::function< void(const bool, const ConfigSection &)> clb)
Register a callback that the Loader will call when exposing of the whole configuration is requested.
Definition: loader.h:855
Loader(const Loader &)=delete
std::vector< std::string > & waitable_services()
service names to wait on.
Definition: loader.h:800
std::map< std::string, PluginInfo > PluginMap
Definition: loader.h:986
std::string program_
Definition: loader.h:1027
void after_first_finished(std::function< void()> &&func)
set a function that's called after the first plugin exited.
Definition: loader.h:825
std::string plugin_folder_
Definition: loader.h:1023
AppInfo appinfo_
Definition: loader.h:1028
std::vector< std::string > supported_app_options_
Definition: loader.h:1037
std::vector< std::shared_ptr< PluginStateObserver > > default_observers_
Definition: loader.h:1056
Status
Definition: loader.h:882
void spawn_signal_handler_thread()
PluginThreads plugin_threads_
active plugin threads.
Definition: loader.h:1015
std::map< const ConfigSection *, std::shared_ptr< PluginFuncEnv > > plugin_start_env_
Map of all {plugin instance -> plugin start() PluginFuncEnv} objects.
Definition: loader.h:1010
PluginMap plugins_
Map of all successfully-loaded plugins (without key name).
Definition: loader.h:998
std::string runtime_folder_
Definition: loader.h:1024
PluginFuncEnv object.
Definition: loader.h:675
std::mutex mutex_
Definition: loader.h:717
const AppInfo * app_info_
Definition: loader.h:710
bool running_
Definition: loader.h:712
const ConfigSection * config_section_
Definition: loader.h:711
std::string error_message_
Definition: loader.h:713
std::condition_variable cond_
Definition: loader.h:716
Definition: loader.h:720
size_t running() const
Definition: loader.h:732
WaitingMPSCQueue< std::exception_ptr > plugin_stopped_events_
queue of events after plugin's start() function exited.
Definition: loader.h:747
void push_exit_status(std::exception_ptr &&eptr)
Definition: loader.h:728
std::vector< std::thread > threads_
Definition: loader.h:739
provide waiting pop and push operator to thread-safe queues.
Definition: waiting_queue_adaptor.h:40
error_type
Definition: error.h:36
static void start(mysql_harness::PluginFuncEnv *env)
Definition: http_auth_backend_plugin.cc:180
static void run(mysql_harness::PluginFuncEnv *)
Definition: io_plugin.cc:199
Header for compiler-dependent features.
std::string format(const routing_guidelines::Session_info &session_info, bool extended_session_info)
Definition: dest_metadata_cache.cc:170
bool load(THD *, const dd::String_type &fname, dd::String_type *buf)
Read an sdi file from disk and store in a buffer.
Definition: sdi_file.cc:308
std::chrono::milliseconds milliseconds
Definition: authorize_manager.cc:70
Singleton class that stores the application type.
Definition: component_application_type_name.h:31
const ConfigSection * get_config_section(const PluginFuncEnv *env) noexcept
Definition: loader.cc:237
std::string join(const detail::range auto &rng, std::string_view delim)
join elements of a range into a string separated by a delimiter.
Definition: string.h:74
const AppInfo * get_app_info(const PluginFuncEnv *env) noexcept
Definition: loader.cc:233
void set_error(PluginFuncEnv *env, ErrorType error_type, const char *fmt,...) noexcept
Definition: loader.cc:253
bool is_running(const PluginFuncEnv *env) noexcept
Definition: loader.cc:243
bool wait_for_stop(const PluginFuncEnv *env, uint32_t milliseconds) noexcept
Definition: loader.cc:245
void clear_running(PluginFuncEnv *env) noexcept
Definition: loader.cc:249
Definition: options.cc:57
required string key
Definition: replication_asynchronous_connection_failover.proto:60
LEX_CSTRING * plugin_name(st_plugin_int **ref)
Definition: sql_plugin_ref.h:95