From cf983db131bd1b033f63d3a49b84837a674214e8 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 3 Sep 2026 11:30:20 -0400 Subject: [PATCH] BUG: Release ImageIOFactory mutex before probing candidate IOs CreateImageIO() held createImageIOMutex for its entire body, including the CanReadFile()/CanWriteFile() probing loop over every registered IO. Several IO CanReadFile/CanWriteFile implementations open and parse the file, so this serialized every ReadImage/WriteImage call in the process, even for unrelated files, across all image formats. Under concurrent reads of many distinct files from many threads, this collapses effective throughput and can look indistinguishable from a hang once enough concurrent demand builds up. Narrow the lock to only the ObjectFactoryBase::CreateAllInstance call that builds the candidate IO list; release it before probing files, so independent reads/writes on different files no longer serialize. This mirrors the concurrency portion of b5def871167aa14d9886ba63e2315c237524d715 (ENH: Two-phase ImageIOFactory dispatch via extension check, main), backported here as a minimal, behavior-preserving fix for the release branch without the extension-dispatch API changes. --- .../IO/ImageBase/src/itkImageIOFactory.cxx | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Modules/IO/ImageBase/src/itkImageIOFactory.cxx b/Modules/IO/ImageBase/src/itkImageIOFactory.cxx index 0e1694f5d43..564839948b3 100644 --- a/Modules/IO/ImageBase/src/itkImageIOFactory.cxx +++ b/Modules/IO/ImageBase/src/itkImageIOFactory.cxx @@ -34,20 +34,26 @@ ImageIOFactory::CreateImageIO(const char * path, IOFileModeEnum mode) { std::list possibleImageIO; - const std::lock_guard lockGuard(createImageIOMutex); - - for (auto & allobject : ObjectFactoryBase::CreateAllInstance("itkImageIOBase")) { - auto * io = dynamic_cast(allobject.GetPointer()); - if (io) - { - possibleImageIO.emplace_back(io); - } - else + // Lock the mutex while creating all instances of ImageIOBase, to + // ensure thread safety during intialization of third-party libraries. + const std::lock_guard lockGuard(createImageIOMutex); + + for (auto & allobject : ObjectFactoryBase::CreateAllInstance("itkImageIOBase")) { - std::cerr << "Error ImageIO factory did not return an ImageIOBase: " << allobject->GetNameOfClass() << std::endl; + auto * io = dynamic_cast(allobject.GetPointer()); + if (io) + { + possibleImageIO.emplace_back(io); + } + else + { + std::cerr << "Error ImageIO factory did not return an ImageIOBase: " << allobject->GetNameOfClass() + << std::endl; + } } } + for (auto & k : possibleImageIO) { if (mode == IOFileModeEnum::ReadMode)