1411 lines
55 KiB
C++
1411 lines
55 KiB
C++
/* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include <mysql/components/my_service.h>
|
|
#include <mysql/components/service_implementation.h>
|
|
#include <mysql/components/services/dynamic_loader.h>
|
|
#include <mysql/components/services/dynamic_loader_scheme_file.h>
|
|
#include <mysql/components/services/mysql_runtime_error_service.h>
|
|
#include <mysql/components/services/registry.h>
|
|
#include <mysqld_error.h>
|
|
#include <stddef.h>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
|
|
#include "c_string_less.h"
|
|
#include "depth_first_search.h"
|
|
#include "dynamic_loader.h"
|
|
#include "my_dbug.h"
|
|
#include "my_inttypes.h"
|
|
#include "my_psi_config.h"
|
|
#include "my_sys.h"
|
|
#include "mysql_component.h"
|
|
#include "registry.h"
|
|
#include "rwlock_scoped_lock.h"
|
|
#include "scope_guard.h"
|
|
#include "server_component.h"
|
|
|
|
/**
|
|
@page PAGE_COMPONENTS Component Subsystem
|
|
|
|
The component subsystem is designed to overcome some of the architectural
|
|
issues of the plugin subsystem, namely:
|
|
1. Plugins can only "talk" to the server and not with other plugins
|
|
2. Plugins have access to the server symbols and can call them directly,
|
|
i.e. no encapsulation
|
|
3. There's no explicit set of dependencies of a plugin, thus it's
|
|
hard to initialize them properly
|
|
4. Plusing require a running server to operate.
|
|
|
|
The component infrastructure aims at allowing the MySQL server subsystems
|
|
to be encapsulated into a set of logical components. Additional components
|
|
may be added to a running server to extend its functionality.
|
|
|
|
Components can be linked either dynamically or statically.
|
|
|
|
Each component will provide implementations of an extensive set of named
|
|
APIs, services, that other components can consume. To facilitate this
|
|
there will be a registry of all services available to all components.
|
|
|
|
Each component will communicate with other components only through
|
|
services and will explicitly state the services it provides and consumes.
|
|
|
|
The infrastructure will enable components to override and complement
|
|
other components through re-implementing the relevant service APIs.
|
|
|
|
Once there's a critical mass of such components exposing their functionality
|
|
and consuming the services they need through named service APIs this will
|
|
allow additional loadable components to take advantage of all the
|
|
functionality they need without having to carve in specialized APIs into the
|
|
monolithic server code for each new need.
|
|
|
|
The infrastructure described here is mostly orthogonal to the ongoing
|
|
activity on modularizing the MySQL Server. However, properly defined modules
|
|
and interfaces in the Server, allows server functionality to easily be
|
|
exposed as services where it makes sense.
|
|
|
|
@subpage PAGE_COMPONENTS_CONCEPTS
|
|
|
|
@section sect_components_services_inventory Service Inventory
|
|
|
|
To the @ref group_components_services_inventory
|
|
*/
|
|
|
|
/**
|
|
@defgroup group_components_services_inventory Component Services Inventory
|
|
|
|
This is a group of all component service APIs
|
|
|
|
See @ref PAGE_COMPONENTS_SERVICE for explanation of what a component service
|
|
is
|
|
*/
|
|
|
|
/**
|
|
|
|
@page PAGE_COMPONENTS_CONCEPTS Component Subsystem Concepts
|
|
|
|
These are the building blocks of the component subsystem:
|
|
|
|
- @subpage PAGE_COMPONENTS_SERVICE
|
|
- @subpage PAGE_COMPONENTS_REGISTRY
|
|
- @subpage PAGE_COMPONENTS_DYNAMIC_LOADER
|
|
- @subpage PAGE_COMPONENTS_COMPONENT
|
|
|
|
To understand how the component subsystem interacts with the
|
|
binary it's hosted in and with the server in specific read:
|
|
|
|
- @subpage page_components_layering
|
|
- @subpage page_components_layering_plugins
|
|
*/
|
|
|
|
/**
|
|
@page PAGE_COMPONENTS_DYNAMIC_LOADER The Dynamic Loader Service
|
|
|
|
@section sect_components_dyloader_introduction Introduction
|
|
|
|
The dynamic loader Service is one of the core Services of the MySQL
|
|
Component subsystem. It is responsible for loading new components,
|
|
register the services they implement, resolve dependencies and provide
|
|
service implementations for these dependencies to the component.
|
|
It is also responsible for unloading components, effectively reversing
|
|
the effect of load operation in mostly reverse order, in unload
|
|
method().
|
|
|
|
Macros to help define components are listed in component_implementation.h.
|
|
|
|
To use the provided services a reference to the required service
|
|
implementation must be obtained from the registry: either explicitly via
|
|
calling the registry service methods or implicitly by declaring it into the
|
|
component's metadata so that the dynamic loader can satisfy that dependency
|
|
at component load time.
|
|
Once the service reference is no longer needed the reference to it must be
|
|
released. The registry keeps reference counts for each service
|
|
implementation it lists to track if a service implementation is used
|
|
or not.
|
|
|
|
Components are not reference counted, as they do not communicate with other
|
|
components in any other way but via service references.
|
|
And since service implementations are reference counted the dynamic loader
|
|
can reliably detect if all service implementations a component provides
|
|
are unused and can safely unload the component.
|
|
|
|
@section sect_components_dyloader_component_urn Component URN and schemes.
|
|
|
|
Component names are structured in such a way that allows components
|
|
from multiple sources to be loaded. Each component is
|
|
specified by a URN of "scheme://path", where "scheme" defines which
|
|
dynamic loader scheme service to use to acquire the component definition.
|
|
Single URNs can contain several components.
|
|
|
|
A basic dynamic loader scheme service is implemented for the
|
|
"file://" scheme (see @ref dynamic_loader_scheme_file.cc),
|
|
which accepts a path to a dynamic library to load components
|
|
from using the OS dynamic library APIs. This dynamic loader scheme
|
|
implementation does not rely on any server functionality and hence can
|
|
be placed into the component infrastructure core (a.k.a. the mimimal
|
|
chassis).
|
|
|
|
An additional service implementation, the path filter dynamic Loader
|
|
"file://" scheme service implementation, is provided for the "file://"
|
|
scheme as wrapper on any already registered file scheme dynamic loader
|
|
service implementation, that assumes a path argument is just a filename
|
|
without extension and limits shared libraries to be loadable only from
|
|
the MySQL server plug-in directory. The filter is implemented as a
|
|
separate service implementation as it requires access to the server
|
|
internal variables (the plugin directory path) and is hence implemented
|
|
into the server component. This also demonstrates how other components
|
|
can reimplement functionality provided by a component via setting a new
|
|
default implementation of a service they want to re-implement.
|
|
|
|
@section builtin_scheme The "builtin://" Scheme.
|
|
|
|
The dynamic loader scheme service for "builtin://" was designed to
|
|
include components that were meant to be statically linked into MySQL
|
|
executable, but to comply with the components requirements, that is mainly
|
|
to assure components do not interact with any other component in ways not
|
|
meant by the components subsystem, i.e. not through the services in the
|
|
service registry.
|
|
This is easily asserted by housing components in separate dynamic
|
|
libraries. This makes the "builtin://" scheme, a bit of a bad practice as
|
|
it breaks the safeguard of having only one component in a OS binary.
|
|
Thus currently the scheme name is reserved but no implementation of it is
|
|
provided.
|
|
|
|
@sa mysql_persistent_dynamic_loader_imp,
|
|
mysql_dynamic_loader_scheme_file_path_filter_imp
|
|
*/
|
|
/**
|
|
@page page_components_layering Component Infrastructure Layers
|
|
|
|
@section sect_components_minimal_chassis The Minimal Chassis
|
|
|
|
The core of the component infrastructure is the so called "minimal chassis".
|
|
It consists of implementations of the registry and the dynamic loader.
|
|
These are enough to bootstrap the subsystem and do not require any extra
|
|
functionality.
|
|
|
|
The minimal chassis can theoretically be embedded into any binary (be it
|
|
an executable or a shared library) and can be used to provide extensibility
|
|
to it. Hence it can even be isolated into a standalone library.
|
|
|
|
However the minimal chassis is currently is statically linked into the
|
|
server executable. Since the minimal chassis is logically independent it
|
|
can (and is) initialized very early in the server bootstrap process.
|
|
|
|
The minimal chassis lacks an impelmentation of persistent storage, i.e.
|
|
every time it's bootstraped it will only contain the registry and the
|
|
dynamic loader services. While this is a good basis it lacks a central
|
|
functionality expected from a mysql server extension system: keep a
|
|
persisted list of extensions that are to be loaded automatically into
|
|
the infrastructure in an orderly way to provide the functionality
|
|
contained in them to server users.
|
|
|
|
Hence an extra logical layer is added on top of the minimal chassis:
|
|
|
|
@section sect_components_layering_server_component The Server Component
|
|
|
|
The server component is currently the whole of the server binary code.
|
|
It's not loaded dynamically (as any ordinary component is) since it's
|
|
embedded into the same OS binary as the minimal chassis: the mysql server
|
|
executable binary.
|
|
As any other component the server component too can expose any of the
|
|
functionality it encapsulates via the service implementations it provides to
|
|
the other components in the component infrastructure.
|
|
|
|
The server component is initialized relatively late in the bootstrap process:
|
|
when all of the server is initialized and it's ready to "go" (i.e. accept
|
|
client connections). At that time the component persistency table is read
|
|
and all of the component sets defined in it are loaded and initialized.
|
|
|
|
Note that, albeit being in the same binary (component) the server
|
|
component and the minimal chassis are and should remain two distinct
|
|
layers. Mostly to allow safe reuse of the minimal chassis in other
|
|
binaries.
|
|
|
|
Hence the elements of the minimal chassis are implemented in
|
|
components/mysql-server and the implementations of services the
|
|
server component provides are to be found in sql/server_component/
|
|
*/
|
|
|
|
/**
|
|
@page page_components_layering_plugins Components and Plugins
|
|
|
|
An important question to answer is what is the relationship between
|
|
components and plugins. To the end user these both look the same: a way
|
|
to dynamically extend the functionality of the server.
|
|
And that is a sought-after resemblance.
|
|
|
|
But architecturally the two are very distinct.
|
|
|
|
Components are self-contained code containers that interact with
|
|
other code exclusively by implementing and consuming
|
|
services via the registry.
|
|
|
|
Plugins do implement plugin APIs and may choose to call plugin service
|
|
APIs exposed by the server. But in reality they have access to all of
|
|
the server binary global symbols. So most if not all plugins choose to
|
|
use these instead of confining themselves into the (admitedly limited
|
|
set of) plugin APIs available.
|
|
|
|
The above makes plugins an integral part of the server codebase, more
|
|
specifically of the server component.
|
|
|
|
@attention Plugins are dynamically loadable bits of the
|
|
@ref sect_components_layering_server_component
|
|
*/
|
|
|
|
/**
|
|
This place holder is required for the mysql_runtime_error service.
|
|
The service is used in mysql_error_service_printf() api, which is the
|
|
replacement for my_error() server api.
|
|
*/
|
|
REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error);
|
|
my_h_service h_err_service;
|
|
|
|
static PSI_rwlock_key key_rwlock_LOCK_dynamic_loader;
|
|
|
|
struct my_h_component_iterator_imp {
|
|
my_component_registry::const_iterator m_it;
|
|
rwlock_scoped_lock m_lock;
|
|
};
|
|
|
|
struct my_h_component_metadata_iterator_imp {
|
|
my_metadata::const_iterator m_it;
|
|
rwlock_scoped_lock m_lock;
|
|
};
|
|
|
|
/**
|
|
Initializes loader for usage. Initializes RW lock, all other structures
|
|
should be empty. Shouldn't be called multiple times.
|
|
*/
|
|
void mysql_dynamic_loader_imp::init() {
|
|
mysql_rwlock_init(key_rwlock_LOCK_dynamic_loader,
|
|
&mysql_dynamic_loader_imp::LOCK_dynamic_loader);
|
|
}
|
|
/**
|
|
De-initializes loader. De-initializes RW lock, all other structures
|
|
doesn't require any action.
|
|
*/
|
|
void mysql_dynamic_loader_imp::deinit() {
|
|
/* Leave scope and lock before destroying it. */
|
|
{
|
|
rwlock_scoped_lock lock(&mysql_dynamic_loader_imp::LOCK_dynamic_loader,
|
|
true, __FILE__, __LINE__);
|
|
|
|
/* Unload all Components that are loaded. All Components are unloaded in
|
|
one big group to prevent any problems with dependencies. This on the
|
|
other side may lead to situation where one of Components will not unload
|
|
properly, causing all other to not be unloaded, leaving them all still
|
|
loaded in and not deinitialized. There should be an error message issued
|
|
stating a problem during unload to help detect such a problem. */
|
|
if (mysql_dynamic_loader_imp::components_list.size() > 0) {
|
|
const char **urns =
|
|
new const char *[mysql_dynamic_loader_imp::components_list.size()];
|
|
|
|
/* Collect a list of components to unload. */
|
|
int i = 0;
|
|
for (my_component_registry::reverse_iterator it =
|
|
mysql_dynamic_loader_imp::components_list.rbegin();
|
|
it != mysql_dynamic_loader_imp::components_list.rend(); ++it) {
|
|
urns[i] = it->first;
|
|
++i;
|
|
}
|
|
|
|
/* Unload all components. */
|
|
mysql_dynamic_loader_imp::unload_do_list_components(urns, i);
|
|
|
|
delete[] urns;
|
|
}
|
|
}
|
|
|
|
mysql_rwlock_destroy(&mysql_dynamic_loader_imp::LOCK_dynamic_loader);
|
|
}
|
|
|
|
/**
|
|
Loads specified group of Components by URN, initializes them and
|
|
registers all Service Implementations present in these Components.
|
|
Assures all dependencies will be met after loading specified Components.
|
|
The dependencies may be circular, in such case it's necessary to specify
|
|
all Components on cycle to load in one batch. From URNs specified the
|
|
scheme part of URN (part before "://") is extracted and used to acquire
|
|
Service Implementation of scheme Component loader Service for specified
|
|
scheme.
|
|
|
|
@param urns List of URNs of Components to load.
|
|
@param component_count Number of Components on list to load.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
DEFINE_BOOL_METHOD(mysql_dynamic_loader_imp::load,
|
|
(const char *urns[], int component_count)) {
|
|
try {
|
|
/* Acquire write lock for entire process, possibly this could be
|
|
optimized, but must be done with care. */
|
|
rwlock_scoped_lock lock(&mysql_dynamic_loader_imp::LOCK_dynamic_loader,
|
|
true, __FILE__, __LINE__);
|
|
|
|
/* This method calls a chain of methods to perform load operation.
|
|
Each element in chain performs specific part of process, a stage, is
|
|
named upon what stage it performs, and is responsible of calling the
|
|
next element in the chain and performing rollback of changes it
|
|
performed, in case the whole operation do not succeed. The following
|
|
methods are called, in order:
|
|
- load_do_load_component_by_scheme
|
|
- load_do_collect_services_provided
|
|
- load_do_check_dependencies
|
|
- load_do_register_services
|
|
- load_do_resolve_dependencies
|
|
- load_do_initialize_components
|
|
- load_do_commit */
|
|
return mysql_dynamic_loader_imp::load_do_load_component_by_scheme(
|
|
urns, component_count);
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Unloads specified group of Components by URN, deinitializes them and
|
|
unregisters all Service Implementations present in these Components.
|
|
Assumes, thous does not check it, all dependencies of not unloaded
|
|
Components will still be met after unloading specified Components.
|
|
The dependencies may be circular, in such case it's necessary to specify
|
|
all Components on cycle to unload in one batch. From URNs specified the
|
|
scheme part of URN (part before "://") is extracted and used to acquire
|
|
Service Implementation of scheme Component loader Service for specified
|
|
scheme. URN specified should be identical to ones specified in load()
|
|
method, i.e. all letters must have the same case.
|
|
|
|
@param urns List of URNs of Components to unload.
|
|
@param component_count Number of Components on list to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
DEFINE_BOOL_METHOD(mysql_dynamic_loader_imp::unload,
|
|
(const char *urns[], int component_count)) {
|
|
try {
|
|
rwlock_scoped_lock lock(&mysql_dynamic_loader_imp::LOCK_dynamic_loader,
|
|
true, __FILE__, __LINE__);
|
|
|
|
/* This method calls a chain of methods to perform unload operation.
|
|
Each element in chain performs specific part of process, a stage, is
|
|
named upon what stage it performs, and is responsible of calling the
|
|
next element in the chain and performing rollback of changes it
|
|
performed, in case the whole operation do not succeed. The following
|
|
methods are called, in order:
|
|
- unload_do_list_components
|
|
- unload_do_topological_order
|
|
- unload_do_get_scheme_services
|
|
- unload_do_lock_provided_services
|
|
- unload_do_check_provided_services_reference_count
|
|
- unload_do_deinitialize_components
|
|
- unload_do_unload_dependencies
|
|
- unload_do_unregister_services
|
|
- unload_do_unload_components
|
|
- unload_do_commit */
|
|
return mysql_dynamic_loader_imp::unload_do_list_components(urns,
|
|
component_count);
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Creates iterator that iterates through all loaded Components.
|
|
If successful it leaves read lock on dynamic loader until iterator is
|
|
released.
|
|
|
|
@param [out] out_iterator Pointer to Component iterator handle.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
DEFINE_BOOL_METHOD(mysql_dynamic_loader_imp::iterator_create,
|
|
(my_h_component_iterator * out_iterator)) {
|
|
try {
|
|
*out_iterator = NULL;
|
|
|
|
/* This read lock on whole component registry will be held, until the
|
|
iterator is released. */
|
|
rwlock_scoped_lock lock(&mysql_dynamic_loader_imp::LOCK_dynamic_loader,
|
|
false, __FILE__, __LINE__);
|
|
|
|
my_component_registry::const_iterator r =
|
|
mysql_dynamic_loader_imp::components_list.cbegin();
|
|
|
|
if (r == mysql_dynamic_loader_imp::components_list.cend()) {
|
|
return true;
|
|
}
|
|
|
|
*out_iterator = new my_h_component_iterator_imp{r, std::move(lock)};
|
|
return false;
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Releases Component iterator. Releases read lock on dynamic loader.
|
|
|
|
@param iterator Component iterator handle.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
DEFINE_METHOD(void, mysql_dynamic_loader_imp::iterator_release,
|
|
(my_h_component_iterator iterator)) {
|
|
try {
|
|
my_h_component_iterator_imp *iter =
|
|
reinterpret_cast<my_h_component_iterator_imp *>(iterator);
|
|
|
|
if (!iter) return;
|
|
|
|
delete iter;
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Gets name and URN of Service pointed to by iterator.
|
|
|
|
@param iterator Component iterator handle.
|
|
@param [out] out_name Pointer to string with Component name to set result
|
|
pointer to.
|
|
@param [out] out_urn Pointer to string with URN from which the Component was
|
|
loaded from, to set result pointer to.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true Failure, may be caused when called on iterator that went
|
|
through all values already.
|
|
*/
|
|
DEFINE_BOOL_METHOD(mysql_dynamic_loader_imp::iterator_get,
|
|
(my_h_component_iterator iterator, const char **out_name,
|
|
const char **out_urn)) {
|
|
try {
|
|
*out_name = NULL;
|
|
*out_urn = NULL;
|
|
|
|
if (!iterator) return true;
|
|
|
|
my_component_registry::const_iterator &iter =
|
|
reinterpret_cast<my_h_component_iterator_imp *>(iterator)->m_it;
|
|
|
|
if (iter != mysql_dynamic_loader_imp::components_list.cend()) {
|
|
mysql_component *imp = iter->second.get();
|
|
*out_name = imp->name_c_str();
|
|
*out_urn = imp->urn_c_str();
|
|
|
|
return false;
|
|
}
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Advances specified iterator to next element. Will succeed but return true if
|
|
it reaches one-past-last element.
|
|
|
|
@param iterator Component iterator handle.
|
|
@return Status of performed operation and validity of iterator after
|
|
operation.
|
|
@retval false success
|
|
@retval true Failure or called on iterator that was on last element.
|
|
*/
|
|
DEFINE_BOOL_METHOD(mysql_dynamic_loader_imp::iterator_next,
|
|
(my_h_component_iterator iterator)) {
|
|
try {
|
|
if (!iterator) return true;
|
|
|
|
my_component_registry::const_iterator &iter =
|
|
reinterpret_cast<my_h_component_iterator_imp *>(iterator)->m_it;
|
|
|
|
if (iter != mysql_dynamic_loader_imp::components_list.cend()) {
|
|
++iter;
|
|
return iter == mysql_dynamic_loader_imp::components_list.cend();
|
|
}
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Checks if specified iterator is valid, i.e. have not reached one-past-last
|
|
element.
|
|
|
|
@param iterator Component iterator handle.
|
|
@return Validity of iterator
|
|
@retval false Valid
|
|
@retval true Invalid or reached one-past-last element.
|
|
*/
|
|
DEFINE_BOOL_METHOD(mysql_dynamic_loader_imp::iterator_is_valid,
|
|
(my_h_component_iterator iterator)) {
|
|
try {
|
|
if (!iterator) return true;
|
|
|
|
my_component_registry::const_iterator &iter =
|
|
reinterpret_cast<my_h_component_iterator_imp *>(iterator)->m_it;
|
|
|
|
return iter == mysql_dynamic_loader_imp::components_list.cend();
|
|
} catch (...) {
|
|
mysql_components_handle_std_exception(__func__);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* This includes metadata-related method implementations that are shared
|
|
by registry and dynamic_loader, so we don't duplicate the code. Following
|
|
defines set up all required symbols. Unfortunately they are not only the
|
|
types, but also static members with different name, so usage of templates
|
|
is not enough to reuse that part of code. */
|
|
#define REGISTRY_IMP mysql_dynamic_loader_imp
|
|
#define REGISTRY mysql_dynamic_loader_imp::components_list
|
|
#define REGISTRY_TYPE my_component_registry
|
|
#define LOCK mysql_dynamic_loader_imp::LOCK_dynamic_loader
|
|
#define ITERATOR_TYPE my_h_component_iterator_imp
|
|
#define METADATA_ITERATOR_TYPE my_h_component_metadata_iterator_imp
|
|
#define OBJECT_ITERATOR my_h_component_iterator
|
|
#define METADATA_ITERATOR my_h_component_metadata_iterator
|
|
|
|
#include "registry_metadata.cc.inc"
|
|
|
|
/**
|
|
Loads specified group of Components by URN. From URNs specified the
|
|
scheme part of URN (part before "://") is extracted and used to acquire
|
|
Service Implementation of scheme Component loader Service for specified
|
|
scheme. In case of failure rollbacks all changes, i.e. unloads loaded
|
|
Components.
|
|
|
|
@param urns List of URNs of Components to load.
|
|
@param component_count Number of Components on list to load.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_load_component_by_scheme(
|
|
const char *urns[], int component_count) {
|
|
scheme_service_map scheme_services;
|
|
|
|
/* List of components that were just loaded by scheme loader services. */
|
|
std::vector<std::unique_ptr<mysql_component>> loaded_components;
|
|
|
|
auto guard = create_scope_guard([&loaded_components, &scheme_services]() {
|
|
/* In case we need to rollback we need to undo what next for loop does,
|
|
i.e. unload all components using assigned scheme loader. */
|
|
for (std::unique_ptr<mysql_component> &loaded_component :
|
|
loaded_components) {
|
|
const my_string &urn = loaded_component->get_urn();
|
|
|
|
SERVICE_TYPE(dynamic_loader_scheme) * scheme_service;
|
|
/* We don't want scheme_services to be in the same scope as usages
|
|
of this deleter are. Right now these are in try{} scope, and it is
|
|
OK. */
|
|
if (mysql_dynamic_loader_imp::get_scheme_service_from_urn(
|
|
urn, &scheme_service, scheme_services)) {
|
|
return;
|
|
}
|
|
scheme_service->unload(urn.c_str());
|
|
}
|
|
});
|
|
|
|
/* First load all components. */
|
|
for (int it = 0; it < component_count; ++it) {
|
|
my_string urn(urns[it]);
|
|
|
|
SERVICE_TYPE(dynamic_loader_scheme) * scheme_service;
|
|
|
|
/* Try to get service responsible for handling specified scheme type. */
|
|
if (mysql_dynamic_loader_imp::get_scheme_service_from_urn(
|
|
urn, &scheme_service, scheme_services)) {
|
|
return true;
|
|
}
|
|
|
|
/* Load component using scheme service. The result is pointer to NULL
|
|
terminated list of components. */
|
|
mysql_component_t *loaded_component_raw;
|
|
if (scheme_service->load(urn.c_str(), &loaded_component_raw)) {
|
|
mysql_error_service_printf(ER_COMPONENTS_CANT_LOAD, MYF(0), urn.c_str());
|
|
return true;
|
|
}
|
|
/* Here we assume loaded_component_raw will be list with only one item. */
|
|
loaded_components.push_back(std::unique_ptr<mysql_component>(
|
|
new mysql_component(loaded_component_raw, urn)));
|
|
}
|
|
|
|
bool res = mysql_dynamic_loader_imp::load_do_collect_services_provided(
|
|
loaded_components);
|
|
if (!res) {
|
|
guard.commit();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
Prepares a list of all Services that are provided by specified Components.
|
|
This will enable us in next step to check if these may be used to satisfy
|
|
other Components dependencies.
|
|
|
|
@param loaded_components List of Components to continue load of.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_collect_services_provided(
|
|
std::vector<std::unique_ptr<mysql_component>> &loaded_components) {
|
|
/* Set of names of services without implementation names that
|
|
specified components provide. */
|
|
std::set<my_string> services_provided;
|
|
|
|
/* Add all services this component provides to list of provided services. */
|
|
for (const std::unique_ptr<mysql_component> &loaded_component :
|
|
loaded_components) {
|
|
for (const mysql_service_ref_t *service_provided :
|
|
loaded_component->get_provided_services()) {
|
|
const char *dot_position = strchr(service_provided->name, '.');
|
|
if (dot_position == NULL) {
|
|
services_provided.insert(my_string(service_provided->name));
|
|
} else {
|
|
/* Insert only part before the dot, which is only the service name,
|
|
without implementation name. */
|
|
services_provided.insert(my_string(
|
|
service_provided->name, dot_position - service_provided->name));
|
|
}
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::load_do_check_dependencies(
|
|
loaded_components, services_provided);
|
|
}
|
|
|
|
/**
|
|
Checks if all dependencies can be satisfied with existing or to be added
|
|
Services.
|
|
|
|
@param loaded_components List of Components to continue load of.
|
|
@param services_provided List of Services that are being provided by
|
|
Components to be loaded.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_check_dependencies(
|
|
std::vector<std::unique_ptr<mysql_component>> &loaded_components,
|
|
const std::set<my_string> &services_provided) {
|
|
/* Check all dependencies can be met with services already registered in
|
|
registry or from components that are being loaded. */
|
|
for (const std::unique_ptr<mysql_component> &loaded_component :
|
|
loaded_components) {
|
|
for (const mysql_service_placeholder_ref_t *service_required :
|
|
loaded_component->get_required_services()) {
|
|
/* Try lookup in services provided by other components being loaded. */
|
|
if (services_provided.find(my_string(service_required->name)) !=
|
|
services_provided.end()) {
|
|
continue;
|
|
}
|
|
|
|
/* Try to lookup in services registered in registry service */
|
|
my_h_service_iterator service_iterator;
|
|
if (!mysql_registry_imp::iterator_create(service_required->name,
|
|
&service_iterator)) {
|
|
mysql_registry_imp::iterator_release(service_iterator);
|
|
continue;
|
|
}
|
|
|
|
/* None service matches requirement, we shall fail. */
|
|
mysql_error_service_printf(ER_COMPONENTS_CANT_SATISFY_DEPENDENCY, MYF(0),
|
|
service_required->name,
|
|
loaded_component->name_c_str());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::load_do_register_services(loaded_components);
|
|
}
|
|
|
|
/**
|
|
Registers all Services that are provided by specified Components.
|
|
In case of failure rollbacks all changes, i.e. unregister registered Service
|
|
Implementations.
|
|
|
|
@param loaded_components List of Components to continue load of.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_register_services(
|
|
std::vector<std::unique_ptr<mysql_component>> &loaded_components) {
|
|
/* List of services from components that were registered. */
|
|
std::vector<const char *> registered_services;
|
|
auto guard = create_scope_guard([®istered_services]() {
|
|
for (const char *service_name : registered_services) {
|
|
mysql_registry_imp::unregister(service_name);
|
|
}
|
|
});
|
|
|
|
/* Register services from components. */
|
|
for (const std::unique_ptr<mysql_component> &loaded_component :
|
|
loaded_components) {
|
|
/* Register all services from component. */
|
|
for (const mysql_service_ref_t *implementation_it :
|
|
loaded_component->get_provided_services()) {
|
|
if (mysql_registry_imp::register_service(
|
|
implementation_it->name,
|
|
reinterpret_cast<my_h_service>(
|
|
implementation_it->implementation))) {
|
|
mysql_error_service_printf(
|
|
ER_COMPONENTS_LOAD_CANT_REGISTER_SERVICE_IMPLEMENTATION, MYF(0),
|
|
implementation_it->name, loaded_component->name_c_str());
|
|
return true;
|
|
}
|
|
registered_services.push_back(implementation_it->name);
|
|
}
|
|
}
|
|
|
|
bool res =
|
|
mysql_dynamic_loader_imp::load_do_resolve_dependencies(loaded_components);
|
|
if (!res) {
|
|
guard.commit();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
Acquires Service Implementations for all dependencies of Components.
|
|
In case of failure rollbacks all changes, i.e. release Services that were
|
|
acquired.
|
|
|
|
@param loaded_components List of Components to continue load of.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_resolve_dependencies(
|
|
std::vector<std::unique_ptr<mysql_component>> &loaded_components) {
|
|
/* List of services acquired for component dependencies. */
|
|
std::vector<my_h_service *> acquired_services;
|
|
auto guard = create_scope_guard([&acquired_services]() {
|
|
for (my_h_service *service_storage : acquired_services) {
|
|
mysql_registry_imp::release(*service_storage);
|
|
*service_storage = NULL;
|
|
}
|
|
});
|
|
|
|
/* Acquire services to meet component dependencies. */
|
|
for (const std::unique_ptr<mysql_component> &loaded_component :
|
|
loaded_components) {
|
|
/* Meet all dependencies for all components. */
|
|
for (mysql_service_placeholder_ref_t *implementation_it :
|
|
loaded_component->get_required_services()) {
|
|
if (mysql_registry_imp::acquire(implementation_it->name,
|
|
reinterpret_cast<my_h_service *>(
|
|
implementation_it->implementation))) {
|
|
mysql_error_service_printf(
|
|
ER_COMPONENTS_CANT_ACQUIRE_SERVICE_IMPLEMENTATION, MYF(0),
|
|
implementation_it->name);
|
|
return true;
|
|
}
|
|
acquired_services.push_back(
|
|
reinterpret_cast<my_h_service *>(implementation_it->implementation));
|
|
}
|
|
}
|
|
|
|
bool res = mysql_dynamic_loader_imp::load_do_initialize_components(
|
|
loaded_components);
|
|
if (!res) {
|
|
guard.commit();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
Calls Components initialization method to make Components ready to function.
|
|
In case of failure rollbacks all changes, i.e. calls deinitialization
|
|
methods on initialized Components.
|
|
|
|
@param loaded_components List of Components to continue load of.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_initialize_components(
|
|
std::vector<std::unique_ptr<mysql_component>> &loaded_components) {
|
|
/* List of components that were initialized. */
|
|
std::vector<mysql_component *> initialized_components;
|
|
|
|
auto guard = create_scope_guard([&initialized_components]() {
|
|
for (mysql_component *initialized_component : initialized_components) {
|
|
if (initialized_component->get_data()->deinit != NULL) {
|
|
initialized_component->get_data()->deinit();
|
|
}
|
|
}
|
|
});
|
|
|
|
/* Initialize components. */
|
|
for (const std::unique_ptr<mysql_component> &loaded_component :
|
|
loaded_components) {
|
|
/* Initialize component, move to main collection of components,
|
|
add to temporary list of components registered, in case we need to
|
|
unregister them on failure. */
|
|
if (loaded_component->get_data()->init != NULL &&
|
|
loaded_component->get_data()->init()) {
|
|
mysql_error_service_printf(ER_COMPONENTS_LOAD_CANT_INITIALIZE, MYF(0),
|
|
loaded_component->name_c_str());
|
|
return true;
|
|
}
|
|
|
|
initialized_components.push_back(loaded_component.get());
|
|
}
|
|
|
|
bool res = mysql_dynamic_loader_imp::load_do_commit(loaded_components);
|
|
if (!res) {
|
|
guard.commit();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
Adds all Components to main list of loaded Components. Marks changes done by
|
|
all previous steps as not to be rolled back.
|
|
|
|
@param loaded_components List of Components to continue load of.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::load_do_commit(
|
|
std::vector<std::unique_ptr<mysql_component>> &loaded_components) {
|
|
/* Move components to the main list of components loaded. */
|
|
for (std::unique_ptr<mysql_component> &loaded_component : loaded_components) {
|
|
mysql_dynamic_loader_imp::components_list.emplace(
|
|
loaded_component->urn_c_str(), std::move(loaded_component));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
Unloads all Components specified in list. It does not acquire a write lock
|
|
on dynamic loader, but requires it to be acquired by caller.
|
|
|
|
@param urns List of URNs of Components to unload.
|
|
@param component_count Number of Components on list to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_list_components(const char *urns[],
|
|
int component_count) {
|
|
std::vector<mysql_component *> components_to_unload;
|
|
/* Finds any duplicated entries in the group specified. */
|
|
std::set<mysql_component *> components_in_group;
|
|
|
|
/* Lookup for components by URNs specified. */
|
|
for (int it = 0; it < component_count; ++it) {
|
|
my_string urn = my_string(urns[it]);
|
|
my_component_registry::iterator component_it =
|
|
mysql_dynamic_loader_imp::components_list.find(urn.c_str());
|
|
/* Return error if any component is not loaded. */
|
|
if (component_it == mysql_dynamic_loader_imp::components_list.end()) {
|
|
mysql_error_service_printf(ER_COMPONENTS_UNLOAD_NOT_LOADED, MYF(0),
|
|
urn.c_str());
|
|
return true;
|
|
}
|
|
components_to_unload.push_back(component_it->second.get());
|
|
if (!components_in_group.insert(component_it->second.get()).second) {
|
|
mysql_error_service_printf(ER_COMPONENTS_UNLOAD_DUPLICATE_IN_GROUP,
|
|
MYF(0), urn.c_str());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::unload_do_topological_order(
|
|
components_to_unload);
|
|
}
|
|
|
|
/**
|
|
Orders components in a order that would allow allow deinitialization to be
|
|
done always for components that have all their dependencies still not
|
|
deinitialized. It also creates a graph of dependencies between the Service
|
|
Implementations provided by the Components to be unloaded and Components
|
|
that use this Service Implementation.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_topological_order(
|
|
const std::vector<mysql_component *> &components_to_unload) {
|
|
/* A graph of dependencies between the Components to be unloaded. For each
|
|
Service Implementation that is provided by any of the Components to be
|
|
unloaded a list of other Components to be unloaded is specified, which all
|
|
have an actual dependency resolved to that particular Service
|
|
Implementation. */
|
|
std::map<const void *, std::vector<mysql_component *>> dependency_graph;
|
|
|
|
/* First list all Service Implementations that are provided by the Components
|
|
to be unloaded.*/
|
|
for (mysql_component *component : components_to_unload) {
|
|
for (const mysql_service_ref_t *service :
|
|
component->get_provided_services()) {
|
|
dependency_graph.emplace(service->implementation,
|
|
std::vector<mysql_component *>{});
|
|
}
|
|
}
|
|
/* Iterate through all dependencies of the Components to be unloaded to check
|
|
which were resolved using Service Implementations provided by any of listed
|
|
above. */
|
|
for (mysql_component *component : components_to_unload) {
|
|
for (const mysql_service_placeholder_ref_t *service :
|
|
component->get_required_services()) {
|
|
std::map<const void *, std::vector<mysql_component *>>::iterator it =
|
|
dependency_graph.find(*service->implementation);
|
|
if (it != dependency_graph.end()) {
|
|
it->second.push_back(component);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Iterate through all components to get the topological order of
|
|
the Components to be unloaded. */
|
|
std::vector<mysql_component *> components_to_unload_ordered;
|
|
/* Set of Components that were already visited by the DFS, to keep state
|
|
between calls to the DFS. */
|
|
std::set<mysql_component *> visited_set;
|
|
|
|
for (mysql_component *component : components_to_unload) {
|
|
/* A DFS run on directional graph, possibly a cyclic-one, with
|
|
V=(list of the Components to unload) and
|
|
E={A->B : component B depends on component A}. A DFS post-order will
|
|
result in a topological ordered list of components. */
|
|
depth_first_search(
|
|
component, [](mysql_component *) {},
|
|
[&components_to_unload_ordered](mysql_component *visited_component) {
|
|
components_to_unload_ordered.push_back(visited_component);
|
|
},
|
|
[&dependency_graph](mysql_component *visited_component) {
|
|
/* List of neighbors, i.e. all components that are dependent on
|
|
currently visited one. */
|
|
std::vector<mysql_component *> component_dependencies;
|
|
|
|
/* Iterate though all provided services to see if any other Component
|
|
to be unloaded is not depending on that Service Implementation. */
|
|
for (const mysql_service_ref_t *service :
|
|
visited_component->get_provided_services()) {
|
|
for (mysql_component *dependent_component :
|
|
dependency_graph[service->implementation]) {
|
|
component_dependencies.push_back(dependent_component);
|
|
}
|
|
}
|
|
|
|
return component_dependencies;
|
|
},
|
|
visited_set);
|
|
}
|
|
|
|
DBUG_ASSERT(components_to_unload.size() ==
|
|
components_to_unload_ordered.size());
|
|
|
|
return mysql_dynamic_loader_imp::unload_do_get_scheme_services(
|
|
components_to_unload_ordered, dependency_graph);
|
|
}
|
|
|
|
/**
|
|
Prefetch all scheme loading Services before we get a lock on a Registry.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param dependency_graph A graph of dependencies between the Components
|
|
to be unloaded.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_get_scheme_services(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
const std::map<const void *, std::vector<mysql_component *>>
|
|
&dependency_graph) {
|
|
scheme_service_map scheme_services;
|
|
|
|
for (mysql_component *component : components_to_unload) {
|
|
SERVICE_TYPE(dynamic_loader_scheme) * scheme_service;
|
|
|
|
/* Try to get service responsible for handling specified scheme type. */
|
|
if (mysql_dynamic_loader_imp::get_scheme_service_from_urn(
|
|
my_string(component->urn_c_str()), &scheme_service,
|
|
scheme_services)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::unload_do_lock_provided_services(
|
|
components_to_unload, dependency_graph, scheme_services);
|
|
}
|
|
|
|
/**
|
|
Takes a lock on all services that are provided by the Components to be
|
|
unloaded, to prevent reference count from being changed.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param dependency_graph A graph of dependencies between the Components
|
|
to be unloaded.
|
|
@param scheme_services Map of scheme loading Services prefetched with
|
|
Service Implementations required to unload all Components to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_lock_provided_services(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
const std::map<const void *, std::vector<mysql_component *>>
|
|
&dependency_graph,
|
|
scheme_service_map &scheme_services) {
|
|
/* We do lock the whole registry, as we don't have yet any better granulation.
|
|
*/
|
|
rwlock_scoped_lock lock = mysql_registry_imp::lock_registry_for_write();
|
|
return mysql_dynamic_loader_imp ::
|
|
unload_do_check_provided_services_reference_count(
|
|
components_to_unload, dependency_graph, scheme_services);
|
|
}
|
|
|
|
/**
|
|
Checks if all Service Implementations provided by the Components to be
|
|
unloaded have no references outside the group of Components to be unloaded.
|
|
This assures that continuing deinitialization of these Components in
|
|
topological order, and by this also unregistration of all provided Service
|
|
Implementations will succeed.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param dependency_graph A graph of dependencies between the Components
|
|
to be unloaded.
|
|
@param scheme_services Map of scheme loading Services prefetched with
|
|
Service Implementations required to unload all Components to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp ::
|
|
unload_do_check_provided_services_reference_count(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
const std::map<const void *, std::vector<mysql_component *>>
|
|
&dependency_graph,
|
|
scheme_service_map &scheme_services) {
|
|
/* Iterate through all Service Implementations that are provided by the
|
|
Components to be unloaded to see if all have provided Service
|
|
Implementations that are not used by Components outside the group of
|
|
Components to unload.*/
|
|
for (mysql_component *component : components_to_unload) {
|
|
for (const mysql_service_ref_t *service :
|
|
component->get_provided_services()) {
|
|
uint64_t reference_count =
|
|
mysql_registry_imp::get_service_implementation_reference_count(
|
|
reinterpret_cast<my_h_service>(service->implementation));
|
|
if (reference_count > 0) {
|
|
std::map<const void *, std::vector<mysql_component *>>::const_iterator
|
|
it = dependency_graph.find(service->implementation);
|
|
if (it == dependency_graph.end() ||
|
|
reference_count != it->second.size()) {
|
|
mysql_error_service_printf(
|
|
ER_COMPONENTS_UNLOAD_CANT_UNREGISTER_SERVICE, MYF(0),
|
|
service->name, component->name_c_str());
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return mysql_dynamic_loader_imp::unload_do_deinitialize_components(
|
|
components_to_unload, scheme_services);
|
|
}
|
|
|
|
/**
|
|
Deinitialize Components using their deinitialization method.
|
|
In case of failure rollbacks all changes, i.e. calls initialization
|
|
method again on deinitialized Components.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param scheme_services Map of scheme loading Services prefetched with
|
|
Service Implementations required to unload all Components to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_deinitialize_components(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
scheme_service_map &scheme_services) {
|
|
bool deinit_result = false;
|
|
/* Release all Services that are used as dependencies, as there can be
|
|
references to Services provided by other components to be unloaded. */
|
|
for (mysql_component *component : components_to_unload) {
|
|
if (component->get_data()->deinit != NULL) {
|
|
if (component->get_data()->deinit()) {
|
|
/* In case of error we don't want to try to restore consistent state.
|
|
This is arbitrary decision, rollback of this operation is possible,
|
|
but it's not sure if components will be able to initialize again
|
|
properly, causing state to be inconsistent. */
|
|
mysql_error_service_printf(ER_COMPONENTS_UNLOAD_CANT_DEINITIALIZE,
|
|
MYF(0), component->name_c_str());
|
|
deinit_result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return deinit_result ||
|
|
mysql_dynamic_loader_imp::unload_do_unload_dependencies(
|
|
components_to_unload, scheme_services);
|
|
}
|
|
|
|
/**
|
|
Releases Service Implementations acquired to satisfy dependencies.
|
|
In case of failure rollbacks all changes, i.e. acquires Services for
|
|
released dependencies again.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param scheme_services Map of scheme loading Services prefetched with
|
|
Service Implementations required to unload all Components to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_unload_dependencies(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
scheme_service_map &scheme_services) {
|
|
bool unload_depends_result = false;
|
|
/* Release all services that are used as dependencies, as there can be
|
|
references to services provided by other components to be unloaded. */
|
|
for (mysql_component *component : components_to_unload) {
|
|
for (mysql_service_placeholder_ref_t *service_dependency :
|
|
component->get_required_services()) {
|
|
if (mysql_registry_imp::release_nolock(reinterpret_cast<my_h_service>(
|
|
*service_dependency->implementation))) {
|
|
/* In case of error we don't want to try to restore consistent state.
|
|
This is arbitrary decision, rollback of this operation is possible,
|
|
but it's not sure if components will be able to initialize again
|
|
properly, causing state to be inconsistent. */
|
|
mysql_error_service_printf(ER_COMPONENTS_CANT_RELEASE_SERVICE, MYF(0));
|
|
unload_depends_result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::unload_do_unregister_services(
|
|
components_to_unload, scheme_services) ||
|
|
unload_depends_result;
|
|
}
|
|
|
|
/**
|
|
Unregisters all Service Implementations of specified Components.
|
|
In case of failure rollbacks all changes, i.e. registers unregistered
|
|
Service Implementations again.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param scheme_services Map of scheme loading Services prefetched with
|
|
Service Implementations required to unload all Components to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_unregister_services(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
scheme_service_map &scheme_services) {
|
|
bool unregister_result = false;
|
|
/* Unregister all services that are provided by these components. */
|
|
for (mysql_component *component : components_to_unload) {
|
|
for (const mysql_service_ref_t *service_provided :
|
|
component->get_provided_services()) {
|
|
if (mysql_registry_imp::unregister_nolock(service_provided->name)) {
|
|
/* In case of error we don't want to try to restore consistent state.
|
|
This is arbitrary decision, rollback of this operation is possible,
|
|
but it's not sure if components will be able to initialize again
|
|
properly, causing state to be inconsistent. */
|
|
mysql_error_service_printf(ER_COMPONENTS_UNLOAD_CANT_UNREGISTER_SERVICE,
|
|
MYF(0), service_provided->name,
|
|
component->name_c_str());
|
|
unregister_result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::unload_do_unload_components(
|
|
components_to_unload, scheme_services) ||
|
|
unregister_result;
|
|
}
|
|
|
|
/**
|
|
Uses Component URN to extract the scheme part of URN (part before "://") and
|
|
use it to acquire Service Implementation of scheme Component loader Service
|
|
for specified scheme, used then to unload specified Components. The unloaded
|
|
Components are removed from the main list of all loaded Components.
|
|
In case of failure rollbacks all changes, i.e. loads unloaded Components
|
|
by their URN and add them to the main list of loaded Components again.
|
|
|
|
@param components_to_unload List of Components to continue unload of.
|
|
@param scheme_services Map of scheme loading Services prefetched with
|
|
Service Implementations required to unload all Components to unload.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_unload_components(
|
|
const std::vector<mysql_component *> &components_to_unload,
|
|
scheme_service_map &scheme_services) {
|
|
bool unload_result = false;
|
|
/* Unload components and remove them from main dictionary. */
|
|
for (mysql_component *component : components_to_unload) {
|
|
SERVICE_TYPE(dynamic_loader_scheme) * scheme_service;
|
|
|
|
/* Try to get service responsible for handling specified scheme type. */
|
|
if (mysql_dynamic_loader_imp::get_scheme_service_from_urn(
|
|
my_string(component->urn_c_str()), &scheme_service,
|
|
scheme_services)) {
|
|
/* In case of error we don't want to try to restore consistent state.
|
|
This is arbitrary decision, rollback of this operation is possible,
|
|
but it's not sure if components will be able to initialize again
|
|
properly, causing state to be inconsistent. */
|
|
unload_result = true;
|
|
continue;
|
|
}
|
|
|
|
my_string component_urn = my_string(component->urn_c_str());
|
|
|
|
mysql_dynamic_loader_imp::components_list.erase(
|
|
mysql_dynamic_loader_imp::components_list.find(component_urn.c_str()));
|
|
|
|
if (scheme_service->unload(component_urn.c_str())) {
|
|
/* In case of error we don't want to try to restore consistent state.
|
|
This is arbitrary decision, rollback of this operation is possible,
|
|
but it's not sure if components will be able to initialize again
|
|
properly, causing state to be inconsistent. */
|
|
mysql_error_service_printf(ER_COMPONENTS_CANT_UNLOAD, MYF(0),
|
|
component_urn.c_str());
|
|
unload_result = true;
|
|
}
|
|
}
|
|
|
|
return mysql_dynamic_loader_imp::unload_do_commit() || unload_result;
|
|
}
|
|
|
|
/**
|
|
Finishes unloading process by marking changes to not be rolled back.
|
|
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::unload_do_commit() {
|
|
/* All components were successfully unloaded, we commit changes by returning
|
|
no error. */
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
Returns scheme loading Service based on given URN. It uses supplied cache to
|
|
reuse Services. If Service is not present in cache, it will be acquired from
|
|
registry.
|
|
|
|
@param urn URN of Components to get scheme loader Service for.
|
|
@param [out] out_scheme_service Pointer to store result scheme loader
|
|
Service.
|
|
@param [in,out] scheme_services Map of scheme loader services already
|
|
acquired.
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
bool mysql_dynamic_loader_imp::get_scheme_service_from_urn(
|
|
const my_string &urn,
|
|
SERVICE_TYPE(dynamic_loader_scheme) * *out_scheme_service,
|
|
scheme_service_map &scheme_services) {
|
|
/* Find scheme prefix. */
|
|
size_t scheme_end = urn.find("://");
|
|
if (scheme_end == my_string::npos) {
|
|
mysql_error_service_printf(ER_COMPONENTS_NO_SCHEME, MYF(0), urn.c_str());
|
|
return true;
|
|
}
|
|
my_string scheme(urn.begin(), urn.begin() + scheme_end, urn.get_allocator());
|
|
|
|
/* Look for scheme loading service in cache. */
|
|
scheme_service_map::iterator scheme_it = scheme_services.find(scheme);
|
|
if (scheme_it != scheme_services.end()) {
|
|
*out_scheme_service = scheme_it->second;
|
|
} else {
|
|
/* If not present, acquire from registry service and insert to cache. */
|
|
my_service<SERVICE_TYPE(dynamic_loader_scheme)> service(
|
|
(my_string("dynamic_loader_scheme_") + scheme).c_str(),
|
|
&imp_mysql_server_registry);
|
|
|
|
if (service) {
|
|
mysql_error_service_printf(ER_COMPONENTS_NO_SCHEME_SERVICE, MYF(0),
|
|
scheme.c_str(), urn.c_str());
|
|
return true;
|
|
}
|
|
*out_scheme_service = service;
|
|
scheme_services.insert(make_pair(scheme, std::move(service)));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* static members for mysql_dynamic_loader_imp */
|
|
my_component_registry mysql_dynamic_loader_imp::components_list;
|
|
mysql_rwlock_t mysql_dynamic_loader_imp::LOCK_dynamic_loader;
|
|
|
|
/* Following code initialize and deinitialize Service Implementations by
|
|
managing RW locks and their PSI augmentation. */
|
|
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
static PSI_rwlock_info all_dynamic_loader_rwlocks[] = {
|
|
{&key_rwlock_LOCK_dynamic_loader, "LOCK_dynamic_loader", PSI_FLAG_SINGLETON,
|
|
0, PSI_DOCUMENT_ME}};
|
|
|
|
static void init_dynamic_loader_psi_keys(void) {
|
|
const char *category = "components";
|
|
int count;
|
|
|
|
count = static_cast<int>(array_elements(all_dynamic_loader_rwlocks));
|
|
mysql_rwlock_register(category, all_dynamic_loader_rwlocks, count);
|
|
}
|
|
#endif
|
|
|
|
void dynamic_loader_init() {
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
init_dynamic_loader_psi_keys();
|
|
#endif
|
|
mysql_dynamic_loader_imp::init();
|
|
imp_mysql_server_registry.acquire("mysql_runtime_error", &h_err_service);
|
|
mysql_service_mysql_runtime_error =
|
|
reinterpret_cast<SERVICE_TYPE(mysql_runtime_error) *>(h_err_service);
|
|
}
|
|
|
|
void dynamic_loader_deinit() {
|
|
mysql_dynamic_loader_imp::deinit();
|
|
imp_mysql_server_registry.release(h_err_service);
|
|
}
|