MySQL 9.3.0
Source Code Documentation
loader.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2015, 2025, 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 */
758 Loader(const std::string &program, LoaderConfig &config);
759 Loader(const Loader &) = delete;
761
762 Loader &operator=(const Loader &) = delete;
763
764 /**
765 * Fetch available plugins.
766 *
767 * @return List of names of available plugins.
768 */
769
770 std::list<Config::SectionKey> available() const;
771
772 /**
773 * Initialize and start all loaded plugins.
774 *
775 * All registered plugins will be initialized in proper order and
776 * started (if they have a `start` callback).
777 *
778 * @throws first exception that was triggered by an error returned from any
779 * plugin function.
780 */
781 void start();
782
783 /**
784 * Get reference to configuration object.
785 *
786 * @note In production code we initialize Loader with LoaderConfig
787 * reference maintained by DIM, so this method will return this object.
788 */
789 LoaderConfig &get_config() { return config_; }
790
791 /**
792 * service names to wait on.
793 *
794 * add a service name and call on_service_ready() when the service ready().
795 *
796 * @see on_service_ready()
797 */
798 std::vector<std::string> &waitable_services() { return waitable_services_; }
799
800 /**
801 * service names to wait on.
802 *
803 * @see on_service_ready()
804 */
805 const std::vector<std::string> &waitable_services() const {
806 return waitable_services_;
807 }
808
809 /**
810 * set a function that's called after all plugins have been started.
811 *
812 * @see after_all_finished()
813 */
814 void after_all_started(std::function<void()> &&func) {
815 after_all_started_ = std::move(func);
816 }
817
818 /**
819 * set a function that's called after the first plugin exited.
820 *
821 * @see after_all_started()
822 */
823 void after_first_finished(std::function<void()> &&func) {
824 after_first_finished_ = std::move(func);
825 }
826
827 /**
828 * Register global configuration options supported by the application. Will be
829 * used by the Loader to verify the [DEFAULT] options in the configuration.
830 *
831 * @param options array of global options supported by the applications
832 */
833 template <size_t N>
835 const std::array<std::string_view, N> &options) {
836 supported_app_options_.clear();
837 for (const auto &option : options) {
838 supported_app_options_.emplace_back(std::string(option));
839 }
840 }
841
842 /**
843 * Register a callback that the Loader will call when exposing of the whole
844 * configuration is requested. This callback is supposed to expose the
845 * application-level configration options (not plugin specific ones).
846 *
847 * @param clb callback to register that takes 2 parameters
848 * - bool indicating if the requested configuration is the initial
849 * one (if true) or default (false)
850 * - object representing default section of the current
851 * configuration
852 */
854 std::function<void(const bool, const ConfigSection &)> clb) {
855 expose_app_config_clb_ = clb;
856 }
857
858 /**
859 * Load all the configured plugins.
860 */
861 void load_all(); // throws bad_plugin on load error
862
863 /**
864 * Request the application and all the configured plugins to expose their
865 * configuration in the DyncamicConfig object.
866 *
867 * @param initial if true initial configuration is to exposed, default
868 * configuration otherwise
869 */
870 void expose_config_all(const bool initial);
871
872 private:
873 enum class Status { UNVISITED, ONGOING, VISITED };
874
875 /**
876 * Flags progress of Loader. The progress always proceeds from top to bottom
877 * order in this list.
878 */
879 enum class Stage {
880 // NOTE: do not alter order of these enums!
881 Unset,
882 Loading,
883 Initializing,
884 Starting,
885 Running,
886 Stopping,
887 Deinitializing,
888 Unloading,
889 };
890
891 /**
892 * Load the named plugin from a specific library.
893 *
894 * @param plugin_name Name of the plugin to be loaded.
895 *
896 * @param library_name Name of the library the plugin should be
897 * loaded from.
898 *
899 * @throws bad_plugin (std::runtime_error) on load error
900 */
901 const Plugin *load_from(const std::string &plugin_name,
902 const std::string &library_name);
903
904 const Plugin *load(const std::string &plugin_name);
905
906 /**
907 * Load the named plugin and all dependent plugins.
908 *
909 * @param plugin_name Name of the plugin to be loaded.
910 * @param key Key of the plugin to be loaded.
911 *
912 * @throws bad_plugin (std::runtime_error) on load error
913 * @throws bad_section (std::runtime_error) when section 'plugin_name' is not
914 * present in configuration
915 *
916 * @post After the execution of this procedure, the plugin and all
917 * plugins required by that plugin will be loaded.
918 */
919 /** @overload */
920 const Plugin *load(const std::string &plugin_name, const std::string &key);
921
922 // IMPORTANT design note: start_all() will block until PluginFuncEnv objects
923 // have been created for all plugins. This guarantees that the required
924 // PluginFuncEnv will always exist when plugin stop() function is called.
925
926 // start() calls these, indents reflect call hierarchy
927 void setup_info();
928 std::exception_ptr
929 run(); // returns first exception returned from below harness functions
930 std::exception_ptr init_all(); // returns first exception triggered by init()
931
932 void
933 start_all(); // forwards first exception triggered by start() to main_loop()
934
935 std::exception_ptr
936 main_loop(); // returns first exception triggered by start() or stop()
937
938 // class stop_all() and waits for plugins the terminate
939 std::exception_ptr stop_and_wait_all();
940
941 std::exception_ptr stop_all(); // returns first exception triggered by stop()
942
943 std::exception_ptr
944 deinit_all(); // returns first exception triggered by deinit()
945
946 void unload_all();
947 size_t external_plugins_to_load_count();
948
949 /**
950 * Topological sort of all plugins and their dependencies.
951 *
952 * Will create a list of plugins in topological order from "top"
953 * to "bottom".
954 */
955 bool topsort();
956 bool visit(const std::string &name, std::map<std::string, Status> *seen,
957 std::list<std::string> *order);
958
959 /**
960 * Holds plugin's API call information
961 *
962 * @note There's 1 instance per plugin type (not plugin instance)
963 */
964 class HARNESS_EXPORT PluginInfo {
965 public:
966 PluginInfo(const std::string &folder, const std::string &libname);
967 PluginInfo(const Plugin *const plugin) : plugin_(plugin) {}
968
969 void load_plugin_descriptor(const std::string &name); // throws bad_plugin
970
971 const Plugin *plugin() const { return plugin_; }
972
973 const DynamicLibrary &library() const { return module_; }
974
975 private:
977 const Plugin *plugin_{};
978 };
979
980 using PluginMap = std::map<std::string, PluginInfo>;
981
982 // Init order is important, so keep config_ first.
983
984 /**
985 * Configuration sections for all plugins.
986 */
988
989 /**
990 * Map of all successfully-loaded plugins (without key name).
991 */
993
994 /**
995 * Map of all {plugin instance -> plugin start() PluginFuncEnv} objects.
996 * Initially these objects are created in Loader::start_all() and then kept
997 * around until the end of Loader::stop_all(). At the time of writing,
998 * PluginFuncEnv objects for remaining plugin functions (init(), stop() and
999 * deinit()) are not required to live beyond their respective functions calls,
1000 * and are therefore created on stack (automatic variables) as needed during
1001 * those calls.
1002 */
1003 std::map<const ConfigSection *, std::shared_ptr<PluginFuncEnv>>
1005
1006 /**
1007 * active plugin threads.
1008 */
1010
1011 /**
1012 * Initialization order.
1013 */
1014 std::list<std::string> order_;
1015
1016 std::string logging_folder_;
1017 std::string plugin_folder_;
1018 std::string runtime_folder_;
1019 std::string config_folder_;
1020 std::string data_folder_;
1021 std::string program_;
1022 AppInfo appinfo_;
1023
1025
1027 std::condition_variable signal_thread_ready_cond_;
1028 bool signal_thread_ready_{false};
1029 std::thread signal_thread_;
1030
1031 std::vector<std::string> supported_app_options_;
1032 /**
1033 * Checks if all the options in the configuration fed to the Loader are
1034 * supported.
1035 *
1036 * @throws std::runtime_error if there is unsupported option in the
1037 * configuration
1038 */
1039 void check_config_options_supported();
1040
1041 /**
1042 * Checks if all the options in the section [DEFAULT] in the configuration fed
1043 * to the Loader are supported.
1044 *
1045 * @throws std::runtime_error if there is unsupported option in the [DEFAULT]
1046 * section of the configuration
1047 */
1048 void check_default_config_options_supported();
1049
1050 std::vector<std::shared_ptr<PluginStateObserver>> default_observers_;
1051
1052 // service names that need to be waited on.
1053 //
1054 // @see on_service_ready()
1055 std::vector<std::string> waitable_services_;
1056
1057 // called after "start_all()" succeeded.
1058 std::function<void()> after_all_started_;
1059
1060 // called after "main_loop()" exited.
1061 std::function<void()> after_first_finished_;
1062
1063 // called as a part of expose_config_all()
1064 std::function<void(const bool, const ConfigSection &)> expose_app_config_clb_;
1065
1066#ifdef FRIEND_TEST
1067 friend class ::TestLoader;
1068#endif
1069}; // class Loader
1070
1071} // namespace mysql_harness
1072
1073#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:964
const Plugin * plugin() const
Definition: loader.h:971
DynamicLibrary module_
Definition: loader.h:976
const DynamicLibrary & library() const
Definition: loader.h:973
PluginInfo(const Plugin *const plugin)
Definition: loader.h:967
Definition: loader.h:750
std::list< std::string > order_
Initialization order.
Definition: loader.h:1014
Stage
Flags progress of Loader.
Definition: loader.h:879
std::string logging_folder_
Definition: loader.h:1016
const std::vector< std::string > & waitable_services() const
service names to wait on.
Definition: loader.h:805
std::vector< std::string > waitable_services_
Definition: loader.h:1055
std::string data_folder_
Definition: loader.h:1020
std::thread signal_thread_
Definition: loader.h:1029
LoaderConfig & config_
Configuration sections for all plugins.
Definition: loader.h:987
LoaderConfig & get_config()
Get reference to configuration object.
Definition: loader.h:789
void after_all_started(std::function< void()> &&func)
set a function that's called after all plugins have been started.
Definition: loader.h:814
std::function< void(const bool, const ConfigSection &)> expose_app_config_clb_
Definition: loader.h:1064
std::function< void()> after_all_started_
Definition: loader.h:1058
std::mutex signal_thread_ready_m_
Definition: loader.h:1026
void register_supported_app_options(const std::array< std::string_view, N > &options)
Register global configuration options supported by the application.
Definition: loader.h:834
std::function< void()> after_first_finished_
Definition: loader.h:1061
Loader & operator=(const Loader &)=delete
std::string config_folder_
Definition: loader.h:1019
std::condition_variable signal_thread_ready_cond_
Definition: loader.h:1027
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:853
Loader(const Loader &)=delete
std::vector< std::string > & waitable_services()
service names to wait on.
Definition: loader.h:798
std::map< std::string, PluginInfo > PluginMap
Definition: loader.h:980
std::string program_
Definition: loader.h:1021
void after_first_finished(std::function< void()> &&func)
set a function that's called after the first plugin exited.
Definition: loader.h:823
std::string plugin_folder_
Definition: loader.h:1017
AppInfo appinfo_
Definition: loader.h:1022
std::vector< std::string > supported_app_options_
Definition: loader.h:1031
std::vector< std::shared_ptr< PluginStateObserver > > default_observers_
Definition: loader.h:1050
Status
Definition: loader.h:873
void spawn_signal_handler_thread()
PluginThreads plugin_threads_
active plugin threads.
Definition: loader.h:1009
std::map< const ConfigSection *, std::shared_ptr< PluginFuncEnv > > plugin_start_env_
Map of all {plugin instance -> plugin start() PluginFuncEnv} objects.
Definition: loader.h:1004
PluginMap plugins_
Map of all successfully-loaded plugins (without key name).
Definition: loader.h:992
std::string runtime_folder_
Definition: loader.h:1018
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:68
Definition: common.h:44
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