Add basic concept of module and module registry + dependency graph
Modules have protected lifecycle functions.
Modules are identified by a factory object that knows how to construct them.
Modules can indicate which dependencies they have.
Dependencies are started before their dependent modules.
Don't support incremental module stop for the moment - it's all or nothing.
Test: atest --host bluetooth_test_gd:ModuleTest
Change-Id: I026b9e893501506a6cea8387941b3a9424a9bec9
diff --git a/gd/Android.bp b/gd/Android.bp
index 1bbb95e..572eebc 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -99,6 +99,7 @@
},
srcs: [
"stack_manager.cc",
+ "module.cc",
":BluetoothCommonSources",
":BluetoothHalSources",
":BluetoothPacketSources",
@@ -221,11 +222,17 @@
},
},
srcs: [
+ "module_unittest.cc",
":BluetoothCommonTestSources",
":BluetoothHciTestSources",
":BluetoothL2capTestSources",
":BluetoothPacketTestSources",
],
+ cflags: [
+ "-Wno-unused-parameter",
+ "-Wno-implicit-fallthrough",
+ "-Wno-unused-result",
+ ],
generated_headers : [
"BluetoothGeneratedPackets_h",
],
diff --git a/gd/module.cc b/gd/module.cc
new file mode 100644
index 0000000..95f4d10
--- /dev/null
+++ b/gd/module.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "module.h"
+
+namespace bluetooth {
+
+ModuleFactory::ModuleFactory(std::function<Module*()> ctor) : ctor_(ctor) {
+}
+
+bool ModuleRegistry::IsStarted(const ModuleFactory* factory) const {
+ return started_modules_.find(factory) != started_modules_.end();
+}
+
+void ModuleRegistry::Start(ModuleList* modules) {
+ for (auto it = modules->list_.begin(); it != modules->list_.end(); it++) {
+ if (IsStarted(*it)) {
+ continue;
+ }
+
+ Module* instance = (*it)->ctor_();
+ ModuleList dependencies;
+ instance->ListDependencies(&dependencies);
+ Start(&dependencies);
+
+ instance->Start(this);
+ start_order_.push_back(*it);
+ started_modules_[*it] = instance;
+ }
+}
+
+void ModuleRegistry::StopAll() {
+ // Since modules were brought up in dependency order,
+ // it is safe to tear down by going in reverse order.
+ for (auto it = start_order_.rbegin(); it != start_order_.rend(); it++) {
+ auto instance = started_modules_.find(*it);
+ ASSERT(instance != started_modules_.end());
+ instance->second->Stop(this);
+
+ delete instance->second;
+ started_modules_.erase(instance);
+ }
+
+ ASSERT(started_modules_.empty());
+ start_order_.clear();
+}
+} // namespace bluetooth
diff --git a/gd/module.h b/gd/module.h
new file mode 100644
index 0000000..2e78bca
--- /dev/null
+++ b/gd/module.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <vector>
+#include <map>
+
+#include "os/log.h"
+
+namespace bluetooth {
+
+class Module;
+class ModuleRegistry;
+
+class ModuleFactory {
+ friend ModuleRegistry;
+ public:
+ ModuleFactory(std::function<Module*()> ctor);
+
+ private:
+ std::function<Module*()> ctor_;
+};
+
+class ModuleList {
+ friend ModuleRegistry;
+ public:
+ template <class T>
+ void add() {
+ list_.push_back(&T::Factory);
+ }
+
+ private:
+ std::vector<const ModuleFactory*> list_;
+};
+
+// Each leaf node module must have a factory like so:
+//
+// static const ModuleFactory Factory;
+//
+// which will provide a constructor for the module registry to call.
+// The module registry will also use the Factory as the identifier
+// for that module.
+class Module {
+ friend ModuleRegistry;
+ public:
+ virtual ~Module() = default;
+ protected:
+ // Populate the provided list with modules that must start before yours
+ virtual void ListDependencies(ModuleList* list) = 0;
+
+ // You can grab your started dependencies from the registry in this call
+ virtual void Start(const ModuleRegistry* registry) = 0;
+
+ // Release all resources, you're about to be deleted
+ virtual void Stop(const ModuleRegistry* registry) = 0;
+};
+
+class ModuleRegistry {
+ public:
+ template <class T>
+ T* GetInstance() const {
+ auto instance = started_modules_.find(&T::Factory);
+ ASSERT(instance != started_modules_.end());
+ return static_cast<T *>(instance->second);
+ };
+
+ template <class T>
+ bool IsStarted() const {
+ return IsStarted(&T::Factory);
+ }
+
+ bool IsStarted(const ModuleFactory* factory) const;
+
+ // Start all the modules on this list and their dependencies
+ // in dependency order
+ void Start(ModuleList* modules);
+
+ // Stop all running modules in reverse order of start
+ void StopAll();
+
+ private:
+ std::map<const ModuleFactory*, Module*> started_modules_;
+ std::vector<const ModuleFactory*> start_order_;
+};
+
+} // namespace bluetooth
diff --git a/gd/module_unittest.cc b/gd/module_unittest.cc
new file mode 100644
index 0000000..62e8966
--- /dev/null
+++ b/gd/module_unittest.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "module.h"
+
+#include "gtest/gtest.h"
+
+namespace bluetooth {
+namespace {
+
+class ModuleTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ registry_ = new ModuleRegistry();
+ }
+
+ void TearDown() override {
+ delete registry_;
+ }
+
+ ModuleRegistry* registry_;
+};
+
+class TestModuleNoDependency : public Module {
+ public:
+ static const ModuleFactory Factory;
+
+ protected:
+ void ListDependencies(ModuleList* list) override {
+ }
+
+ void Start(const ModuleRegistry* registry) override {
+ // A module is not considered started until Start() finishes
+ EXPECT_FALSE(registry->IsStarted<TestModuleNoDependency>());
+ }
+
+ void Stop(const ModuleRegistry* registry) override {
+ // A module is not considered stopped until after Stop() finishes
+ EXPECT_TRUE(registry->IsStarted<TestModuleNoDependency>());
+ }
+};
+
+const ModuleFactory TestModuleNoDependency::Factory = ModuleFactory([]() {
+ return new TestModuleNoDependency();
+});
+
+class TestModuleOneDependency : public Module {
+ public:
+ static const ModuleFactory Factory;
+
+ protected:
+ void ListDependencies(ModuleList* list) override {
+ list->add<TestModuleNoDependency>();
+ }
+
+ void Start(const ModuleRegistry* registry) override {
+ EXPECT_TRUE(registry->IsStarted<TestModuleNoDependency>());
+
+ // A module is not considered started until Start() finishes
+ EXPECT_FALSE(registry->IsStarted<TestModuleOneDependency>());
+ }
+
+ void Stop(const ModuleRegistry* registry) override {
+ EXPECT_TRUE(registry->IsStarted<TestModuleNoDependency>());
+
+ // A module is not considered stopped until after Stop() finishes
+ EXPECT_TRUE(registry->IsStarted<TestModuleOneDependency>());
+ }
+};
+
+const ModuleFactory TestModuleOneDependency::Factory = ModuleFactory([]() {
+ return new TestModuleOneDependency();
+});
+
+
+class TestModuleNoDependencyTwo : public Module {
+ public:
+ static const ModuleFactory Factory;
+
+ protected:
+ void ListDependencies(ModuleList* list) override {
+ }
+
+ void Start(const ModuleRegistry* registry) override {
+ // A module is not considered started until Start() finishes
+ EXPECT_FALSE(registry->IsStarted<TestModuleNoDependencyTwo>());
+ }
+
+ void Stop(const ModuleRegistry* registry) override {
+ // A module is not considered stopped until after Stop() finishes
+ EXPECT_TRUE(registry->IsStarted<TestModuleNoDependencyTwo>());
+ }
+};
+
+const ModuleFactory TestModuleNoDependencyTwo::Factory = ModuleFactory([]() {
+ return new TestModuleNoDependencyTwo();
+});
+
+class TestModuleTwoDependencies : public Module {
+ public:
+ static const ModuleFactory Factory;
+
+ protected:
+ void ListDependencies(ModuleList* list) override {
+ list->add<TestModuleOneDependency>();
+ list->add<TestModuleNoDependencyTwo>();
+ }
+
+ void Start(const ModuleRegistry* registry) override {
+ EXPECT_TRUE(registry->IsStarted<TestModuleOneDependency>());
+ EXPECT_TRUE(registry->IsStarted<TestModuleNoDependencyTwo>());
+
+ // A module is not considered started until Start() finishes
+ EXPECT_FALSE(registry->IsStarted<TestModuleTwoDependencies>());
+ }
+
+ void Stop(const ModuleRegistry* registry) override {
+ EXPECT_TRUE(registry->IsStarted<TestModuleOneDependency>());
+ EXPECT_TRUE(registry->IsStarted<TestModuleNoDependencyTwo>());
+
+ // A module is not considered stopped until after Stop() finishes
+ EXPECT_TRUE(registry->IsStarted<TestModuleTwoDependencies>());
+ }
+};
+
+const ModuleFactory TestModuleTwoDependencies::Factory = ModuleFactory([]() {
+ return new TestModuleTwoDependencies();
+});
+
+TEST_F(ModuleTest, no_dependency) {
+ ModuleList list;
+ list.add<TestModuleNoDependency>();
+ registry_->Start(&list);
+
+ EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+ registry_->StopAll();
+
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+TEST_F(ModuleTest, one_dependency) {
+ ModuleList list;
+ list.add<TestModuleOneDependency>();
+ registry_->Start(&list);
+
+ EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+ EXPECT_TRUE(registry_->IsStarted<TestModuleOneDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+ registry_->StopAll();
+
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+TEST_F(ModuleTest, two_dependencies) {
+ ModuleList list;
+ list.add<TestModuleTwoDependencies>();
+ registry_->Start(&list);
+
+ EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependency>());
+ EXPECT_TRUE(registry_->IsStarted<TestModuleOneDependency>());
+ EXPECT_TRUE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+ EXPECT_TRUE(registry_->IsStarted<TestModuleTwoDependencies>());
+
+ registry_->StopAll();
+
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleOneDependency>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleNoDependencyTwo>());
+ EXPECT_FALSE(registry_->IsStarted<TestModuleTwoDependencies>());
+}
+
+} // namespace
+} // namespace bluetooth