Apollo Cyber Study. (cyber/class_loader)

// Study: 是我的筆記

下邊是我把CYBER_REGISTER_COMPONENT真正會展開的東西放在一起

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
template <typename Derived, typename Base>
void RegisterClass(const std::string& class_name,
const std::string& base_class_name) {
// Study: GetCurLoadingLibraryName() is a singleton
// It value only changed in LoadLibrary in class_loader_utility
AINFO << "registerclass:" << class_name << "," << base_class_name << ","
<< GetCurLoadingLibraryName();

// Study: The only ClassFactory required the Derived Class (The modules real class)
// have a constructor with no argument
utility::AbstractClassFactory<Base>* new_class_factrory_obj =
new utility::ClassFactory<Derived, Base>(class_name, base_class_name);
new_class_factrory_obj->AddOwnedClassLoader(GetCurActiveClassLoader());
new_class_factrory_obj->SetRelativeLibraryPath(GetCurLoadingLibraryName());

GetClassFactoryMapMapMutex().lock();
ClassClassFactoryMap& factory_map =
GetClassFactoryMapByBaseClass(typeid(Base).name());
factory_map[class_name] = new_class_factrory_obj;
GetClassFactoryMapMapMutex().unlock();
}

// Study: Using a proxy class constructor and it static instantiate
// to call RegisterClass once the shared object that have called CYBER_REGISTER_COMPONENT
// have been loaded
#define CLASS_LOADER_REGISTER_CLASS_INTERNAL(Derived, Base, UniqueID) \
namespace { \
struct ProxyType##UniqueID { \
ProxyType##UniqueID() { \
apollo::cyber::class_loader::utility::RegisterClass<Derived, Base>( \
#Derived, #Base); \
} \
}; \
static ProxyType##UniqueID g_register_class_##UniqueID; \
}

#define CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, UniqueID) \
CLASS_LOADER_REGISTER_CLASS_INTERNAL(Derived, Base, UniqueID)

// Study: Assign a unique id to each registered component, avoid name collision
// register class macro
#define CLASS_LOADER_REGISTER_CLASS(Derived, Base) \
CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, __COUNTER__)

// Study: This is the macro that used in modules
// All modules is subclass of apollo::cyber::ComponentBase
#define CYBER_REGISTER_COMPONENT(name) \
CLASS_LOADER_REGISTER_CLASS(name, apollo::cyber::ComponentBase)

cyber/class_loader/utility/class_loader_utility

雖然名字叫utility, 但真正的class loading 過程都在這
cc文件中的實現沒有特別,就不說了

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
/******************************************************************************
* Copyright 2018 The Apollo Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/

#ifndef CYBER_CLASS_LOADER_CLASS_LOADER_UTILITY_H_
#define CYBER_CLASS_LOADER_CLASS_LOADER_UTILITY_H_

#include <Poco/SharedLibrary.h>
#include <cassert>
#include <cstdio>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <typeinfo>
#include <utility>
#include <vector>

#include "cyber/class_loader/utility/class_factory.h"
#include "cyber/common/log.h"

/**
* class register implement
*/
namespace apollo {
namespace cyber {
namespace class_loader {

class ClassLoader;

namespace utility {

// Study: 用了Poco庫去加載shared library
// https://vovkos.github.io/doxyrest-showcase/poco/sphinxdoc/class_Poco_SharedLibrary.html#details-doxid-class-poco-1-1-shared-library
using PocoLibraryPtr = std::shared_ptr<Poco::SharedLibrary>;
// Study: 可怕的命名, 雖然合理, 但可怕
using ClassClassFactoryMap =
std::map<std::string, utility::AbstractClassFactoryBase*>;
using BaseToClassFactoryMapMap = std::map<std::string, ClassClassFactoryMap>;
using LibpathPocolibVector =
std::vector<std::pair<std::string, PocoLibraryPtr>>;
using ClassFactoryVector = std::vector<AbstractClassFactoryBase*>;

// Study:  Singletion and it getter, mutex, etc
// This act as a global states
BaseToClassFactoryMapMap& GetClassFactoryMapMap();
std::recursive_mutex& GetClassFactoryMapMapMutex();
LibpathPocolibVector& GetLibPathPocoShareLibVector();
std::recursive_mutex& GetLibPathPocoShareLibMutex();
ClassClassFactoryMap& GetClassFactoryMapByBaseClass(
const std::string& typeid_base_class_name);
std::string GetCurLoadingLibraryName();
void SetCurLoadingLibraryName(const std::string& library_name);
ClassLoader* GetCurActiveClassLoader();
void SetCurActiveClassLoader(ClassLoader* loader);

// Study: When call LoadLibrary, need provide a ClassLoader.
// Avoid load same library multiple time for different class loader
bool IsLibraryLoaded(const std::string& library_path, ClassLoader* loader);
bool IsLibraryLoadedByAnybody(const std::string& library_path);
// Study: The Core function
bool LoadLibrary(const std::string& library_path, ClassLoader* loader);
void UnloadLibrary(const std::string& library_path, ClassLoader* loader);

template <typename Derived, typename Base>
void RegisterClass(const std::string& class_name,
const std::string& base_class_name);
template <typename Base>
Base* CreateClassObj(const std::string& class_name, ClassLoader* loader);
template <typename Base>
std::vector<std::string> GetValidClassNames(ClassLoader* loader);

// Study: Put a class factory to global map
template <typename Derived, typename Base>
void RegisterClass(const std::string& class_name,
const std::string& base_class_name) {
AINFO << "registerclass:" << class_name << "," << base_class_name << ","
<< GetCurLoadingLibraryName();

utility::AbstractClassFactory<Base>* new_class_factrory_obj =
new utility::ClassFactory<Derived, Base>(class_name, base_class_name);
new_class_factrory_obj->AddOwnedClassLoader(GetCurActiveClassLoader());
new_class_factrory_obj->SetRelativeLibraryPath(GetCurLoadingLibraryName());

GetClassFactoryMapMapMutex().lock();
ClassClassFactoryMap& factory_map =
GetClassFactoryMapByBaseClass(typeid(Base).name());
factory_map[class_name] = new_class_factrory_obj;
GetClassFactoryMapMapMutex().unlock();
}

// Study: Using the loaded class factory to create a object with no argument
// using the class loader
template <typename Base>
Base* CreateClassObj(const std::string& class_name, ClassLoader* loader) {
GetClassFactoryMapMapMutex().lock();
ClassClassFactoryMap& factoryMap =
GetClassFactoryMapByBaseClass(typeid(Base).name());
AbstractClassFactory<Base>* factory = nullptr;
if (factoryMap.find(class_name) != factoryMap.end()) {
factory = dynamic_cast<utility::AbstractClassFactory<Base>*>(
factoryMap[class_name]);
}
GetClassFactoryMapMapMutex().unlock();

Base* classobj = nullptr;
if (factory && factory->IsOwnedBy(loader)) {
classobj = factory->CreateObj();
}

return classobj;
}

// Study: What class can the class loader load
template <typename Base>
std::vector<std::string> GetValidClassNames(ClassLoader* loader) {
std::lock_guard<std::recursive_mutex> lck(GetClassFactoryMapMapMutex());

ClassClassFactoryMap& factoryMap =
GetClassFactoryMapByBaseClass(typeid(Base).name());
std::vector<std::string> classes;
for (auto& class_factory : factoryMap) {
AbstractClassFactoryBase* factory = class_factory.second;
if (factory && factory->IsOwnedBy(loader)) {
classes.emplace_back(class_factory.first);
}
}

return classes;
}

} // End namespace utility
} // End namespace class_loader
} // namespace cyber
} // namespace apollo
#endif // CYBER_CLASS_LOADER_CLASS_LOADER_UTILITY_H_

cyber/class_loader/class_loader

The core function is already implemented in class loader utility.
The class loader is used to provided a higher abstract level of feature
Moreover, it provide the reference counting to object and library.
Dynamically determined the real time to unload a library

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
/******************************************************************************
* Copyright 2018 The Apollo Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/
#ifndef CYBER_CLASS_LOADER_CLASS_LOADER_H_
#define CYBER_CLASS_LOADER_CLASS_LOADER_H_

#include <algorithm>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include "cyber/class_loader/class_loader_register_macro.h"

namespace apollo {
namespace cyber {
namespace class_loader {

/**
* for library load,createclass object
*/
class ClassLoader {
public:
explicit ClassLoader(const std::string& library_path);
// Study: Although it is virtual, but there are no derived class, so ignore the virtual
virtual ~ClassLoader();

bool IsLibraryLoaded();
// Study: Core function
bool LoadLibrary();
int UnloadLibrary();

const std::string GetLibraryPath() const;
template <typename Base>
std::vector<std::string> GetValidClassNames();
template <typename Base>
std::shared_ptr<Base> CreateClassObj(const std::string& class_name);
template <typename Base>
bool IsClassValid(const std::string& class_name);

private:
template <typename Base>
void OnClassObjDeleter(Base* obj);

private:
std::string library_path_;
int loadlib_ref_count_;
std::mutex loadlib_ref_count_mutex_;
int classobj_ref_count_;
std::mutex classobj_ref_count_mutex_;
};

template <typename Base>
std::vector<std::string> ClassLoader::GetValidClassNames() {
return (utility::GetValidClassNames<Base>(this));
}

template <typename Base>
bool ClassLoader::IsClassValid(const std::string& class_name) {
std::vector<std::string> valid_classes = GetValidClassNames<Base>();
return (std::find(valid_classes.begin(), valid_classes.end(), class_name) !=
valid_classes.end());
}

// Study: Create class object, must return a shared pointer or
// a wrapper to the real object, otherwise it cannot do the
// reference counting
template <typename Base>
std::shared_ptr<Base> ClassLoader::CreateClassObj(
const std::string& class_name) {
if (!IsLibraryLoaded()) {
LoadLibrary();
}

Base* class_object = utility::CreateClassObj<Base>(class_name, this);
if (nullptr == class_object) {
AWARN << "CreateClassObj failed, ensure class has been registered. "
<< "classname: " << class_name << ",lib: " << GetLibraryPath();
return std::shared_ptr<Base>();
}

std::lock_guard<std::mutex> lck(classobj_ref_count_mutex_);
classobj_ref_count_ = classobj_ref_count_ + 1;
std::shared_ptr<Base> classObjSharePtr(
class_object, std::bind(&ClassLoader::OnClassObjDeleter<Base>, this,
std::placeholders::_1));
return classObjSharePtr;
}

template <typename Base>
void ClassLoader::OnClassObjDeleter(Base* obj) {
if (nullptr == obj) {
return;
}

std::lock_guard<std::mutex> lck(classobj_ref_count_mutex_);
delete obj;
classobj_ref_count_ = classobj_ref_count_ - 1;
}

// Study: Auto load on create
ClassLoader::ClassLoader(const std::string& library_path)
: library_path_(library_path),
loadlib_ref_count_(0),
classobj_ref_count_(0) {
LoadLibrary();
}

ClassLoader::~ClassLoader() { UnloadLibrary(); }

bool ClassLoader::IsLibraryLoaded() {
return utility::IsLibraryLoaded(library_path_, this);
}

bool ClassLoader::LoadLibrary() {
std::lock_guard<std::mutex> lck(loadlib_ref_count_mutex_);
loadlib_ref_count_ = loadlib_ref_count_ + 1;
AINFO << "Begin LoadLibrary: " << library_path_;
return utility::LoadLibrary(library_path_, this);
}

// Study: Only unload library after not more reference
int ClassLoader::UnloadLibrary() {
std::lock_guard<std::mutex> lckLib(loadlib_ref_count_mutex_);
std::lock_guard<std::mutex> lckObj(classobj_ref_count_mutex_);

if (classobj_ref_count_ > 0) {
AINFO << "There are still classobjs have not been deleted, "
"classobj_ref_count_: "
<< classobj_ref_count_;
} else {
loadlib_ref_count_ = loadlib_ref_count_ - 1;
if (loadlib_ref_count_ == 0) {
utility::UnloadLibrary(library_path_, this);
} else {
if (loadlib_ref_count_ < 0) {
loadlib_ref_count_ = 0;
}
}
}
return loadlib_ref_count_;
}

const std::string ClassLoader::GetLibraryPath() const { return library_path_; }

} // namespace class_loader
} // namespace cyber
} // namespace apollo
#endif // CYBER_CLASS_LOADER_CLASS_LOADER_H_

cyber/class_loader/class_loader_manager

對class loader的再高一層次抽象, 如果直接用class loader
那每要加一個library就要多一個class loader.
在要unload library時就會出現不知道用那個class loader的問題,
所以就要一個manager去管理了.