blob: f71fdb8ee8b5933db72a1f4cb8fd49a941d1ada0 [file] [log] [blame]
/*
* 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 <chrono>
#include <functional>
#include <future>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "common/bind.h"
#include "module_state_dumper.h"
#include "os/handler.h"
#include "os/log.h"
#include "os/thread.h"
namespace bluetooth {
class Module;
class ModuleDumper;
class ModuleRegistry;
class TestModuleRegistry;
class FuzzTestModuleRegistry;
class ModuleFactory {
friend ModuleRegistry;
friend FuzzTestModuleRegistry;
public:
ModuleFactory(std::function<Module*()> ctor);
private:
std::function<Module*()> ctor_;
};
class ModuleList {
friend Module;
friend ModuleRegistry;
public:
template <class T>
void add() {
list_.push_back(&T::Factory);
}
// Return the number of modules in this list
size_t NumModules() const {
return list_.size();
}
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 : protected ModuleStateDumper {
friend ModuleDumper;
friend ModuleRegistry;
friend TestModuleRegistry;
public:
virtual ~Module() = default;
protected:
// Populate the provided list with modules that must start before yours
virtual void ListDependencies(ModuleList* list) const = 0;
// You can grab your started dependencies during or after this call
// using GetDependency(), or access the module registry via GetModuleRegistry()
virtual void Start() = 0;
// Release all resources, you're about to be deleted
virtual void Stop() = 0;
virtual std::string ToString() const = 0;
::bluetooth::os::Handler* GetHandler() const;
const ModuleRegistry* GetModuleRegistry() const;
template <class T>
T* GetDependency() const {
return static_cast<T*>(GetDependency(&T::Factory));
}
template <typename Functor, typename... Args>
void Call(Functor&& functor, Args&&... args) {
GetHandler()->Call(std::forward<Functor>(functor), std::forward<Args>(args)...);
}
template <typename T, typename Functor, typename... Args>
void CallOn(T* obj, Functor&& functor, Args&&... args) {
GetHandler()->CallOn(obj, std::forward<Functor>(functor), std::forward<Args>(args)...);
}
private:
Module* GetDependency(const ModuleFactory* module) const;
::bluetooth::os::Handler* handler_ = nullptr;
ModuleList dependencies_;
const ModuleRegistry* registry_;
};
class ModuleRegistry {
friend Module;
friend ModuleDumper;
friend class StackManager;
public:
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, ::bluetooth::os::Thread* thread);
template <class T>
T* Start(::bluetooth::os::Thread* thread) {
return static_cast<T*>(Start(&T::Factory, thread));
}
Module* Start(const ModuleFactory* id, ::bluetooth::os::Thread* thread);
// Stop all running modules in reverse order of start
void StopAll();
protected:
Module* Get(const ModuleFactory* module) const;
void set_registry_and_handler(Module* instance, ::bluetooth::os::Thread* thread) const;
os::Handler* GetModuleHandler(const ModuleFactory* module) const;
std::map<const ModuleFactory*, Module*> started_modules_;
std::vector<const ModuleFactory*> start_order_;
std::string last_instance_;
};
class TestModuleRegistry : public ModuleRegistry {
public:
void InjectTestModule(const ModuleFactory* module, Module* instance) {
start_order_.push_back(module);
started_modules_[module] = instance;
set_registry_and_handler(instance, &test_thread);
instance->Start();
}
Module* GetModuleUnderTest(const ModuleFactory* module) const {
return Get(module);
}
template <class T>
T* GetModuleUnderTest() const {
return static_cast<T*>(GetModuleUnderTest(&T::Factory));
}
os::Handler* GetTestModuleHandler(const ModuleFactory* module) const {
return GetModuleHandler(module);
}
os::Thread& GetTestThread() {
return test_thread;
}
bool SynchronizeModuleHandler(const ModuleFactory* module, std::chrono::milliseconds timeout) const {
return SynchronizeHandler(GetTestModuleHandler(module), timeout);
}
bool SynchronizeHandler(os::Handler* handler, std::chrono::milliseconds timeout) const {
std::promise<void> promise;
auto future = promise.get_future();
handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
return future.wait_for(timeout) == std::future_status::ready;
}
private:
os::Thread test_thread{"test_thread", os::Thread::Priority::NORMAL};
};
class FuzzTestModuleRegistry : public TestModuleRegistry {
public:
template <class T>
T* Inject(const ModuleFactory* overriding) {
Module* instance = T::Factory.ctor_();
InjectTestModule(overriding, instance);
return static_cast<T*>(instance);
}
template <class T>
T* Start() {
return ModuleRegistry::Start<T>(&GetTestThread());
}
void WaitForIdleAndStopAll() {
if (!GetTestThread().GetReactor()->WaitForIdle(std::chrono::milliseconds(100))) {
LOG_ERROR("idle timed out");
}
StopAll();
}
};
} // namespace bluetooth