Skip to content

Refactor backend library loading and rename Arrow suffix to PodioArrow - #999

Open
arnavdham wants to merge 8 commits into
AIDASoft:masterfrom
arnavdham:feature/arrow-dynamic-loading
Open

Refactor backend library loading and rename Arrow suffix to PodioArrow#999
arnavdham wants to merge 8 commits into
AIDASoft:masterfrom
arnavdham:feature/arrow-dynamic-loading

Conversation

@arnavdham

@arnavdham arnavdham commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Make Arrow mapper registration follow the same runtime-loading model as SIO, while refactoring the underlying library loading logic for both backends into a generalized, thread-safe loader.

  • Consolidates dynamic library loading into a single podio::utilities::BackendLibraryLoader used by both SIO and Arrow.
  • Ensures the library loader initialization is strictly thread-safe using C++11 magic statics, fixing potential data races.
  • Renames the generated Arrow plugin suffix from Arrow to PodioArrow (e.g. libedm4hepPodioArrow.so) to avoid potential clashes with libarrow.so during dynamic LD_LIBRARY_PATH scanning.

BEGINRELEASENOTES

  • Refactoring: Generalized dynamic library loading for I/O backends into a shared, thread-safe BackendLibraryLoader.
  • Change: Changed the library suffix generated by PODIO_ADD_ARROW from Arrow to PodioArrow to prevent dynamic loading collisions. Downstream projects will need to update their CMake aliases accordingly.
  • Fix: Arrow mapper libraries are now loaded dynamically at registry lookup time instead of relying on linker side-effects.

ENDRELEASENOTES

Comment on lines +24 to +25
* Registration is expected to happen during shared library loading/startup,
* before worker threads query the registry. After registration the registry is

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit misleading, the loading happens lazily on getConverter/getReader calls, so users must not use those in multi-threaded context.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this initialization is thread safe. Once lazy loading is complete MT is completely fine.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so it does matter when lazy loading happens. Races can occur if several threads start up and simultaneously try to initialize reader.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can discuss this in our meet but I think the arrowConverterLibraryLoader will only be initialsed once and as it is a local static variable it is thread safe. https://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense now. We agreed, the text can be improved to specify which library this sentence is talking about (libpodioArrow or libedm4hepArrow etc)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better comment added.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one is not correct, libpodioArrow.so is not a data model, so registry does not get updated when that one is loaded (it may contain the main symbol for the registry).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I just remove libpodioArrow.so as example?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thank you.

Comment thread src/ArrowConverterRegistry.cc Outdated

for (auto& lib : fs::directory_iterator(dir)) {
const auto filename = lib.path().filename().string();
if (filename.find("Arrow") != std::string::npos && filename.find("libarrow") == std::string::npos) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can it be either one? libarrow must come from arrow-cpp, which libpodioArrow is meant to link against, so it gets loaded as a dependency.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe check for ArrowMapper?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe, we agreed not to name the library Mapper, but simply Arrow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only checking for Arrow now.

Comment thread src/ArrowConverterRegistry.cc Outdated
Comment thread src/ArrowConverterRegistry.cc Outdated
Comment thread src/ArrowTypeRegistry.cc Outdated
@arnavdham
arnavdham requested a review from jmcarcell August 8, 2026 06:25
Comment thread src/ArrowConverterRegistry.cc Outdated

for (auto& lib : fs::directory_iterator(dir)) {
const auto filename = lib.path().filename().string();
if (filename.find("Arrow") != std::string::npos) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ok because of the capital A; in a view the directories in LD_LIBRARY_PATH may contain lots of libraries, and in particular libarrow.so, which doesn't match. But if there were multiple podio datamodels that means all the arrow libraries are opened. This is just a comment, since I had to think about this case and check which names are used in LCG stacks (there doesn't seem to be anything else with Arrow in their name in the top level folders).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also give this a slightly less generic suffix that is less likely to clash. That might require to change parts of the code gen in some cmake macro I would assume (without having checked).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to work, I think we will have to make changes downstream(edm4hep) as well so I would suggest keeping it as Arrow for simplicity.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more impactful improvement would be to load only a relevant library for the model(s) used in a file, which would likely also resolve this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think getting the library would be easy. Also we don't have files right now as everything is in memory

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this to work, I think we will have to make changes downstream(edm4hep) as well so I would suggest keeping it as Arrow for simplicity.

I don't think there has been a podio release yet with the functionality, so the most we would break at this point is some nightly builds for which we don't make too many guarantees. So if we want to make this change, now is essentially the time.

A more impactful improvement would be to load only a relevant library for the model(s) used in a file, which would likely also resolve this.

There is generally room for improvement here, see also: #403 I don't think we need to worry too much about performance here in general. There have been some interesting interactions with cvmfs and root dictionary loading, but realistically people will have only a (very) small number of datamodels to consider and even the fallback of going through LD_LIBRARY_PATH will only do a scan for matching libraries once.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there has been a podio release yet with the functionality, so the most we would break at this point is some nightly builds for which we don't make too many guarantees. So if we want to make this change, now is essentially the time.

Would you like to suggest a name? We had ArrowMapper which I did not like at the time.

There is generally room for improvement here, see also: #403 I don't think we need to worry too much about performance here in general.

Right, my point was that loading an irrelevant Arrow library is on the same scale of a problem.

I don't think getting the library would be easy. Also we don't have files right now as everything is in memory

A library name can go to Table metadata.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like to suggest a name? We had ArrowMapper which I did not like at the time.

I don't have a strong opinion on this one. One could even do PodioArrow (or any variation of that) to be very specific. Given that all the builds and environment building are pretty well automated, I don't think too many people will look at the generated library names in any case.

@jmcarcell

Copy link
Copy Markdown
Member

Any more comments @veprbl? Looks good to me as it is

@tmadlener tmadlener left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least the library loading part looks like an almost copy of what we do for SIO (i.e. SIOBlockLibraryLoader is essentially ArrowConverterLibraryLoader with some string replacements). Can those two be generalized into one configurable library loader that works for both? Configuration would probably take the env variable name, a library pattern and probably some designator for the log messages.

Comment thread src/ArrowConverterRegistry.cc Outdated

for (auto& lib : fs::directory_iterator(dir)) {
const auto filename = lib.path().filename().string();
if (filename.find("Arrow") != std::string::npos) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also give this a slightly less generic suffix that is less likely to clash. That might require to change parts of the code gen in some cmake macro I would assume (without having checked).

@arnavdham

Copy link
Copy Markdown
Contributor Author

At least the library loading part looks like an almost copy of what we do for SIO (i.e. SIOBlockLibraryLoader is essentially ArrowConverterLibraryLoader with some string replacements). Can those two be generalized into one configurable library loader that works for both? Configuration would probably take the env variable name, a library pattern and probably some designator for the log messages.

I could do this.

@arnavdham
arnavdham requested a review from tmadlener August 10, 2026 16:45

@tmadlener tmadlener left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pulling out the generic bits, looks good to me.

I am wondering whether we want the fallback to LD_LIBRARY_PATH for Arrow as well. For SIO it's essentially mainly there because we need it for some backwards compatibility, which is not really a concern yet for Arrow.

Comment thread include/podio/utilities/LibraryLoader.h Outdated
Comment thread src/ArrowConverterRegistry.cc Outdated
Comment thread src/SIOBlock.cc Outdated
@arnavdham

Copy link
Copy Markdown
Contributor Author

Thanks for pulling out the generic bits, looks good to me.

I am wondering whether we want the fallback to LD_LIBRARY_PATH for Arrow as well. For SIO it's essentially mainly there because we need it for some backwards compatibility, which is not really a concern yet for Arrow.

So no scanning if podio_arrow_path is not set? Maybe bool allowFallbackToLDLibraryPath can be added to the constructor of BackendLibraryLoader set to true for SIO and false for arrow? @veprbl @tmadlener

@tmadlener

Copy link
Copy Markdown
Collaborator

So no scanning if podio_arrow_path is not set?

Yes, exactly. I have no strong opinion here.

Maybe bool allowFallbackToLDLibraryPath can be added to the constructor of BackendLibraryLoader set to true for SIO and false for arrow?

If we decide to not have the fallback for arrow that would be the technical way of doing it probably.

@veprbl

veprbl commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

I'm not in favor of removing LD_LIBRARY_PATH. If anything, non-standard PODIO_ARROW_PATH should be retired.

@arnavdham

Copy link
Copy Markdown
Contributor Author

I'm not in favor of removing LD_LIBRARY_PATH. If anything, non-standard PODIO_ARROW_PATH should be retired.

So I keep the current implementation and just change Arrow to PodioArrow to avoid clashes and similar change in edm4hep and its should be fine? @veprbl @tmadlener

@veprbl

veprbl commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

I'm not in favor of removing LD_LIBRARY_PATH. If anything, non-standard PODIO_ARROW_PATH should be retired.

So I keep the current implementation and just change Arrow to PodioArrow to avoid clashes and similar change in edm4hep and its should be fine? @veprbl @tmadlener

Let's do that, please.

@tmadlener

Copy link
Copy Markdown
Collaborator

So I keep the current implementation and just change Arrow to PodioArrow to avoid clashes and similar change in edm4hep and its should be fine?

I don't think any changes in edm4hep would be necessary? Shouldn't a corresponding change in the PODIO_ADD_ARROW macro already propagate through all the machinery transparently?

@arnavdham

Copy link
Copy Markdown
Contributor Author

So I keep the current implementation and just change Arrow to PodioArrow to avoid clashes and similar change in edm4hep and its should be fine?

I don't think any changes in edm4hep would be necessary? Shouldn't a corresponding change in the PODIO_ADD_ARROW macro already propagate through all the machinery transparently?

https://github.com/key4hep/EDM4hep/pull/502/changes the changes made here would have to be renamed from edm4hepArrow to edm4hepPodioArrow

@arnavdham

Copy link
Copy Markdown
Contributor Author

Have renamed to PodioArrow

@tmadlener

Copy link
Copy Markdown
Collaborator

Ah yes, missed the fact that we use the target names directly there. In that case this would need a change in EDM4hep as well.

@arnavdham

Copy link
Copy Markdown
Contributor Author

key4hep/EDM4hep#509 Change in edm4hep

@arnavdham arnavdham changed the title Load Arrow mapper libraries on registry lookup Refactor backend library loading and rename Arrow suffix to PodioArrow Aug 11, 2026
Comment on lines 19 to 39
@@ -28,11 +30,16 @@ void ArrowConverterRegistry::registerReader(const std::string& typeName, BufferR
}

ArrowConverterRegistry::BufferReaderFunc ArrowConverterRegistry::getReader(const std::string& typeName) const {
loadArrowLibraries();
auto it = m_readerRegistry.find(typeName);
if (it != m_readerRegistry.end()) {
return it->second;
}
return nullptr;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being rather slow here, but IIUC, this might happen on multiple threads concurrently here. The getReader (potentially also the getConverter, but there I haven't fully understood the logic yet, I think) is called in a section which podio assumes is safe to be called from multiple threads. In SIO we load the libraries during the construction of the reader. This can't be placed into the ArrowConverterRegistry constructor because we will actually try to call into that from the loaded libraries, I think. But maybe there is some place where we have slightly less potential problems with trying to load from multiple threads.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the question we had too. In the end, it seems like Arnav's implementation relies on a guarantee that static local variable initialization is thread safe in C++.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, good point. I missed the static assignment in the loadXYZLibraries. In that case I think threading should pose no issues and, I think this also gives us a guarantee of happening exactly once.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ig this can be merged now if there are no more comments?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants