Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 538dd9b1 authored by Zach Johnson's avatar Zach Johnson
Browse files

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
parent 3990df54
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ cc_library {
    },
    srcs: [
        "stack_manager.cc",
        "module.cc",
        ":BluetoothCommonSources",
        ":BluetoothHalSources",
        ":BluetoothPacketSources",
@@ -221,11 +222,17 @@ cc_test {
        },
    },
    srcs: [
        "module_unittest.cc",
        ":BluetoothCommonTestSources",
        ":BluetoothHciTestSources",
        ":BluetoothL2capTestSources",
        ":BluetoothPacketTestSources",
    ],
    cflags: [
        "-Wno-unused-parameter",
        "-Wno-implicit-fallthrough",
        "-Wno-unused-result",
    ],
    generated_headers : [
        "BluetoothGeneratedPackets_h",
    ],

system/gd/module.cc

0 → 100644
+60 −0
Original line number Diff line number Diff line
/*
 * 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

system/gd/module.h

0 → 100644
+101 −0
Original line number Diff line number Diff line
/*
 * 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
+198 −0
Original line number Diff line number Diff line
/*
 * 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