1212#include < memory>
1313#include < mutex>
1414#include < sstream>
15+ #include < stdexcept>
1516#include < utility> // std::pair
17+ #include < vector>
1618
1719#include " adios2/common/ADIOSMacros.h"
1820
@@ -592,14 +594,27 @@ Engine &IO::Open(const std::string &name, const Mode mode, helper::Comm comm, co
592594 std::transform (engineTypeLC.begin (), engineTypeLC.end (), engineTypeLC.begin (), ::tolower);
593595 }
594596
595- /* Second step in handling virtual engines */
596- /* BPFile for read needs to use BP5, BP4, or BP3 depending on the file's
597- * version
598- */
599- if ((engineTypeLC == " file" || engineTypeLC == " bpfile" || engineTypeLC == " bp" ||
600- isDefaultEngine))
597+ // Engine selection probes the filesystem on rank 0 only and broadcasts the
598+ // chosen engine type, so a missing target fails on every rank instead of a
599+ // lone rank-0 throw that hangs the others.
600+ const bool isVirtualFile = (engineTypeLC == " file" || engineTypeLC == " bpfile" ||
601+ engineTypeLC == " bp" || engineTypeLC == " filestream" );
602+
603+ if (isDefaultEngine || isVirtualFile)
601604 {
602- if (helper::EndsWith (name, " .h5" , false ))
605+ if (engineTypeLC == " filestream" )
606+ {
607+ // FileStream streams BP4/BP5 file output only (not DAOS, timeseries,
608+ // etc.). A not-yet-created stream is normal, so BPVersionLocal returns
609+ // '4' for a missing path and the engine waits rather than erroring.
610+ char version = ' 4' ;
611+ if (comm.Rank () == 0 )
612+ {
613+ version = helper::BPVersionLocal (name);
614+ }
615+ engineTypeLC = std::string (" bp" ) + comm.BroadcastValue (version);
616+ }
617+ else if (helper::EndsWith (name, " .h5" , false ))
603618 {
604619 engineTypeLC = " hdf5" ;
605620 }
@@ -613,49 +628,66 @@ Engine &IO::Open(const std::string &name, const Mode mode, helper::Comm comm, co
613628 }
614629 else if ((mode_to_use == Mode::Read) || (mode_to_use == Mode::ReadRandomAccess))
615630 {
616- // Check if TarInfo parameter indicates a TAR file
617631 auto it = m_Parameters.find (" TarInfo" );
618632 if (it != m_Parameters.end ())
619633 {
634+ // TarInfo is a parameter (same on every rank), so guess directly.
620635 engineTypeLC = lf_GuessEngineFromTAR (it->second );
621636 }
622- else if (helper::IsDAOSDataset (name))
623- {
624- // Must precede the generic directory branch: a DAOS
625- // dataset is also a directory and contains an mmd.0,
626- // so BPVersion would otherwise mis-detect it as BP5.
627- engineTypeLC = " daos" ;
628- }
629- else if (adios2sys::SystemTools::FileIsDirectory (name))
630- {
631- char v = helper::BPVersion (name, comm, m_TransportsParameters);
632- engineTypeLC = " bp" ;
633- engineTypeLC.push_back (v);
634- }
635- else if (adios2sys::SystemTools::FileIsDirectory (name + " .tier0" ))
636- {
637- engineTypeLC = " mhs" ;
638- }
639637 else
640638 {
641- if (helper::EndsWith (name, " .bp" , false ))
642- {
643- engineTypeLC = " bp3" ;
644- }
645- else
639+ // Rank 0 picks the engine from the on-disk dataset; an empty
640+ // result means nothing exists at the location.
641+ std::vector<char > selected;
642+ if (comm.Rank () == 0 )
646643 {
647- /* We need to figure out the type of file
648- * from the file itself
649- */
650- if (helper::IsHDF5File (name, *this , comm, m_TransportsParameters))
644+ std::string sel;
645+ if (adios2sys::SystemTools::PathExists (name) ||
646+ adios2sys::SystemTools::PathExists (name + " .tier0" ))
651647 {
652- engineTypeLC = " hdf5" ;
648+ // DAOS must precede the directory branch: a DAOS dataset is
649+ // also a directory containing an mmd.0 and would mis-detect
650+ // as BP5.
651+ if (helper::IsDAOSDataset (name))
652+ {
653+ sel = " daos" ;
654+ }
655+ else if (adios2sys::SystemTools::FileIsDirectory (name))
656+ {
657+ sel = std::string (" bp" ) + helper::BPVersionLocal (name);
658+ }
659+ else if (adios2sys::SystemTools::FileIsDirectory (name + " .tier0" ))
660+ {
661+ sel = " mhs" ;
662+ }
663+ else if (helper::EndsWith (name, " .bp" , false ))
664+ {
665+ sel = " bp3" ;
666+ }
667+ else
668+ {
669+ sel = helper::IsHDF5FileLocal (name, *this , comm, m_TransportsParameters)
670+ ? " hdf5"
671+ : " bp3" ;
672+ }
653673 }
654- else
674+ selected.assign (sel.begin (), sel.end ());
675+ }
676+ comm.BroadcastVector (selected);
677+ if (selected.empty ())
678+ {
679+ // Built identically on every rank from 'name', so all ranks
680+ // throw together without broadcasting the message.
681+ std::string err = " Cannot determine the engine for reading \" " + name +
682+ " \" : nothing exists at that location." ;
683+ if (mode_to_use == Mode::Read)
655684 {
656- engineTypeLC = " bp3" ;
685+ err += " If it is a stream that has not been created yet, select "
686+ " the engine explicitly with IO::SetEngine()." ;
657687 }
688+ helper::Throw<std::runtime_error>(" Core" , " IO" , " Open" , err);
658689 }
690+ engineTypeLC.assign (selected.begin (), selected.end ());
659691 }
660692 }
661693 else
@@ -665,29 +697,6 @@ Engine &IO::Open(const std::string &name, const Mode mode, helper::Comm comm, co
665697 }
666698 }
667699
668- /* Note: Mismatch between BP4/BP5 writer and FileStream reader is not
669- handled if writer has not created the directory yet, when FileStream
670- falls back to default (BP4) */
671- if (engineTypeLC == " filestream" )
672- {
673- if (helper::EndsWith (name, " .ats" , false ))
674- {
675- engineTypeLC = " timeseries" ;
676- }
677- else if (helper::IsDAOSDataset (name))
678- {
679- engineTypeLC = " daos" ;
680- }
681- else
682- {
683- char v = helper::BPVersion (name, comm, m_TransportsParameters);
684- engineTypeLC = " bp" ;
685- engineTypeLC.push_back (v);
686- }
687- // std::cout << "Engine " << engineTypeLC << " selected for FileStream"
688- // << std::endl;
689- }
690-
691700 // For the inline engine, there must be exactly 1 reader, and exactly 1
692701 // writer.
693702 if (engineTypeLC == " inline" )
0 commit comments