From 4ae2e025c9d2b14e94eccb4117f1930ba38dcaa4 Mon Sep 17 00:00:00 2001 From: Dennis Klein Date: Thu, 28 Sep 2017 23:27:22 +0200 Subject: [PATCH] FairMQ: Implement DeviceRunner --- fairmq/CMakeLists.txt | 3 +- fairmq/DeviceRunner.cxx | 107 +++++++++++++++++++++++++++ fairmq/DeviceRunner.h | 84 +++++++++++++++++++++ fairmq/options/FairMQProgOptions.cxx | 12 +++ fairmq/options/FairMQProgOptions.h | 1 + fairmq/runFairMQDevice.h | 101 +++++++------------------ 6 files changed, 231 insertions(+), 77 deletions(-) create mode 100644 fairmq/DeviceRunner.cxx create mode 100644 fairmq/DeviceRunner.h diff --git a/fairmq/CMakeLists.txt b/fairmq/CMakeLists.txt index 5d8dd3c6..d6074d93 100644 --- a/fairmq/CMakeLists.txt +++ b/fairmq/CMakeLists.txt @@ -63,6 +63,7 @@ set(FAIRMQ_DEPRECATED_HEADER_FILES ) set(FAIRMQ_HEADER_FILES ${FAIRMQ_DEPRECATED_HEADER_FILES} + DeviceRunner.h EventManager.h FairMQChannel.h FairMQDevice.h @@ -83,7 +84,6 @@ set(FAIRMQ_HEADER_FILES devices/FairMQSink.h devices/FairMQSplitter.h logger/logger.h - options/FairMQEventManager.h options/FairMQParser.h options/FairMQProgOptions.h options/FairMQSuboptParser.h @@ -133,6 +133,7 @@ endif() set(FAIRMQ_SOURCE_FILES + DeviceRunner.cxx FairMQChannel.cxx FairMQDevice.cxx FairMQLogger.cxx diff --git a/fairmq/DeviceRunner.cxx b/fairmq/DeviceRunner.cxx new file mode 100644 index 00000000..71c955d2 --- /dev/null +++ b/fairmq/DeviceRunner.cxx @@ -0,0 +1,107 @@ +/******************************************************************************** + * Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#include "DeviceRunner.h" +#include +#include + +using namespace fair::mq; + +DeviceRunner::DeviceRunner(int argc, char const* argv[]) +: fRawCmdLineArgs{tools::ToStrVector(argc, argv, false)} +, fPluginManager{PluginManager::MakeFromCommandLineOptions(fRawCmdLineArgs)} +, fDevice{nullptr} +{ +} + +auto DeviceRunner::Run() -> int +{ + ////// CALL HOOK /////// + fEvents.Emit(*this); + //////////////////////// + + // Load builtin plugins last + fPluginManager->LoadPlugin("s:control"); + + ////// CALL HOOK /////// + fEvents.Emit(*this); + //////////////////////// + + fPluginManager->ForEachPluginProgOptions([&](boost::program_options::options_description options){ + fConfig.AddToCmdLineOptions(options); + }); + fConfig.AddToCmdLineOptions(fPluginManager->ProgramOptions()); + + ////// CALL HOOK /////// + fEvents.Emit(*this); + //////////////////////// + + fConfig.ParseAll(fRawCmdLineArgs, true); + + ////// CALL HOOK /////// + fEvents.Emit(*this); + //////////////////////// + + if (!fDevice) + { + LOG(ERROR) << "getDevice(): no valid device provided. Exiting."; + return 1; + } + + // Handle --print-channels + fDevice->RegisterChannelEndpoints(); + if (fConfig.Count("print-channels")) + { + fDevice->PrintRegisteredChannels(); + fDevice->ChangeState(FairMQDevice::END); + return 0; + } + + // Handle --version + if (fConfig.Count("version")) + { + std::cout << "User device version: " << fDevice->GetVersion() << std::endl; + std::cout << "FAIRMQ_INTERFACE_VERSION: " << FAIRMQ_INTERFACE_VERSION << std::endl; + fDevice->ChangeState(FairMQDevice::END); + return 0; + } + + LOG(DEBUG) << "PID: " << getpid(); + + // Configure device + fDevice->SetConfig(fConfig); + + // Initialize plugin services + fPluginManager->EmplacePluginServices(&fConfig, fDevice); + + // Instantiate and run plugins + fPluginManager->InstantiatePlugins(); + + // Wait for control plugin to release device control + fPluginManager->WaitForPluginsToReleaseDeviceControl(); + + return 0; +} + +auto DeviceRunner::RunWithExceptionHandlers() -> int +{ + try + { + return Run(); + } + catch (std::exception& e) + { + LOG(ERROR) << "Unhandled exception reached the top of main: " << e.what() << ", application will now exit"; + return 1; + } + catch (...) + { + LOG(ERROR) << "Non-exception instance being thrown. Please make sure you use std::runtime_exception() instead. Application will now exit."; + return 1; + } +} diff --git a/fairmq/DeviceRunner.h b/fairmq/DeviceRunner.h new file mode 100644 index 00000000..6e4bcdf8 --- /dev/null +++ b/fairmq/DeviceRunner.h @@ -0,0 +1,84 @@ +/******************************************************************************** + * Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#ifndef FAIR_MQ_DEVICERUNNER_H +#define FAIR_MQ_DEVICERUNNER_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace fair +{ +namespace mq +{ + +/** + * @class DeviceRunner DeviceRunner.h + * @brief Utility class to facilitate a convenient top-level device launch/shutdown. + * + * Runs a single FairMQ device with config and plugin support. + * + * For customization user hooks are executed at various steps during device launch/shutdown in the following sequence: + * + * LoadPlugins + * | + * v + * SetCustomCmdLineOptions + * | + * v + * ModifyRawCmdLineArgs + * | + * v + * InstatiateDevice + * + * Each hook has access to all members of the DeviceRunner and really only differs by the point in time it is called. + * + * For an example usage of this class see the fairmq/runFairMQDevice.h header. + */ +class DeviceRunner +{ + public: + DeviceRunner(int argc, char const* argv[]); + + auto Run() -> int; + auto RunWithExceptionHandlers() -> int; + + template + auto AddHook(std::function hook) -> void { fEvents.Subscribe("runner", hook); } + template + auto RemoveHook() -> void { fEvents.Unsubscribe("runner"); } + + std::vector fRawCmdLineArgs; + std::shared_ptr fPluginManager; + FairMQProgOptions fConfig; + std::shared_ptr fDevice; + + private: + EventManager fEvents; +}; + +namespace hooks +{ +struct LoadPlugins : Event {}; +struct SetCustomCmdLineOptions : Event {}; +struct ModifyRawCmdLineArgs : Event {}; +struct InstantiateDevice : Event {}; +} /* namespace hooks */ + +} /* namespace mq */ +} /* namespace fair */ + +#endif /* FAIR_MQ_DEVICERUNNER_H */ diff --git a/fairmq/options/FairMQProgOptions.cxx b/fairmq/options/FairMQProgOptions.cxx index 8823bb3d..9272aa51 100644 --- a/fairmq/options/FairMQProgOptions.cxx +++ b/fairmq/options/FairMQProgOptions.cxx @@ -39,6 +39,18 @@ FairMQProgOptions::~FairMQProgOptions() { } +void FairMQProgOptions::ParseAll(const std::vector& cmdLineArgs, bool allowUnregistered) +{ + std::vector argv(cmdLineArgs.size()); + + std::transform(cmdLineArgs.begin(), cmdLineArgs.end(), argv.begin(), [](const std::string& str) + { + return str.c_str(); + }); + + ParseAll(argv.size(), const_cast(argv.data()), allowUnregistered); +} + void FairMQProgOptions::ParseAll(const int argc, char const* const* argv, bool allowUnregistered) { // init description diff --git a/fairmq/options/FairMQProgOptions.h b/fairmq/options/FairMQProgOptions.h index cb90bab8..90d985cf 100644 --- a/fairmq/options/FairMQProgOptions.h +++ b/fairmq/options/FairMQProgOptions.h @@ -49,6 +49,7 @@ class FairMQProgOptions : public FairProgOptions FairMQProgOptions(); virtual ~FairMQProgOptions(); + void ParseAll(const std::vector& cmdLineArgs, bool allowUnregistered); // parse command line and txt/INI configuration file. // default parser for the mq-configuration file (JSON/XML) is called if command line key mq-config is called virtual void ParseAll(const int argc, char const* const* argv, bool allowUnregistered = false); diff --git a/fairmq/runFairMQDevice.h b/fairmq/runFairMQDevice.h index 897e319b..3e161875 100644 --- a/fairmq/runFairMQDevice.h +++ b/fairmq/runFairMQDevice.h @@ -6,15 +6,10 @@ * copied verbatim in the file "LICENSE" * ********************************************************************************/ -#include -#include -#include -#include -#include +#include #include #include #include -#include template class GenericFairMQDevice : public FairMQDevice @@ -45,80 +40,34 @@ void addCustomOptions(boost::program_options::options_description&); int main(int argc, const char** argv) { - try - { - // Call custom program options hook + using namespace fair::mq; + using namespace fair::mq::hooks; + + fair::mq::DeviceRunner runner{argc, argv}; + + // runner.AddHook([](DeviceRunner& r){ + // // for example: + // r.fPluginManager->SetSearchPaths({"/lib", "/lib/plugins"}); + // r.fPluginManager->LoadPlugin("asdf"); + // }); + + runner.AddHook([](DeviceRunner& r){ boost::program_options::options_description customOptions("Custom options"); addCustomOptions(customOptions); + r.fConfig.AddToCmdLineOptions(customOptions); + }); - // Create plugin manager and load command line supplied plugins - // Plugin manager needs to be destroyed after config! TODO Investigate why - auto pluginManager = fair::mq::PluginManager::MakeFromCommandLineOptions(fair::mq::tools::ToStrVector(argc, argv)); + // runner.AddHook([](DeviceRunner& r){ + // // for example: + // r.fRawCmdLineArgs.push_back("--blubb"); + // }); - // Load builtin plugins last - pluginManager->LoadPlugin("s:control"); + runner.AddHook([](DeviceRunner& r){ + r.fDevice = std::shared_ptr{getDevice(r.fConfig)}; + }); - // Construct command line options parser - FairMQProgOptions config; - config.AddToCmdLineOptions(customOptions); - pluginManager->ForEachPluginProgOptions([&config](boost::program_options::options_description options){ - config.AddToCmdLineOptions(options); - }); - config.AddToCmdLineOptions(pluginManager->ProgramOptions()); + return runner.RunWithExceptionHandlers(); - // Parse command line options - config.ParseAll(argc, argv, true); - - // Call device creation hook - std::shared_ptr device{getDevice(config)}; - if (!device) - { - LOG(ERROR) << "getDevice(): no valid device provided. Exiting."; - return 1; - } - - // Handle --print-channels - device->RegisterChannelEndpoints(); - if (config.Count("print-channels")) - { - device->PrintRegisteredChannels(); - device->ChangeState(FairMQDevice::END); - return 0; - } - - // Handle --version - if (config.Count("version")) - { - std::cout << "User device version: " << device->GetVersion() << std::endl; - std::cout << "FAIRMQ_INTERFACE_VERSION: " << FAIRMQ_INTERFACE_VERSION << std::endl; - device->ChangeState(FairMQDevice::END); - return 0; - } - - LOG(DEBUG) << "PID: " << getpid(); - - // Configure device - device->SetConfig(config); - - // Initialize plugin services - pluginManager->EmplacePluginServices(&config, device); - - // Instantiate and run plugins - pluginManager->InstantiatePlugins(); - - // Wait for control plugin to release device control - pluginManager->WaitForPluginsToReleaseDeviceControl(); - } - catch (std::exception& e) - { - LOG(ERROR) << "Unhandled exception reached the top of main: " << e.what() << ", application will now exit"; - return 1; - } - catch (...) - { - LOG(ERROR) << "Non-exception instance being thrown. Please make sure you use std::runtime_exception() instead. Application will now exit."; - return 1; - } - - return 0; + // Run without builtin catch all exception handler, just: + // return runner.Run(); }