From cd1924968fe96bdf8ab8f6e08293e9e0de67123e Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 24 May 2024 15:38:06 +0200 Subject: [PATCH 1/5] SIM: Add detectorList option Signed-off-by: Felix Schlepper --- Common/SimConfig/CMakeLists.txt | 7 +- .../include/SimConfig/DetectorLists.h | 37 ++++++ .../SimConfig/include/SimConfig/SimConfig.h | 82 ++++++------ Common/SimConfig/src/DetectorLists.cxx | 78 ++++++++++++ Common/SimConfig/src/SimConfig.cxx | 119 ++++++++++++++++-- run/CMakeLists.txt | 2 + run/o2simdefaultdetectorlist.json | 64 ++++++++++ 7 files changed, 338 insertions(+), 51 deletions(-) create mode 100644 Common/SimConfig/include/SimConfig/DetectorLists.h create mode 100644 Common/SimConfig/src/DetectorLists.cxx create mode 100644 run/o2simdefaultdetectorlist.json diff --git a/Common/SimConfig/CMakeLists.txt b/Common/SimConfig/CMakeLists.txt index 80e40284b6e28..c179542580f87 100644 --- a/Common/SimConfig/CMakeLists.txt +++ b/Common/SimConfig/CMakeLists.txt @@ -13,7 +13,9 @@ o2_add_library(SimConfig SOURCES src/SimConfig.cxx src/SimParams.cxx src/SimUserDecay.cxx - src/DigiParams.cxx src/G4Params.cxx + src/DigiParams.cxx + src/G4Params.cxx + src/DetectorLists.cxx src/MatMapParams.cxx src/InteractionDiamondParam.cxx src/GlobalProcessCutSimParam.cxx @@ -27,8 +29,9 @@ o2_target_root_dictionary(SimConfig include/SimConfig/SimParams.h include/SimConfig/SimUserDecay.h include/SimConfig/InteractionDiamondParam.h - include/SimConfig/DigiParams.h + include/SimConfig/DigiParams.h include/SimConfig/G4Params.h + include/SimConfig/DetectorLists.h include/SimConfig/GlobalProcessCutSimParam.h include/SimConfig/MatMapParams.h) diff --git a/Common/SimConfig/include/SimConfig/DetectorLists.h b/Common/SimConfig/include/SimConfig/DetectorLists.h new file mode 100644 index 0000000000000..bdabe71db0872 --- /dev/null +++ b/Common/SimConfig/include/SimConfig/DetectorLists.h @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_DETECTORLISTS_H_ +#define O2_DETECTORLISTS_H_ + +#include +#include +#include + +#include "Framework/Logger.h" + +namespace o2::conf +{ +// Container defining different general evolutions of the ALICE experiment. Each +// evolution is given a name and a list defining the names of the detectors and +// passive elements present. +using DetectorList_t = std::vector; +using DetectorMap_t = std::unordered_map; + +// Parse the detector map from a JSON file. +// Return false if parsing failed. +bool parseDetectorMapfromJSON(const std::string& path, DetectorMap_t& map); + +// Print the DetetectorMap +void printDetMap(const DetectorMap_t& map, const std::string& list = ""); +} // namespace o2::conf + +#endif // O2_DETECTORLISTS_H_ diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index b288083098d6c..b215f22546c8e 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -48,43 +48,43 @@ enum class TimeStampMode { // configuration struct (which can be passed around) struct SimConfigData { - std::vector mActiveModules; // list of active modules - std::vector mReadoutDetectors; // list of readout detectors - std::string mMCEngine; // chosen VMC engine - std::string mGenerator; // chosen VMC generator - std::string mTrigger; // chosen VMC generator trigger - unsigned int mNEvents; // number of events to be simulated - std::string mExtKinFileName; // file name of external kinematics file (needed for ext kinematics generator) - std::string mEmbedIntoFileName; // filename containing the reference events to be used for the embedding - unsigned int mStartEvent; // index of first event to be taken - float mBMax; // maximum for impact parameter sampling - bool mIsMT; // chosen MT mode (Geant4 only) - std::string mOutputPrefix; // prefix to be used for output files - std::string mLogSeverity; // severity for FairLogger - std::string mLogVerbosity; // loglevel for FairLogger - std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens - // Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello - // (can be used to **loosely** change any configuration parameter from command-line) - std::string mConfigFile; // path to a JSON or INI config file (file extension is required to determine type). - // values within the config file will override values set in code by the param classes - // but will themselves be overridden by any values given in mKeyValueTokens. - int mPrimaryChunkSize; // defining max granularity for input primaries of a sim job - int mInternalChunkSize; // - ULong_t mStartSeed; // base for random number seeds - int mSimWorkers = 1; // number of parallel sim workers (when it applies) - bool mFilterNoHitEvents = false; // whether to filter out events not leaving any response - std::string mCCDBUrl; // the URL where to find CCDB - uint64_t mTimestamp; // timestamp in ms to anchor transport simulation to + std::vector mActiveModules; // list of active modules + std::vector mReadoutDetectors; // list of readout detectors + std::string mMCEngine; // chosen VMC engine + std::string mGenerator; // chosen VMC generator + std::string mTrigger; // chosen VMC generator trigger + unsigned int mNEvents; // number of events to be simulated + std::string mExtKinFileName; // file name of external kinematics file (needed for ext kinematics generator) + std::string mEmbedIntoFileName; // filename containing the reference events to be used for the embedding + unsigned int mStartEvent; // index of first event to be taken + float mBMax; // maximum for impact parameter sampling + bool mIsMT; // chosen MT mode (Geant4 only) + std::string mOutputPrefix; // prefix to be used for output files + std::string mLogSeverity; // severity for FairLogger + std::string mLogVerbosity; // loglevel for FairLogger + std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens + // Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello + // (can be used to **loosely** change any configuration parameter from command-line) + std::string mConfigFile; // path to a JSON or INI config file (file extension is required to determine type). + // values within the config file will override values set in code by the param classes + // but will themselves be overridden by any values given in mKeyValueTokens. + unsigned int mPrimaryChunkSize; // defining max granularity for input primaries of a sim job + int mInternalChunkSize; // + ULong_t mStartSeed; // base for random number seeds + int mSimWorkers = 1; // number of parallel sim workers (when it applies) + bool mFilterNoHitEvents = false; // whether to filter out events not leaving any response + std::string mCCDBUrl; // the URL where to find CCDB + uint64_t mTimestamp; // timestamp in ms to anchor transport simulation to TimeStampMode mTimestampMode = TimeStampMode::kNow; // telling of timestamp was given as option or defaulted to now - int mRunNumber = -1; // ALICE run number (if set != -1); the timestamp should be compatible - int mField; // L3 field setting in kGauss: +-2,+-5 and 0 - SimFieldMode mFieldMode = SimFieldMode::kDefault; // uniform magnetic field - bool mAsService = false; // if simulation should be run as service/deamon (does not exit after run) - bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) - bool mIsUpgrade = false; // true if the simulation is for Run 5 - std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events - bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) - bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc + int mRunNumber = -1; // ALICE run number (if set != -1); the timestamp should be compatible + int mField; // L3 field setting in kGauss: +-2,+-5 and 0 + SimFieldMode mFieldMode = SimFieldMode::kDefault; // uniform magnetic field + bool mAsService = false; // if simulation should be run as service/deamon (does not exit after run) + bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) + bool mIsUpgrade = false; // true if the simulation is for Run 5 + std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events + bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) + bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc VertexMode mVertexMode = VertexMode::kDiamondParam; // by default we should use die InteractionDiamond parameter ClassDefNV(SimConfigData, 4); @@ -140,6 +140,7 @@ class SimConfig // static helper functions to determine list of active / readout modules // can also be used from outside static void determineActiveModules(std::vector const& input, std::vector const& skipped, std::vector& active, bool isUpgrade = false); + static bool determineActiveModulesList(const std::string& version, std::vector const& input, std::vector const& skipped, std::vector& active); static void determineReadoutDetectors(std::vector const& active, std::vector const& enabledRO, std::vector const& skippedRO, std::vector& finalRO); // helper to parse field option @@ -179,6 +180,9 @@ class SimConfig private: SimConfigData mConfigData; //! + // Filter out skipped elements in the list + static bool filterSkippedElements(std::vector& elements, std::vector const& skipped); + // adjust/overwrite some option settings when collision context is used void adjustFromCollContext(std::string const& collcontextfile, std::string const& prefix); @@ -206,9 +210,9 @@ struct SimReconfigData { std::string configFile; // path to a JSON or INI config file (file extension is required to determine type). // values within the config file will override values set in code by the param classes // but will themselves be overridden by any values given in mKeyValueTokens. - unsigned int primaryChunkSize; // defining max granularity for input primaries of a sim job - ULong_t startSeed; // base for random number seeds - bool stop; // to shut down the service + unsigned int primaryChunkSize; // defining max granularity for input primaries of a sim job + ULong_t startSeed; // base for random number seeds + bool stop; // to shut down the service std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events ClassDefNV(SimReconfigData, 1); diff --git a/Common/SimConfig/src/DetectorLists.cxx b/Common/SimConfig/src/DetectorLists.cxx new file mode 100644 index 0000000000000..c9132a3cb84a5 --- /dev/null +++ b/Common/SimConfig/src/DetectorLists.cxx @@ -0,0 +1,78 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/DetectorLists.h" +#include +#include +#include +#include +#include + +namespace o2::conf +{ + +bool parseDetectorMapfromJSON(const std::string& path, DetectorMap_t& map) +{ + // Parse JSON file to build map + std::ifstream fileStream(path, std::ios::in); + if (!fileStream.is_open()) { + LOGP(error, "Cannot open '{}'!", path); + return false; + } + rapidjson::IStreamWrapper isw(fileStream); + rapidjson::Document doc; + doc.ParseStream(isw); + if (doc.HasParseError()) { + LOGP(error, "Error parsing provided json file '{}':", path); + LOGP(error, " - Error -> {}", rapidjson::GetParseError_En(doc.GetParseError())); + LOGP(error, " - Offset -> {}", doc.GetErrorOffset()); + return false; + } + + // Clear and rebuild map + map.clear(); + try { + for (auto verItr = doc.MemberBegin(); verItr != doc.MemberEnd(); ++verItr) { + const auto& version = verItr->name.GetString(); + DetectorList_t list; + const auto& elements = doc[version]; + for (const auto& ele : elements.GetArray()) { + list.emplace_back(ele.GetString()); + } + map.emplace(version, list); + } + } catch (const std::exception& e) { + LOGP(error, "Failed to build detector map from file '{}' with '{}'", path, e.what()); + return false; + } + + return true; +} + +void printDetMap(const DetectorMap_t& map, const std::string& list) +{ + if (list.empty()) { + LOGP(error, "List of all available versions including their detectors:"); + for (int i{0}; const auto& [version, elements] : map) { + LOGP(error, " - {: >2d}. {}:", i++, version); + for (int j{0}; const auto& element : elements) { + LOGP(error, "\t\t* {: >2d}.\t{}", j++, element); + } + } + } else { + LOGP(error, "List of available modules for version {}:", list); + for (int j{0}; const auto& element : map.at(list)) { + LOGP(error, "\t* {: >2d}.\t{}", j++, element); + } + } +} + +} // namespace o2::conf diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index ce5d2687979e1..b7c48bf734924 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include using namespace o2::conf; namespace bpo = boost::program_options; @@ -34,6 +36,15 @@ void SimConfig::initOptions(boost::program_options::options_description& options "skipModules", bpo::value>()->multitoken()->default_value(std::vector({""}), ""), "list of modules excluded in geometry (precendence over -m")( "readoutDetectors", bpo::value>()->multitoken()->default_value(std::vector(), ""), "list of detectors creating hits, all if not given; added to to active modules")( "skipReadoutDetectors", bpo::value>()->multitoken()->default_value(std::vector(), ""), "list of detectors to skip hit creation (precendence over --readoutDetectors")( + "detectorList", bpo::value()->default_value("ALICE2"), + "Use a specific version of ALICE, e.g., a predefined list." + "There is an 'official' list provided with:" + "\nALICE2 : The default configuration for Run 3" + "\nALICE2.1: The future configuration for Run 4" + "\nALICE3 : The far-future configuration for Run 5-6" + "\nAdditionally one can provide their own custom list of modules which should be included in the geometry." + "\nBy specifiying LIST:JSONFILE where LIST is a list present in JSONFILE." + "\nOne limitation is that LIST cannot have ':' in their name!")( "nEvents,n", bpo::value()->default_value(0), "number of events")( "startEvent", bpo::value()->default_value(0), "index of first event to be used (when applicable)")( "extKinFile", bpo::value()->default_value("Kinematics.root"), @@ -142,14 +153,64 @@ void SimConfig::determineActiveModules(std::vector const& inputargs #endif } } - // now we take out detectors listed as skipped - for (auto& s : skippedModules) { - auto iter = std::find(activeModules.begin(), activeModules.end(), s); - if (iter != activeModules.end()) { - // take it out - activeModules.erase(iter); + filterSkippedElements(activeModules, skippedModules); +} + +bool SimConfig::determineActiveModulesList(const std::string& version, std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules) +{ + DetectorList_t modules; + DetectorMap_t map; + if (auto pos = version.find(':'); pos != std::string::npos) { + auto pversion = version.substr(0, pos); + auto ppath = version.substr(pos + 1); + if (!parseDetectorMapfromJSON(ppath, map)) { + LOGP(error, "Could not parse {}; check errors above!", ppath); + return false; + } + if (map.find(pversion) == map.end()) { + LOGP(error, "List {} is not defined in custom JSON file!", pversion); + printDetMap(map); + return false; + } + modules = map[pversion]; + LOGP(info, "Running with version {} from custom detector list '{}'", pversion, ppath); + } else { + // Otherwise check 'official' versions which provided in config + auto o2env = std::getenv("O2_ROOT"); + if (!o2env) { + LOGP(error, "O2_ROOT environment not defined"); + return false; } + const std::string rootpath(fmt::format("{}/share/config/o2simdefaultdetectorlist.json", o2env)); + if (!parseDetectorMapfromJSON(rootpath, map)) { + LOGP(error, "Could not parse {} -> check errors above!", rootpath); + return false; + } + if (map.find(version) == map.end()) { + LOGP(error, "List {} is not defined in 'official' JSON file!", version); + printDetMap(map); + return false; + } + modules = map[version]; + LOGP(info, "Running with official detector version '{}'", version); } + // check if specified modules are in list + if (inputargs.size() != 1 || inputargs[0] != "all") { + std::vector diff; + std::set_difference(inputargs.begin(), inputargs.end(), modules.begin(), modules.end(), std::back_inserter(diff)); + if (!diff.empty()) { + LOGP(error, "Modules specified that are not present in detector list {}", version); + for (int j{0}; const auto& m : diff) { + LOGP(info, " - {: <2}. {}", j++, m); + } + printDetMap(map, version); + return false; + } + } + // Insert into active modules if module is built buy -m or insert all if default for -m is used ("all") + std::copy_if(modules.begin(), modules.end(), std::back_inserter(activeModules), + [&inputargs](const auto& e) { return (inputargs.size() == 1 && inputargs[0] == "all") || (std::find(inputargs.begin(), inputargs.end(), e) != inputargs.end()); }); + return filterSkippedElements(activeModules, skippedModules); } void SimConfig::determineReadoutDetectors(std::vector const& activeModules, std::vector const& enableReadout, std::vector const& disableReadout, std::vector& readoutDetectors) @@ -206,8 +267,20 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); - // get final set of active Modules - determineActiveModules(vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules, mConfigData.mIsUpgrade); + // Reset modules and detectors as they are anyway re-parsed + mConfigData.mReadoutDetectors.clear(); + mConfigData.mActiveModules.clear(); + + if (vm["detectorList"].defaulted()) { + // get final set of active Modules + determineActiveModules(vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules, mConfigData.mIsUpgrade); + } else { + // Check for a specific detector version + if (!determineActiveModulesList(vm["detectorList"].as(), vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules)) { + return false; + } + } + if (mConfigData.mNoGeant) { // CAVE is all that's needed (and that will be built either way), so clear all modules and set the O2TrivialMCEngine mConfigData.mActiveModules.clear(); @@ -333,6 +406,34 @@ bool SimConfig::parseFieldString(std::string const& fieldstring, int& fieldvalue return true; } +bool SimConfig::filterSkippedElements(std::vector& elements, std::vector const& skipped) +{ + for (auto& s : skipped) { + if (s.empty()) { // nothing to skip here + continue; + } + auto iter = std::find(elements.begin(), elements.end(), s); + if (iter != elements.end()) { + // take it out + elements.erase(iter); + } else { + LOGP(error, "Skipped modules specified that are not present in built modules!"); + LOGP(error, "Built modules:"); + for (int j{0}; const auto& m : elements) { + LOGP(error, " + {: <2}. {}", j++, m); + } + std::vector diff; + std::set_difference(skipped.begin(), skipped.end(), elements.begin(), elements.end(), std::back_inserter(diff)); + LOGP(error, "Specified skipped modules not present in built modules:"); + for (int j{0}; const auto& m : diff) { + LOGP(error, " - {: <2}. {}", j++, m); + } + return false; + } + } + return true; +} + void SimConfig::adjustFromCollContext(std::string const& collcontextfile, std::string const& prefix) { // When we use pregenerated collision contexts, some options @@ -384,8 +485,6 @@ void SimConfig::adjustFromCollContext(std::string const& collcontextfile, std::s bool SimConfig::resetFromArguments(int argc, char* argv[]) { - namespace bpo = boost::program_options; - // Arguments parsing bpo::variables_map vm; bpo::options_description desc("Allowed options"); diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index 4881df6ea27a2..b1d09db4d02c8 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -148,6 +148,8 @@ o2_add_dpl_workflow(mctracks-to-aod-simple-task o2_data_file(COPY o2simtopology_template.json DESTINATION config) +o2_data_file(COPY o2simdefaultdetectorlist.json DESTINATION config) + # * # add a complex simulation as a unit test (if simulation was enabled) # perform # * # some checks on kinematics and track references diff --git a/run/o2simdefaultdetectorlist.json b/run/o2simdefaultdetectorlist.json new file mode 100644 index 0000000000000..752d3633672ac --- /dev/null +++ b/run/o2simdefaultdetectorlist.json @@ -0,0 +1,64 @@ +{ + "ALICE2": [ + "ITS", + "TPC", + "TRD", + "TOF", + "PHS", + "EMC", + "HMP", + "MFT", + "MCH", + "MID", + "ZDC", + "FT0", + "FV0", + "FDD", + "CTP", + "HALL", + "MAG", + "DIPO", + "COMP", + "PIPE", + "ABSO", + "SHIL" + ], + "ALICE2.1": [ + "IT3", + "TPC", + "TRD", + "TOF", + "PHS", + "EMC", + "HMP", + "MFT", + "MCH", + "MID", + "ZDC", + "FT0", + "FV0", + "FDD", + "CTP", + "FOC", + "HALL", + "MAG", + "DIPO", + "COMP", + "PIPE", + "ABSO", + "SHIL" + ], + "ALICE3": [ + "TRK", + "FT3", + "FCT", + "RCH", + "MI3", + "ECL", + "HALL", + "MAG", + "A3IP", + "A3ABSO", + "A3MAG" + ] +} From a870b204547f08e93945b9f327d6150ba7850f27 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 24 May 2024 15:39:34 +0200 Subject: [PATCH 2/5] SIM: Add dynamic detector library loading Signed-off-by: Felix Schlepper --- Common/SimConfig/CMakeLists.txt | 2 + .../SimConfig/include/SimConfig/SimDLLoader.h | 27 ++ Common/SimConfig/src/SimDLLoader.cxx | 13 + Common/Utils/CMakeLists.txt | 4 +- .../Utils/include/CommonUtils/DLLoaderBase.h | 253 ++++++++++++++++++ Common/Utils/src/DLLoaderBase.cxx | 12 + .../include/ITSSimulation/Detector.h | 8 + .../ITSMFT/ITS/simulation/src/Detector.cxx | 2 + .../include/TPCSimulation/Detector.h | 24 +- Detectors/TPC/simulation/src/Detector.cxx | 3 + .../include/TRKSimulation/Detector.h | 8 +- .../ALICE3/TRK/simulation/src/Detector.cxx | 3 + macro/CMakeLists.txt | 8 +- macro/build_geometry.C | 18 +- 14 files changed, 361 insertions(+), 24 deletions(-) create mode 100644 Common/SimConfig/include/SimConfig/SimDLLoader.h create mode 100644 Common/SimConfig/src/SimDLLoader.cxx create mode 100644 Common/Utils/include/CommonUtils/DLLoaderBase.h create mode 100644 Common/Utils/src/DLLoaderBase.cxx diff --git a/Common/SimConfig/CMakeLists.txt b/Common/SimConfig/CMakeLists.txt index c179542580f87..f8e007209eacc 100644 --- a/Common/SimConfig/CMakeLists.txt +++ b/Common/SimConfig/CMakeLists.txt @@ -12,6 +12,7 @@ o2_add_library(SimConfig SOURCES src/SimConfig.cxx src/SimParams.cxx + src/SimDLLoader.cxx src/SimUserDecay.cxx src/DigiParams.cxx src/G4Params.cxx @@ -27,6 +28,7 @@ o2_add_library(SimConfig o2_target_root_dictionary(SimConfig HEADERS include/SimConfig/SimConfig.h include/SimConfig/SimParams.h + include/SimConfig/SimDLLoader.h include/SimConfig/SimUserDecay.h include/SimConfig/InteractionDiamondParam.h include/SimConfig/DigiParams.h diff --git a/Common/SimConfig/include/SimConfig/SimDLLoader.h b/Common/SimConfig/include/SimConfig/SimDLLoader.h new file mode 100644 index 0000000000000..e778023b28276 --- /dev/null +++ b/Common/SimConfig/include/SimConfig/SimDLLoader.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef SIMDLLOADER_H_ +#define SIMDLLOADER_H_ + +#include "CommonUtils/DLLoaderBase.h" + +namespace o2::conf +{ + +class SimDLLoader : public o2::utils::DLLoaderBase +{ + O2DLLoaderDef(SimDLLoader) +}; + +} // namespace o2::conf + +#endif // SIMDLLOADER_H_ diff --git a/Common/SimConfig/src/SimDLLoader.cxx b/Common/SimConfig/src/SimDLLoader.cxx new file mode 100644 index 0000000000000..ed15a8b0cef6a --- /dev/null +++ b/Common/SimConfig/src/SimDLLoader.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/SimDLLoader.h" +O2DLLoaderImpl(o2::conf::SimDLLoader) diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 7f650b973990b..47f6c9627a6a7 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -24,8 +24,9 @@ o2_add_library(CommonUtils src/NameConf.cxx src/IRFrameSelector.cxx src/DebugStreamer.cxx + src/DLLoaderBase.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::Tree Boost::iostreams O2::CommonDataFormat O2::Headers - FairLogger::FairLogger O2::MathUtils TBB::tbb) + FairLogger::FairLogger O2::MathUtils TBB::tbb Boost::boost) o2_target_root_dictionary(CommonUtils HEADERS include/CommonUtils/TreeStream.h @@ -45,6 +46,7 @@ o2_target_root_dictionary(CommonUtils include/CommonUtils/KeyValParam.h include/CommonUtils/VerbosityConfig.h include/CommonUtils/FileFetcher.h + include/CommonUtils/DLLoaderBase.h include/CommonUtils/NameConf.h include/CommonUtils/IRFrameSelector.h include/CommonUtils/DebugStreamer.h) diff --git a/Common/Utils/include/CommonUtils/DLLoaderBase.h b/Common/Utils/include/CommonUtils/DLLoaderBase.h new file mode 100644 index 0000000000000..91d67663d7245 --- /dev/null +++ b/Common/Utils/include/CommonUtils/DLLoaderBase.h @@ -0,0 +1,253 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief A thin wrapper to manage dynamic library loading, based around boost::dll + +#ifndef DLLOADER_H_ +#define DLLOADER_H_ + +#include "Framework/Logger.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::utils +{ + +// Manages dynamic loading and unloading (or rather ensuring they are not +// unloaded for the duration of the program) of libraries and symbol lookups. It +// ensures thread-safety through the use of a mutex and implements the Meyers +// Singleton pattern to provide a single instance of the manager. +template +class DLLoaderBase +{ + + public: + using library_t = std::shared_ptr; + + // Returns the singleton instance of the manager. Any function should only be + // accessed through an instance. This being a singleton serves two purposes: + // 1. Libraries are loaded only once. + // 2. Once loaded they are not again unloaded until the end of the program. + static DerivedType& Instance() + { + return DerivedType::sInstance; + } + + // Loads a dynamic library by its name and stores its handle. Returns true + // if the library is successfully loaded or already loaded. + bool addLibrary(const std::string& library) + { + const std::lock_guard lock(mLock); + + if (mLibraries.find(library) != mLibraries.end()) { + return true; // Library already loaded + } + + if (mO2Path.empty()) { + if (const auto* path = std::getenv("O2_ROOT")) { + mO2Path = path; + } else { + LOGP(error, "$O2_ROOT not set!"); + return false; + } + } + + auto path = getO2Path(library); + if (auto dpath{boost::dll::shared_library::decorate(path)}; !std::filesystem::exists(dpath.c_str())) { + LOGP(error, "Library under '{}' does not exist!", dpath.c_str()); + return false; + } + + try { + auto lib = std::make_shared(path, + boost::dll::load_mode::append_decorations | + boost::dll::load_mode::rtld_lazy); + if (lib == nullptr) { + throw std::runtime_error("Library handle is nullptr!"); + } + mLibraries[library] = std::move(lib); + LOGP(info, "Loaded dynamic library '{}' from '{}'", library, path); + return true; + } catch (std::exception& e) { + LOGP(error, "Failed to load library (path='{}'), failed reason: '{}'", boost::dll::shared_library::decorate(path).c_str(), e.what()); + return false; + } catch (...) { + LOGP(error, "Failed to load library (path='{}') for unknown reason!", boost::dll::shared_library::decorate(path).c_str()); + return false; + } + } + + // Unloads a given library returns true if this succeeded. + // + // Nota bene: Actually, we have very little control here when the unloading + // hapens since the linkder decides this based on if there is any reference + // left. And even if the reference counter goes to zero the linker is free to + // leave the library loaded and clean up whenever it wants. + bool unloadLibrary(const std::string& library) + { + const std::lock_guard lock(mLock); + + if (auto it = mLibraries.find(library); it != mLibraries.end()) { + mLibraries.erase(it); + return true; + } + + LOGP(error, "No '{}' library found, cannot unload it!", library); + return false; + } + + // Resets all loaded libraries and O2Path, this invalidates all outside kept + // references. + void reset() + { + mO2Path.clear(); + mLibraries.clear(); + } + + // Checks if a library contains a specific symbol. + bool hasSymbol(const std::string& library, const std::string& symbol) + { + const std::lock_guard lock(mLock); + + if (mLibraries.find(library) == mLibraries.end()) { + // Library not loaded, attempt to load it + if (!addLibrary(library)) { + return false; + } + } + + // Checks if the symbol exists but does not load it. + return mLibraries[library]->has(symbol); + } + + // Gets a function alias from a loaded library. + template + std::optional> getFunctionAlias(const std::string& library, const std::string& fname) + { + const std::lock_guard lock(mLock); + + if (fname.empty()) { + LOGP(error, "Function name cannot be empty!"); + return std::nullopt; + } + + if (mLibraries.find(library) == mLibraries.end()) { + // Library not loaded, attempt to load it + if (!addLibrary(library)) { + return std::nullopt; + } + } + + const auto& lib = *mLibraries[library]; + if (!lib.has(fname)) { + LOGP(error, "Library '{}' does not have a symbol '{}'", library, fname); + return std::nullopt; + } + + boost::function func = boost::dll::import_alias(lib, fname); + if (func.empty()) { + LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); + return std::nullopt; + } + return func; + } + + // Immediatley executes a function alias from a loaded library. + template + Ret executeFunctionAlias(const std::string& library, const std::string& fname, Args... args) + { + const std::lock_guard lock(mLock); + + using ProtoType = Ret(Args...); + if (auto func = getFunctionAlias(library, fname)) { + return (*func)(args...); + } + + // cannot execute function at all + throw std::runtime_error(fmt::format("Cannot get '{}' from '{}'", fname, library)); + } + + // Prints information about the loaded libraries and their symbols. + void print(bool verbose = false) + { + const std::lock_guard lock(mLock); + + if (mO2Path.empty() || mLibraries.empty()) { + LOGP(info, "No libraries added!"); + } + + LOGP(info, "Printing loaded dynamic library information:"); + for (int i{0}; const auto& [library, handle] : mLibraries) { + LOGP(info, " {: <3d}: {}", i++, library); + if (verbose) { + boost::dll::library_info libInfo{boost::dll::shared_library::decorate(getO2Path(library))}; + for (int j{0}; const auto& sec : libInfo.sections()) { + LOGP(info, " SECTION {: <3d}: {}", j++, sec); + for (int k{0}; const auto& symb : libInfo.symbols(sec)) { + LOGP(info, " SYMBOL {: <3d}: {}", k++, symb); + } + } + } + } + } + + // Delete copy and move constructors to enforce singleton pattern. + DLLoaderBase(const DLLoaderBase&) = delete; + DLLoaderBase& operator=(const DLLoaderBase&) = delete; + DLLoaderBase(DLLoaderBase&&) = delete; + DLLoaderBase& operator=(DLLoaderBase&&) = delete; + + protected: + // Constructor and destructor are protected to enforce singleton pattern. + DLLoaderBase() = default; + ~DLLoaderBase() = default; + + private: + // Returns the full path to a O2 shared library.. + [[nodiscard]] std::string getO2Path(const std::string& library) const + { + return mO2Path + "/lib/" + library; + } + + // Returns the demangled type name of a prototype, e.g., for pretty printing. + template + [[nodiscard]] auto getTypeName() -> std::string + { + return boost::core::demangle(typeid(ProtoType).name()); + } + + std::unordered_map mLibraries{}; // Pointers to loaded libraries, calls `unload()' for each library, e.g., correctly destroy this object + std::recursive_mutex mLock{}; // While a recursive mutex is more expansive it makes locking easier + std::string mO2Path{}; // Holds the path O2 dynamic library determined by $O2_ROOT +}; + +} // namespace o2::utils + +#define O2DLLoaderDef(classname) \ + private: \ + static classname sInstance; \ + classname() = default; \ + friend class o2::utils::DLLoaderBase; + +#define O2DLLoaderImpl(classname) classname classname::sInstance; + +#endif // DLLOADER_H_ diff --git a/Common/Utils/src/DLLoaderBase.cxx b/Common/Utils/src/DLLoaderBase.cxx new file mode 100644 index 0000000000000..1e4ab97604794 --- /dev/null +++ b/Common/Utils/src/DLLoaderBase.cxx @@ -0,0 +1,12 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/DLLoaderBase.h" diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h index 63a1f36c58fab..24c14faca40ab 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h @@ -27,6 +27,8 @@ #include "TLorentzVector.h" // for TLorentzVector #include "TVector3.h" // for TVector3 +#include // for BOOST_DLL_ALIAS + #ifdef ENABLE_UPGRADES #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" // for Description of Inner Barrel (ITS3) #endif @@ -79,6 +81,12 @@ class Detector : public o2::base::DetImpl /// kFALSE for inactive detectors Detector(Bool_t active, TString name = "ITS"); + // Factory method + static o2::base::Detector* create(const char* name, bool active) + { + return new Detector(active, name); + } + /// Default constructor Detector(); diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index c40355f0f34d8..2aa9cf63ae81a 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -1317,3 +1317,5 @@ Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TV } ClassImp(o2::its::Detector); + +BOOST_DLL_ALIAS(o2::its::Detector::create, create_Detector_its) diff --git a/Detectors/TPC/simulation/include/TPCSimulation/Detector.h b/Detectors/TPC/simulation/include/TPCSimulation/Detector.h index 74b9241417be0..507ba992e2715 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/Detector.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/Detector.h @@ -63,11 +63,17 @@ class Detector : public o2::base::DetImpl kAlumina1 = 26 }; /** Name : Detector Name - * Active: kTRUE for active detectors (ProcessHits() will be called) - * kFALSE for inactive detectors - */ + * Active: kTRUE for active detectors (ProcessHits() will be called) + * kFALSE for inactive detectors + */ Detector(Bool_t Active); + // Factory method + static o2::base::Detector* create(bool active) + { + return new Detector(active); + } + /** default constructor */ Detector(); @@ -78,8 +84,8 @@ class Detector : public o2::base::DetImpl void InitializeO2Detector() override; /** this method is called for each step during simulation - * (see FairMCApplication::Stepping()) - */ + * (see FairMCApplication::Stepping()) + */ // virtual Bool_t ProcessHitsOrig( FairVolume* v=0); Bool_t ProcessHits(FairVolume* v = nullptr) override; @@ -105,8 +111,8 @@ class Detector : public o2::base::DetImpl void ConstructGeometry() override; /** This method is an example of how to add your own point - * of type DetectorPoint to the clones array - */ + * of type DetectorPoint to the clones array + */ Point* addHit(float x, float y, float z, float time, float nElectrons, float trackID, float detID); /// Copied from AliRoot - should go to someplace else @@ -130,8 +136,8 @@ class Detector : public o2::base::DetImpl Double_t Gamma(Double_t k); /** The following methods can be implemented if you need to make - * any optional action in your detector during the transport. - */ + * any optional action in your detector during the transport. + */ void EndOfEvent() override; void FinishPrimary() override { ; } diff --git a/Detectors/TPC/simulation/src/Detector.cxx b/Detectors/TPC/simulation/src/Detector.cxx index 60dcc542c70ff..9a77ee3b54c03 100644 --- a/Detectors/TPC/simulation/src/Detector.cxx +++ b/Detectors/TPC/simulation/src/Detector.cxx @@ -3216,3 +3216,6 @@ std::string Detector::getHitBranchNames(int probe) const } ClassImp(o2::tpc::Detector); + +#include +BOOST_DLL_ALIAS(o2::tpc::Detector::create, create_Detector_tpc) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h index 47a48167e0562..5b777641dbe99 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -34,6 +34,12 @@ class Detector : public o2::base::DetImpl Detector(); ~Detector(); + // Factory method + static o2::base::Detector* create(bool active) + { + return new Detector(active); + } + void ConstructGeometry() override; o2::itsmft::Hit* addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, @@ -104,4 +110,4 @@ struct UseShm { } // namespace base } // namespace o2 #endif -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index d6c4613b6b589..33de3d4cabb7d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -366,3 +366,6 @@ o2::itsmft::Hit* Detector::addHit(int trackID, int detID, const TVector3& startP } // namespace o2 ClassImp(o2::trk::Detector); + +#include +BOOST_DLL_ALIAS(o2::trk::Detector::create, create_Detector_trk) diff --git a/macro/CMakeLists.txt b/macro/CMakeLists.txt index ca4d8126a84d5..826cb0ea06ade 100644 --- a/macro/CMakeLists.txt +++ b/macro/CMakeLists.txt @@ -114,7 +114,6 @@ if(ENABLE_UPGRADES) set(upgradeTargets O2::Alice3DetectorsPassive O2::ITS3Simulation - O2::TRKSimulation O2::FT3Simulation O2::FCTSimulation O2::IOTOFSimulation @@ -124,10 +123,9 @@ if(ENABLE_UPGRADES) endif() o2_add_test_root_macro(build_geometry.C - PUBLIC_LINK_LIBRARIES O2::DetectorsPassive + PUBLIC_LINK_LIBRARIES O2::SimConfig + O2::DetectorsPassive O2::Field - O2::TPCSimulation - O2::ITSSimulation O2::MFTSimulation O2::MCHSimulation O2::MIDSimulation @@ -183,8 +181,6 @@ o2_add_test_root_macro(o2sim.C PUBLIC_LINK_LIBRARIES O2::Generators O2::DetectorsPassive O2::Field - O2::TPCSimulation - O2::ITSSimulation O2::MFTSimulation O2::MCHSimulation O2::MIDSimulation diff --git a/macro/build_geometry.C b/macro/build_geometry.C index 3712f6bf85a93..04ce8a8f4372c 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -23,8 +23,6 @@ #include "DetectorsPassive/Hall.h" #include "DetectorsPassive/Pipe.h" #include -#include -#include #include #include #include @@ -47,10 +45,10 @@ #include #include "DetectorsCommonDataFormats/UpgradesStatus.h" #include +#include #endif #ifdef ENABLE_UPGRADES -#include #include #include #include @@ -62,6 +60,8 @@ #include #endif +using Return = o2::base::Detector*; + void finalize_geometry(FairRunSim* run); bool isActivated(std::string s) @@ -221,17 +221,20 @@ void build_geometry(FairRunSim* run = nullptr) if (isActivated("TPC")) { // tpc - addReadoutDetector(new o2::tpc::Detector(isReadout("TPC"))); + addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( + "O2TPCSimulation", "create_Detector_tpc", isReadout("TPC"))); } #ifdef ENABLE_UPGRADES if (isActivated("IT3")) { // IT3 - addReadoutDetector(new o2::its::Detector(isReadout("IT3"), "IT3")); + addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( + "O2ITSSimulation", "create_Detector_its", "IT3", isReadout("IT3"))); } if (isActivated("TRK")) { // ALICE 3 TRK - addReadoutDetector(new o2::trk::Detector(isReadout("TRK"))); + addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( + "O2TRKSimulation", "create_Detector_trk", isReadout("TRK"))); } if (isActivated("FT3")) { @@ -267,7 +270,8 @@ void build_geometry(FairRunSim* run = nullptr) if (isActivated("ITS")) { // its - addReadoutDetector(new o2::its::Detector(isReadout("ITS"))); + addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( + "O2ITSSimulation", "create_Detector_its", "ITS", isReadout("ITS"))); } if (isActivated("MFT")) { From 569748df02225cb765a2421dca052fb0dafab1de Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 27 May 2024 22:31:26 +0200 Subject: [PATCH 3/5] SIM: remove Boost dependency Signed-off-by: Felix Schlepper --- Common/Utils/CMakeLists.txt | 2 +- .../Utils/include/CommonUtils/DLLoaderBase.h | 130 ++++++++++-------- .../ITSMFT/ITS/simulation/src/Detector.cxx | 8 +- Detectors/TPC/simulation/src/Detector.cxx | 9 +- .../ALICE3/TRK/simulation/src/Detector.cxx | 9 +- macro/build_geometry.C | 8 +- 6 files changed, 97 insertions(+), 69 deletions(-) diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 47f6c9627a6a7..786ccc8f784fe 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -26,7 +26,7 @@ o2_add_library(CommonUtils src/DebugStreamer.cxx src/DLLoaderBase.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::Tree Boost::iostreams O2::CommonDataFormat O2::Headers - FairLogger::FairLogger O2::MathUtils TBB::tbb Boost::boost) + FairLogger::FairLogger O2::MathUtils TBB::tbb) o2_target_root_dictionary(CommonUtils HEADERS include/CommonUtils/TreeStream.h diff --git a/Common/Utils/include/CommonUtils/DLLoaderBase.h b/Common/Utils/include/CommonUtils/DLLoaderBase.h index 91d67663d7245..634e7a3132764 100644 --- a/Common/Utils/include/CommonUtils/DLLoaderBase.h +++ b/Common/Utils/include/CommonUtils/DLLoaderBase.h @@ -14,20 +14,22 @@ #ifndef DLLOADER_H_ #define DLLOADER_H_ -#include "Framework/Logger.h" - -#include -#include -#include - #include #include #include #include #include #include -#include #include +#include "dlfcn.h" + +#if defined(__APPLE__) +#define DLLOADER_MAC_LINUX(mac, linux) mac +#elif +#define DLLOADER_MAC_LINUX(mac, linux) linux +#endif + +#include "Framework/Logger.h" namespace o2::utils { @@ -39,9 +41,30 @@ namespace o2::utils template class DLLoaderBase { - public: - using library_t = std::shared_ptr; + struct filename_decorations { + static constexpr const char* prefix = "lib"; // same prefix on mac and linux + static constexpr const char* suffix = DLLOADER_MAC_LINUX(".dylib", ".so"); + }; + enum Options { + none = 0, + global = RTLD_GLOBAL, + local = RTLD_LOCAL, + no_delete = RTLD_NODELETE, + no_load = RTLD_NOLOAD, + lazy = RTLD_LAZY, + }; + using handle_t = void; + using handle_ptr_t = handle_t*; + struct HandleDeleter { + void operator()(handle_ptr_t p) + { + if (p != nullptr) { + dlclose(p); + } + } + }; + using library_t = std::unique_ptr; // Returns the singleton instance of the manager. Any function should only be // accessed through an instance. This being a singleton serves two purposes: @@ -72,15 +95,13 @@ class DLLoaderBase } auto path = getO2Path(library); - if (auto dpath{boost::dll::shared_library::decorate(path)}; !std::filesystem::exists(dpath.c_str())) { - LOGP(error, "Library under '{}' does not exist!", dpath.c_str()); + if (!std::filesystem::exists(path)) { + LOGP(error, "Library under '{}' does not exist!", path); return false; } try { - auto lib = std::make_shared(path, - boost::dll::load_mode::append_decorations | - boost::dll::load_mode::rtld_lazy); + auto lib = std::unique_ptr(dlopen(path.c_str(), mLoadPolicy)); if (lib == nullptr) { throw std::runtime_error("Library handle is nullptr!"); } @@ -88,10 +109,10 @@ class DLLoaderBase LOGP(info, "Loaded dynamic library '{}' from '{}'", library, path); return true; } catch (std::exception& e) { - LOGP(error, "Failed to load library (path='{}'), failed reason: '{}'", boost::dll::shared_library::decorate(path).c_str(), e.what()); + LOGP(error, "Failed to load library (path='{}'), failed reason: '{}'", path, e.what()); return false; } catch (...) { - LOGP(error, "Failed to load library (path='{}') for unknown reason!", boost::dll::shared_library::decorate(path).c_str()); + LOGP(error, "Failed to load library (path='{}') for unknown reason!", path.c_str()); return false; } } @@ -135,14 +156,22 @@ class DLLoaderBase } } + dlerror(); // clear previous error + // Checks if the symbol exists but does not load it. - return mLibraries[library]->has(symbol); + handle_ptr_t ptr = dlsym(mLibraries[library].get(), symbol.c_str()); + if (auto err = dlerror(); err != nullptr) { + // LOGP(error, "Did not get {} from {}; error: {}", symbol, library, err); + } + return ptr != nullptr; } - // Gets a function alias from a loaded library. - template - std::optional> getFunctionAlias(const std::string& library, const std::string& fname) + // Executes a function from a loaded library or return nullopt + template + std::optional executeFunction(const std::string& library, const std::string& fname, Args... args) { + using Func_t = Ret (*)(Args...); + const std::lock_guard lock(mLock); if (fname.empty()) { @@ -157,57 +186,39 @@ class DLLoaderBase } } - const auto& lib = *mLibraries[library]; - if (!lib.has(fname)) { + const auto& lib = mLibraries[library].get(); + if (!hasSymbol(library, fname)) { LOGP(error, "Library '{}' does not have a symbol '{}'", library, fname); return std::nullopt; } - boost::function func = boost::dll::import_alias(lib, fname); - if (func.empty()) { - LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); + dlerror(); // Clear previous error + + auto func = (Func_t)dlsym(lib, fname.c_str()); + if (auto err = dlerror(); err != nullptr) { + LOGP(error, "Did not get {} from {}; error: {}", fname, library, err); return std::nullopt; } - return func; - } - - // Immediatley executes a function alias from a loaded library. - template - Ret executeFunctionAlias(const std::string& library, const std::string& fname, Args... args) - { - const std::lock_guard lock(mLock); - using ProtoType = Ret(Args...); - if (auto func = getFunctionAlias(library, fname)) { - return (*func)(args...); + if (func == nullptr) { + LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); + return std::nullopt; } - // cannot execute function at all - throw std::runtime_error(fmt::format("Cannot get '{}' from '{}'", fname, library)); + // Execute function an return its return value + return func(args...); } - // Prints information about the loaded libraries and their symbols. - void print(bool verbose = false) + // Wrapper for function execution which fatals if execution fails + template + Ret executeFunctionAlias(const std::string& library, const std::string& fname, Args... args) { - const std::lock_guard lock(mLock); - - if (mO2Path.empty() || mLibraries.empty()) { - LOGP(info, "No libraries added!"); + if (auto opt = executeFunction(library, fname, args...)) { + return *opt; } - LOGP(info, "Printing loaded dynamic library information:"); - for (int i{0}; const auto& [library, handle] : mLibraries) { - LOGP(info, " {: <3d}: {}", i++, library); - if (verbose) { - boost::dll::library_info libInfo{boost::dll::shared_library::decorate(getO2Path(library))}; - for (int j{0}; const auto& sec : libInfo.sections()) { - LOGP(info, " SECTION {: <3d}: {}", j++, sec); - for (int k{0}; const auto& symb : libInfo.symbols(sec)) { - LOGP(info, " SYMBOL {: <3d}: {}", k++, symb); - } - } - } - } + LOGP(fatal, "Execution of '{}' from '{}' failed spectaculary!", fname, library); + __builtin_unreachable(); // is this safe, AFACIT only gcc and clang are supported anyway } // Delete copy and move constructors to enforce singleton pattern. @@ -225,19 +236,20 @@ class DLLoaderBase // Returns the full path to a O2 shared library.. [[nodiscard]] std::string getO2Path(const std::string& library) const { - return mO2Path + "/lib/" + library; + return mO2Path + "/lib/" + filename_decorations::prefix + library + filename_decorations::suffix; } // Returns the demangled type name of a prototype, e.g., for pretty printing. template [[nodiscard]] auto getTypeName() -> std::string { - return boost::core::demangle(typeid(ProtoType).name()); + return typeid(ProtoType).name(); // TODO } std::unordered_map mLibraries{}; // Pointers to loaded libraries, calls `unload()' for each library, e.g., correctly destroy this object std::recursive_mutex mLock{}; // While a recursive mutex is more expansive it makes locking easier std::string mO2Path{}; // Holds the path O2 dynamic library determined by $O2_ROOT + Options mLoadPolicy{Options::lazy}; // load resolution policy }; } // namespace o2::utils diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index 2aa9cf63ae81a..d005c9ea9858a 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -1318,4 +1318,10 @@ Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TV ClassImp(o2::its::Detector); -BOOST_DLL_ALIAS(o2::its::Detector::create, create_Detector_its) +// Define Factory method for calling from the outside +extern "C" { +o2::base::Detector* create_detector_its(const char* name, bool active) +{ + return o2::its::Detector::create(name, active); +} +} diff --git a/Detectors/TPC/simulation/src/Detector.cxx b/Detectors/TPC/simulation/src/Detector.cxx index 9a77ee3b54c03..367abdc1a753a 100644 --- a/Detectors/TPC/simulation/src/Detector.cxx +++ b/Detectors/TPC/simulation/src/Detector.cxx @@ -3217,5 +3217,10 @@ std::string Detector::getHitBranchNames(int probe) const ClassImp(o2::tpc::Detector); -#include -BOOST_DLL_ALIAS(o2::tpc::Detector::create, create_Detector_tpc) +// Define Factory method for calling from the outside +extern "C" { +o2::base::Detector* create_detector_tpc(bool active) +{ + return o2::tpc::Detector::create(active); +} +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 33de3d4cabb7d..7a16d0643942c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -367,5 +367,10 @@ o2::itsmft::Hit* Detector::addHit(int trackID, int detID, const TVector3& startP ClassImp(o2::trk::Detector); -#include -BOOST_DLL_ALIAS(o2::trk::Detector::create, create_Detector_trk) +// Define Factory method for calling from the outside +extern "C" { +o2::base::Detector* create_detector_trk(bool active) +{ + return o2::trk::Detector::create(active); +} +} diff --git a/macro/build_geometry.C b/macro/build_geometry.C index 04ce8a8f4372c..aebfbee7ce283 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -222,19 +222,19 @@ void build_geometry(FairRunSim* run = nullptr) if (isActivated("TPC")) { // tpc addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( - "O2TPCSimulation", "create_Detector_tpc", isReadout("TPC"))); + "O2TPCSimulation", "create_detector_tpc", isReadout("TPC"))); } #ifdef ENABLE_UPGRADES if (isActivated("IT3")) { // IT3 addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( - "O2ITSSimulation", "create_Detector_its", "IT3", isReadout("IT3"))); + "O2ITSSimulation", "create_detector_its", "IT3", isReadout("IT3"))); } if (isActivated("TRK")) { // ALICE 3 TRK addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( - "O2TRKSimulation", "create_Detector_trk", isReadout("TRK"))); + "O2TRKSimulation", "create_detector_trk", isReadout("TRK"))); } if (isActivated("FT3")) { @@ -271,7 +271,7 @@ void build_geometry(FairRunSim* run = nullptr) if (isActivated("ITS")) { // its addReadoutDetector(o2::conf::SimDLLoader::Instance().executeFunctionAlias( - "O2ITSSimulation", "create_Detector_its", "ITS", isReadout("ITS"))); + "O2ITSSimulation", "create_detector_its", "ITS", isReadout("ITS"))); } if (isActivated("MFT")) { From e54526bdde92ec8d91a778857d794b1abbfc198c Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 27 May 2024 23:48:46 +0200 Subject: [PATCH 4/5] SIM: Fix malformed ifdef Signed-off-by: Felix Schlepper --- Common/Utils/include/CommonUtils/DLLoaderBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Utils/include/CommonUtils/DLLoaderBase.h b/Common/Utils/include/CommonUtils/DLLoaderBase.h index 634e7a3132764..0c89c44cce378 100644 --- a/Common/Utils/include/CommonUtils/DLLoaderBase.h +++ b/Common/Utils/include/CommonUtils/DLLoaderBase.h @@ -25,7 +25,7 @@ #if defined(__APPLE__) #define DLLOADER_MAC_LINUX(mac, linux) mac -#elif +#else #define DLLOADER_MAC_LINUX(mac, linux) linux #endif From b44bdd5afa3fba92fcb260b949e5e539ba66ed58 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 28 May 2024 07:20:18 +0200 Subject: [PATCH 5/5] SIM: remove stray include Signed-off-by: Felix Schlepper --- Common/Utils/include/CommonUtils/DLLoaderBase.h | 11 ++++++----- .../ITS/simulation/include/ITSSimulation/Detector.h | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Common/Utils/include/CommonUtils/DLLoaderBase.h b/Common/Utils/include/CommonUtils/DLLoaderBase.h index 0c89c44cce378..61d349876c92e 100644 --- a/Common/Utils/include/CommonUtils/DLLoaderBase.h +++ b/Common/Utils/include/CommonUtils/DLLoaderBase.h @@ -160,9 +160,10 @@ class DLLoaderBase // Checks if the symbol exists but does not load it. handle_ptr_t ptr = dlsym(mLibraries[library].get(), symbol.c_str()); - if (auto err = dlerror(); err != nullptr) { - // LOGP(error, "Did not get {} from {}; error: {}", symbol, library, err); + if (const auto* err = dlerror(); err != nullptr) { + LOGP(error, "Did not get {} from {}; error: {}", symbol, library, err); } + return ptr != nullptr; } @@ -195,17 +196,17 @@ class DLLoaderBase dlerror(); // Clear previous error auto func = (Func_t)dlsym(lib, fname.c_str()); - if (auto err = dlerror(); err != nullptr) { + if (const auto* err = dlerror(); err != nullptr) { LOGP(error, "Did not get {} from {}; error: {}", fname, library, err); return std::nullopt; } if (func == nullptr) { - LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); + LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); return std::nullopt; } - // Execute function an return its return value + // Execute function and return its return value return func(args...); } diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h index 24c14faca40ab..c48f0f942d29c 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h @@ -27,8 +27,6 @@ #include "TLorentzVector.h" // for TLorentzVector #include "TVector3.h" // for TVector3 -#include // for BOOST_DLL_ALIAS - #ifdef ENABLE_UPGRADES #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" // for Description of Inner Barrel (ITS3) #endif