File: dynamic_loader.cc

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (1438 lines) | stat: -rw-r--r-- 58,531 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
/* Copyright (c) 2016, 2025, Oracle and/or its affiliates.

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 designed to work 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 either included with
the program or referenced in the documentation.

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 <assert.h>
#include <mysql/components/minimal_chassis.h>
#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 <algorithm>  // std::find_if
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <vector>

// pfs headers
#include <mysql/components/services/mysql_rwlock.h>

#include "component_common.h"
#include "depth_first_search.h"  // depth_first_search
#include "dynamic_loader_imp.h"
#include "mysql_component_imp.h"
#include "registry_imp.h"
#include "scope_guard.h"  // create_scope_guard

/**
  @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. Plugins 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 minimal
  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:

  The minimal chassis library has three exposed apis those are:
  - minimal_chassis_init()
  - minimal_chassis_deinit()
  - minimal_chassis_services_refresh()
  (see @ref minimal_chassis.cc)

  Note: minimal chassis support the native implementation of rwlocks and
  trylocks and it doesn't support the prlock because the prlock native
  implementation depends on mysql server code.

  @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.

  The elements of the minimal chassis are implemented in
  components/libminchassis 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 (admittedly 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.
*/
extern REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error);

extern SERVICE_TYPE(registry) imp_mysql_minimal_chassis_registry;

struct my_h_component_iterator_imp {
  my_component_registry::const_iterator m_it;
  minimal_chassis::rwlock_scoped_lock m_lock;
};

struct my_h_component_metadata_iterator_imp {
  my_metadata::const_iterator m_it;
  minimal_chassis::rwlock_scoped_lock m_lock;
};

static PSI_rwlock_key key_rwlock_LOCK_dynamic_loader;

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);
}

/**
  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() {
  init_dynamic_loader_psi_keys();
  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. */
  {
    minimal_chassis::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
      generation group wise to prevent any problems with dependencies. This on
      the other side may lead to situation where one of Components will not
      unload properly for a perticular generation group, causing all other
      components of that group 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) {
      for (auto it = mysql_dynamic_loader_imp::urns_with_gen_list.begin();
           it != mysql_dynamic_loader_imp::urns_with_gen_list.end();) {
        /* If we pass (*it) vector, unload_do_unload_components() function
           will iterates and modify urns_with_gen_list's vector. And both
           iterator and modify will work on same vector, which leads to
           vector iterator invalidation. So, we need to make a copy
           of the vector and pass it to below function */
        auto components_to_unload = (*it);
        if (unload_do_topological_order(components_to_unload)) {
          /* since there is a error in the deinit function, report
             the error and clear the vector elements(i.e components in that
             group and then remove the component group node */
          it->clear();
          // removes the forward_list node
          mysql_dynamic_loader_imp::urns_with_gen_list.remove(*it);
        }
        /* Updating the iterator because unload_do_unload_components
          removes the mysql_dynamic_loader_imp::urns_with_gen_list node */
        it = mysql_dynamic_loader_imp::urns_with_gen_list.begin();
      }
    }
  }
  mysql_rwlock_destroy(&mysql_dynamic_loader_imp::LOCK_dynamic_loader);
}

/**
  De-initializes RW lock
*/
void mysql_dynamic_loader_imp::rw_lock_deinit() {
  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. */
    minimal_chassis::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 {
    minimal_chassis::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 = nullptr;

    /* This read lock on whole component registry will be held, until the
      iterator is released. */
    minimal_chassis::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;
}

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 = nullptr;
    *out_urn = nullptr;

    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.release();
  }
  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 == nullptr) {
        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([&registered_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.release();
  }
  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);
  });

  /* 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.release();
  }
  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 != nullptr) {
        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 != nullptr &&
        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.release();
  }
  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) {
  std::vector<mysql_component *> components_to_load;
  /* Move components to the main list of components loaded. */
  for (std::unique_ptr<mysql_component> &loaded_component : loaded_components) {
    components_to_load.emplace_back(loaded_component.get());
    mysql_dynamic_loader_imp::components_list.emplace(
        loaded_component->urn_c_str(), std::move(loaded_component));
  }
  mysql_dynamic_loader_imp::urns_with_gen_list.emplace_front(
      components_to_load);

  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);
  }

  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.
   */
  minimal_chassis::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 != nullptr) {
      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());

    auto component_it =
        mysql_dynamic_loader_imp::components_list.find(component_urn.c_str());

    /* Find mysql_component pointer from the urns_with_gen_list's vector
       elements, if found then remove it from the vector and after removing, if
       the vector doesn't have no elements then remove the vector node from the
       forward_list and in either case after element is removed, break the loop.
       Note: urns_with_gen_list will contain the unique vector elements of
       mysql_component pointers. */
    for (auto list = mysql_dynamic_loader_imp::urns_with_gen_list.begin();
         list != mysql_dynamic_loader_imp::urns_with_gen_list.end(); ++list) {
      auto comp_ele = (*component_it).second.get();
      auto v_element =
          find_if((*list).begin(), (*list).end(),
                  [comp_ele](mysql_component *ptr) { return ptr == comp_ele; });
      if (v_element != list->end()) {
        list->erase(v_element);
        if (list->size() == 0) {
          mysql_dynamic_loader_imp::urns_with_gen_list.remove(*list);
        }
        break;
      }
    }

    mysql_dynamic_loader_imp::components_list.erase(component_it);

    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_minimal_chassis_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;
generation_urns_list mysql_dynamic_loader_imp::urns_with_gen_list;