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