Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Framework/Core/include/Framework/ExpirationHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "Framework/Lifetime.h"
#include "Framework/RoutingIndices.h"
#include "Framework/DataDescriptorMatcher.h"
#include "Framework/InputSpan.h"
#include <cstdint>
#include <functional>

Expand All @@ -25,11 +26,12 @@ struct PartRef;
struct ServiceRegistry;
struct TimesliceIndex;
struct TimesliceSlot;
struct InputRecord;

struct ExpirationHandler {
using Creator = std::function<TimesliceSlot(TimesliceIndex&)>;
/// Callback type to check if the record must be expired
using Checker = std::function<bool(ServiceRegistry&, uint64_t timestamp)>;
using Checker = std::function<bool(ServiceRegistry&, uint64_t timestamp, InputSpan const& record)>;
/// Callback type to actually materialise a given record
using Handler = std::function<void(ServiceRegistry&, PartRef& expiredInput, data_matcher::VariableContext& variables)>;

Expand Down
11 changes: 11 additions & 0 deletions Framework/Core/include/Framework/InputRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ class InputRecord
public:
using DataHeader = o2::header::DataHeader;

// Typesafe position inside a record of an input.
// Multiple routes by which the input gets in this
// position are multiplexed.
struct InputPos {
size_t index;
constexpr static size_t INVALID = -1LL;
};

InputRecord(std::vector<InputRoute> const& inputs,
InputSpan& span,
ServiceRegistry&);
Expand Down Expand Up @@ -179,6 +187,9 @@ class InputRecord
};

int getPos(const char* name) const;
[[nodiscard]] static InputPos getPos(std::vector<InputRoute> const& routes, ConcreteDataMatcher matcher);
[[nodiscard]] static DataRef getByPos(std::vector<InputRoute> const& routes, InputSpan const& span, int pos, int part = 0);

[[nodiscard]] int getPos(const std::string& name) const;

[[nodiscard]] DataRef getByPos(int pos, int part = 0) const;
Expand Down
5 changes: 5 additions & 0 deletions Framework/Core/include/Framework/LifetimeHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "Framework/ExpirationHandler.h"
#include "Framework/PartRef.h"
#include "InputRoute.h"

#include <chrono>
#include <functional>
Expand Down Expand Up @@ -50,6 +51,10 @@ struct LifetimeHelpers {
/// Callback which always expires records. To be used with, e.g.
/// Lifetime::Transient.
static ExpirationHandler::Checker expireAlways();
/// Callback which expires records based on the content of the record.
/// To be used with, e.g. Lifetime::Optional.
static ExpirationHandler::Checker expireIfPresent(std::vector<InputRoute> const& schema, ConcreteDataMatcher matcher);

/// Callback which expires records with the rate given by @a period, in
/// microseconds.
static ExpirationHandler::Checker expireTimed(std::chrono::microseconds period);
Expand Down
30 changes: 29 additions & 1 deletion Framework/Core/src/DataRelayer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,35 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector<Expira
badSlot++;
continue;
}
if (expirator.checker(services, timestamp.value) == false) {

auto getPartialRecord = [&cache = mCache, numInputTypes = mDistinctRoutesIndex.size()](int li) -> gsl::span<MessageSet const> {
auto offset = li * numInputTypes;
assert(cache.size() >= offset + numInputTypes);
auto const start = cache.data() + offset;
auto const end = cache.data() + offset + numInputTypes;
return {start, end};
};

auto partial = getPartialRecord(ti);
// TODO: get the data ref from message model
auto getter = [&partial](size_t idx, size_t part) {
if (partial[idx].size() > 0 && partial[idx].header(part).get()) {
auto header = partial[idx].header(part).get();
auto payload = partial[idx].payload(part).get();
return DataRef{nullptr,
reinterpret_cast<const char*>(header->GetData()),
reinterpret_cast<char const*>(payload ? payload->GetData() : nullptr),
payload ? payload->GetSize() : 0};
}
return DataRef{};
};
auto nPartsGetter = [&partial](size_t idx) {
return partial[idx].size();
};
InputSpan span{getter, nPartsGetter, static_cast<size_t>(partial.size())};
// Setup the input span

if (expirator.checker(services, timestamp.value, span) == false) {
checkerDenied++;
continue;
}
Expand Down
12 changes: 6 additions & 6 deletions Framework/Core/src/DeviceSpecHelpers.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ struct ExpirationHandlerHelpers {
return [](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::expireNever(); };
}

static RouteConfigurator::ExpirationConfigurator expiringTransientConfigurator(InputSpec const& matcher)
static RouteConfigurator::ExpirationConfigurator expiringTransientConfigurator(InputSpec const&)
{
return [](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::fetchFromObjectRegistry(); };
}
Expand All @@ -321,23 +321,23 @@ struct ExpirationHandlerHelpers {
}

/// This will always exipire an optional record when no data is received.
static RouteConfigurator::DanglingConfigurator danglingOptionalConfigurator()
static RouteConfigurator::DanglingConfigurator danglingOptionalConfigurator(std::vector<InputRoute> const& routes)
{
return [](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::expireAlways(); };
return [routes](DeviceState&, ConfigParamRegistry const&) { return LifetimeHelpers::expireIfPresent(routes, ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", 0}); };
}

/// When the record expires, simply create a dummy entry.
static RouteConfigurator::ExpirationConfigurator expiringOptionalConfigurator(InputSpec const& spec, std::string const& sourceChannel)
{
try {
ConcreteDataMatcher concrete = DataSpecUtils::asConcreteDataMatcher(spec);
return [concrete, sourceChannel](DeviceState&, ConfigParamRegistry const& config) {
return [concrete, sourceChannel](DeviceState&, ConfigParamRegistry const&) {
return LifetimeHelpers::dummy(concrete, sourceChannel);
};
} catch (...) {
ConcreteDataTypeMatcher dataType = DataSpecUtils::asConcreteDataTypeMatcher(spec);
ConcreteDataMatcher concrete{dataType.origin, dataType.description, 0xdeadbeef};
return [concrete, sourceChannel](DeviceState&, ConfigParamRegistry const& config) {
return [concrete, sourceChannel](DeviceState&, ConfigParamRegistry const&) {
return LifetimeHelpers::dummy(concrete, sourceChannel);
};
// We copy the matcher to avoid lifetime issues.
Expand Down Expand Up @@ -849,7 +849,7 @@ void DeviceSpecHelpers::processInEdgeActions(std::vector<DeviceSpec>& devices,
route.configurator = {
.name = "optional",
.creatorConfigurator = ExpirationHandlerHelpers::createOptionalConfigurator(),
.danglingConfigurator = ExpirationHandlerHelpers::danglingOptionalConfigurator(),
.danglingConfigurator = ExpirationHandlerHelpers::danglingOptionalConfigurator(consumerDevice.inputs),
.expirationConfigurator = ExpirationHandlerHelpers::expiringOptionalConfigurator(inputSpec, sourceChannel)};
break;
default:
Expand Down
34 changes: 27 additions & 7 deletions Framework/Core/src/InputRecord.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,48 @@ int InputRecord::getPos(const char* binding) const
return -1;
}

InputRecord::InputPos InputRecord::getPos(std::vector<InputRoute> const& schema, ConcreteDataMatcher concrete)
{
size_t inputIndex = 0;
for (const auto& route : schema) {
if (route.timeslice != 0) {
continue;
}
if (DataSpecUtils::match(route.matcher, concrete)) {
return {inputIndex};
}
++inputIndex;
}
return InputPos{InputPos::INVALID};
}

int InputRecord::getPos(std::string const& binding) const
{
return this->getPos(binding.c_str());
}

DataRef InputRecord::getByPos(int pos, int part) const
{
if (pos >= mSpan.size() || pos < 0) {
return InputRecord::getByPos(mInputsSchema, mSpan, pos, part);
}

DataRef InputRecord::getByPos(std::vector<InputRoute> const& schema, InputSpan const& span, int pos, int part)
{
if (pos >= (int)span.size() || pos < 0) {
throw runtime_error_f("Unknown message requested at position %d", pos);
}
if (part > 0 && part >= getNofParts(pos)) {
if (part > 0 && part >= (int)span.getNofParts(pos)) {
throw runtime_error_f("Invalid message part index at %d:%d", pos, part);
}
if (pos >= mInputsSchema.size()) {
if (pos >= (int)schema.size()) {
throw runtime_error_f("Unknown schema at position %d", pos);
}
auto ref = mSpan.get(pos, part);
auto ref = span.get(pos, part);
auto inputIndex = 0;
auto schemaIndex = 0;
for (size_t i = 0; i < mInputsSchema.size(); ++i) {
for (size_t i = 0; i < schema.size(); ++i) {
schemaIndex = i;
auto& route = mInputsSchema[i];
auto& route = schema[i];
if (route.timeslice != 0) {
continue;
}
Expand All @@ -88,7 +108,7 @@ DataRef InputRecord::getByPos(int pos, int part) const
}
++inputIndex;
}
ref.spec = &mInputsSchema[schemaIndex].matcher;
ref.spec = &schema[schemaIndex].matcher;
return ref;
}

Expand Down
17 changes: 14 additions & 3 deletions Framework/Core/src/LifetimeHelpers.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "Framework/TimesliceIndex.h"
#include "Framework/VariableContextHelpers.h"
#include "Framework/DataTakingContext.h"
#include "Framework/InputRecord.h"

#include "Headers/DataHeader.h"
#include "Headers/DataHeaderHelpers.h"
Expand Down Expand Up @@ -136,12 +137,22 @@ ExpirationHandler::Creator LifetimeHelpers::timeDrivenCreation(std::chrono::micr

ExpirationHandler::Checker LifetimeHelpers::expireNever()
{
return [](ServiceRegistry&, int64_t) -> bool { return false; };
return [](ServiceRegistry&, int64_t, InputSpan const&) -> bool { return false; };
}

ExpirationHandler::Checker LifetimeHelpers::expireAlways()
{
return [](ServiceRegistry&, int64_t) -> bool { return true; };
return [](ServiceRegistry&, int64_t, InputSpan const&) -> bool { return true; };
}

ExpirationHandler::Checker LifetimeHelpers::expireIfPresent(std::vector<InputRoute> const& routes, ConcreteDataMatcher matcher)
{
// find the position of the first route that matches the matcher
auto pos = InputRecord::getPos(routes, matcher);
return [pos, routes](ServiceRegistry&, int64_t, InputSpan const& span) -> bool {
auto ref = InputRecord::getByPos(routes, span, pos.index, 0);
return ref.header != nullptr;
};
}

ExpirationHandler::Creator LifetimeHelpers::uvDrivenCreation(int requestedLoopReason, DeviceState& state)
Expand Down Expand Up @@ -200,7 +211,7 @@ ExpirationHandler::Checker LifetimeHelpers::expireTimed(std::chrono::microsecond
{
auto start = getCurrentTime();
auto last = std::make_shared<decltype(start)>(start);
return [last, period](ServiceRegistry&, int64_t) -> bool {
return [last, period](ServiceRegistry&, int64_t, InputSpan const&) -> bool {
auto current = getCurrentTime();
auto delta = current - *last;
if (delta > period.count()) {
Expand Down