Skip to content

Commit 73ef349

Browse files
committed
[ntuple] automatic evolution in and out std::atomic
Allow for std::atomic<T> --> T (or compatible) automatic conversion and vice versa.
1 parent 50551c3 commit 73ef349

File tree

5 files changed

+52
-3
lines changed

5 files changed

+52
-3
lines changed

tree/ntuple/inc/ROOT/RNTupleDescriptor.hxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public:
153153
/// The dictionary does not need to be available for this method.
154154
/// Needs the full descriptor to look up sub fields.
155155
bool IsCustomEnum(const RNTupleDescriptor &desc) const;
156+
bool IsStdAtomic() const;
156157
};
157158

158159
// clang-format off

tree/ntuple/src/RField.cxx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,8 +1099,11 @@ void ROOT::RAtomicField::ReconcileOnDiskField(const RNTupleDescriptor &desc)
10991099
static const std::vector<std::string> prefixes = {"std::atomic<"};
11001100

11011101
const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1102-
EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);
1103-
EnsureMatchingTypePrefix(fieldDesc, prefixes);
1102+
if (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0) {
1103+
EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);
1104+
} else {
1105+
fSubfields[0]->SetOnDiskId(GetOnDiskId());
1106+
}
11041107
}
11051108

11061109
std::vector<ROOT::RFieldBase::RValue> ROOT::RAtomicField::SplitValue(const RValue &value) const

tree/ntuple/src/RFieldBase.cxx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,13 @@ void ROOT::RFieldBase::ConnectPageSource(ROOT::Internal::RPageSource &pageSource
975975

976976
if (!fIsArtificial) {
977977
R__ASSERT(fOnDiskId != kInvalidDescriptorId);
978-
ReconcileOnDiskField(pageSource.GetSharedDescriptorGuard().GetRef());
978+
// Handle moving from on-disk std::atomic<T> to (compatible of) T in memory centrally because otherwise
979+
// we would need to handle it in each and every ReconcileOnDiskField()
980+
const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
981+
if (!dynamic_cast<RAtomicField *>(this) && desc.GetFieldDescriptor(GetOnDiskId()).IsStdAtomic()) {
982+
SetOnDiskId(desc.GetFieldDescriptor(GetOnDiskId()).GetLinkIds()[0]);
983+
}
984+
ReconcileOnDiskField(desc);
979985
}
980986

981987
for (auto &f : fSubfields) {

tree/ntuple/src/RNTupleDescriptor.cxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ bool ROOT::RFieldDescriptor::IsCustomEnum(const RNTupleDescriptor &desc) const
187187
desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
188188
}
189189

190+
bool ROOT::RFieldDescriptor::IsStdAtomic() const
191+
{
192+
if (fStructure != ROOT::ENTupleStructure::kPlain)
193+
return false;
194+
return (fTypeName.rfind("std::atomic<", 0) == 0);
195+
}
196+
190197
////////////////////////////////////////////////////////////////////////////////
191198

192199
bool ROOT::RColumnDescriptor::operator==(const RColumnDescriptor &other) const

tree/ntuple/test/ntuple_evolution_type.cxx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,35 @@ TEST(RNTupleEvolution, ArrayAsRVec)
251251
EXPECT_EQ(1, a(0)[0]);
252252
EXPECT_EQ(2, a(0)[1]);
253253
}
254+
255+
TEST(RNTupleEvolution, CheckAtomic)
256+
{
257+
FileRaii fileGuard("test_ntuple_evolution_check_atomic.root");
258+
{
259+
auto model = ROOT::RNTupleModel::Create();
260+
auto a = model->MakeField<std::atomic<std::int32_t>>("a");
261+
auto i = model->MakeField<std::int32_t>("i");
262+
auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
263+
264+
*a = 7;
265+
*i = 13;
266+
writer->Fill();
267+
}
268+
269+
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
270+
271+
auto v1 = reader->GetView<std::atomic<std::int64_t>>("a");
272+
auto v2 = reader->GetView<std::atomic<std::int64_t>>("i");
273+
auto v3 = reader->GetView<std::int64_t>("a");
274+
275+
try {
276+
reader->GetView<std::atomic<std::byte>>("a");
277+
FAIL() << "automatic evolution into an invalid atomic inner type should fail";
278+
} catch (const ROOT::RException &err) {
279+
EXPECT_THAT(err.what(), testing::HasSubstr("cannot be matched to its in-memory type"));
280+
}
281+
282+
EXPECT_EQ(7, v1(0));
283+
EXPECT_EQ(13, v2(0));
284+
EXPECT_EQ(7, v3(0));
285+
}

0 commit comments

Comments
 (0)