Skip to content

Commit f04c23d

Browse files
authored
DPL: initial support for plugin based algorithms (#5071)
These are to be used to decouple workflow specific components from the code itself. For example here we use it to load the the rootFileReader from a separate library. This will allow us to load JAlien only when running an analysis workflow.
1 parent 8b00c13 commit f04c23d

File tree

6 files changed

+130
-6
lines changed

6 files changed

+130
-6
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright CERN and copyright holders of ALICE O2. This software is distributed
2+
# under the terms of the GNU General Public License v3 (GPL Version 3), copied
3+
# verbatim in the file "COPYING".
4+
#
5+
# See http://alice-o2.web.cern.ch/license for full licensing information.
6+
#
7+
# In applying this license CERN does not waive the privileges and immunities
8+
# granted to it by virtue of its status as an Intergovernmental Organization or
9+
# submit itself to any jurisdiction.
10+
11+
# Given GCC 7.3 does not provide std::filesystem we use Boost instead
12+
# Drop this once we move to GCC 8.2+
13+
o2_add_library(FrameworkAnalysisSupport
14+
SOURCES src/Plugin.cxx
15+
PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src
16+
PUBLIC_LINK_LIBRARIES O2::Framework)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
#include "Framework/Plugins.h"
11+
#include "Framework/AlgorithmSpec.h"
12+
#include "Framework/AODReaderHelpers.h"
13+
14+
struct ROOTFileReader : o2::framework::AlgorithmPlugin {
15+
o2::framework::AlgorithmSpec create() override
16+
{
17+
return o2::framework::readers::AODReaderHelpers::rootFileReaderCallback();
18+
}
19+
};
20+
21+
DEFINE_DPL_PLUGIN(ROOTFileReader, CustomAlgorithm);

Framework/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ add_subdirectory(Core)
1616

1717
add_subdirectory(Utils)
1818

19+
add_subdirectory(AnalysisSupport)
20+
1921
add_subdirectory(TestWorkflows)

Framework/Core/include/Framework/AlgorithmSpec.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818

1919
#include <functional>
2020

21-
namespace o2
22-
{
23-
namespace framework
21+
namespace o2::framework
2422
{
2523

2624
/// This is the class holding the actual algorithm to be used. Notice that the
@@ -88,6 +86,11 @@ struct AlgorithmSpec {
8886
ErrorCallback onError = nullptr;
8987
};
9088

89+
/// Helper class for an algorithm which is loaded as a plugin.
90+
struct AlgorithmPlugin {
91+
virtual AlgorithmSpec create() = 0;
92+
};
93+
9194
template <typename T>
9295
struct ContextElementTraits {
9396
static decltype(auto) get(ProcessingContext& ctx)
@@ -180,7 +183,6 @@ AlgorithmSpec::InitCallback adaptStateful(LAMBDA l)
180183
return adaptStatefulF(FFL(l));
181184
}
182185

183-
} // namespace framework
184-
} // namespace o2
186+
} // namespace o2::framework
185187

186188
#endif // FRAMEWORK_ALGORITHMSPEC_H
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
#ifndef O2_FRAMEWORK_PLUGINS_H_
11+
#define O2_FRAMEWORK_PLUGINS_H_
12+
13+
#include "Framework/AlgorithmSpec.h"
14+
#include <cstring>
15+
16+
namespace o2::framework
17+
{
18+
19+
enum struct DplPluginKind : int {
20+
// A plugin which can customise the workflow. Needs to return
21+
// an object of kind o2::framework::WorkflowCustomizationService
22+
CustomAlgorithm
23+
};
24+
25+
} // namespace o2::framework
26+
27+
/// An handle for a generic DPL plugin.
28+
/// The handle is returned by the dpl_plugin_callback()
29+
struct DPLPluginHandle {
30+
void* instance;
31+
char const* name;
32+
enum o2::framework::DplPluginKind kind;
33+
DPLPluginHandle* previous;
34+
};
35+
36+
#define DEFINE_DPL_PLUGIN(NAME, KIND) \
37+
extern "C" { \
38+
DPLPluginHandle* dpl_plugin_callback(DPLPluginHandle* previous) \
39+
{ \
40+
return new DPLPluginHandle{new NAME{}, strdup(#NAME), o2::framework::DplPluginKind::KIND, previous}; \
41+
} \
42+
}
43+
44+
namespace o2::framework
45+
{
46+
struct PluginManager {
47+
template <typename T>
48+
static T* getByName(DPLPluginHandle* handle, char const* name)
49+
{
50+
while (handle != nullptr) {
51+
if (strncmp(handle->name, name, strlen(name)) == 0) {
52+
return reinterpret_cast<T*>(handle->instance);
53+
}
54+
handle = handle->previous;
55+
}
56+
return nullptr;
57+
}
58+
};
59+
} // namespace o2::framework
60+
61+
#endif // O2_FRAMEWORK_PLUGINS_H_

Framework/Core/src/WorkflowHelpers.cxx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "Framework/RawDeviceService.h"
2121
#include "Framework/StringHelpers.h"
2222
#include "Framework/CommonMessageBackends.h"
23+
#include "Framework/ExternalFairMQDeviceProxy.h"
24+
#include "Framework/Plugins.h"
2325

2426
#include "Headers/DataHeader.h"
2527
#include <algorithm>
@@ -258,7 +260,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext
258260
static_cast<DataAllocator::SubSpecificationType>(compile_time_hash("internal-dpl-aod-reader")),
259261
aodLifetime}},
260262
{},
261-
readers::AODReaderHelpers::rootFileReaderCallback(),
263+
AlgorithmSpec::dummyAlgorithm(),
262264
{ConfigParamSpec{"aod-file", VariantType::String, {"Input AOD file"}},
263265
ConfigParamSpec{"aod-reader-json", VariantType::String, {"json configuration file"}},
264266
ConfigParamSpec{"time-limit", VariantType::Int64, 0ll, {"Maximum run time limit in seconds"}},
@@ -417,6 +419,26 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext
417419

418420
// add the reader
419421
if (aodReader.outputs.empty() == false) {
422+
uv_lib_t supportLib;
423+
int result = 0;
424+
#ifdef __APPLE__
425+
result = uv_dlopen("libO2FrameworkAnalysisSupport.dylib", &supportLib);
426+
#else
427+
result = uv_dlopen("libO2FrameworkAnalysisSupport.so", &supportLib);
428+
#endif
429+
if (result == -1) {
430+
LOG(ERROR) << uv_dlerror(&supportLib);
431+
}
432+
void* callback = nullptr;
433+
DPLPluginHandle* (*dpl_plugin_callback)(DPLPluginHandle*);
434+
435+
result = uv_dlsym(&supportLib, "dpl_plugin_callback", (void**)&dpl_plugin_callback);
436+
if (result == -1) {
437+
LOG(ERROR) << uv_dlerror(&supportLib);
438+
}
439+
DPLPluginHandle* pluginInstance = dpl_plugin_callback(nullptr);
440+
AlgorithmPlugin* creator = PluginManager::getByName<AlgorithmPlugin>(pluginInstance, "ROOTFileReader");
441+
aodReader.algorithm = creator->create();
420442
aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"});
421443
extraSpecs.push_back(timePipeline(aodReader, ctx.options().get<int64_t>("readers")));
422444
auto concrete = DataSpecUtils::asConcreteDataMatcher(aodReader.inputs[0]);

0 commit comments

Comments
 (0)