3333#include < jxl/thread_parallel_runner.h>
3434#include < jxl/thread_parallel_runner_cxx.h>
3535
36- #include < istream>
3736#include < limits>
3837#include < span>
3938#include < vector>
@@ -172,13 +171,12 @@ Task<vector<ImageData>> JxlImageLoader::load(
172171 // co-routine. I.e. we need to synchronously wait for the work to finish, which could deadlock the global threadpool. Other mitigation
173172 // strategies involve temporarily creating and removing extra threads from the global threadpool (which tev previously implemented),
174173 // but this approach here scales better to huge numbers of images (n-cores extra threads instead of n-images extra threads).
175- static auto jxlPool = ThreadPool ();
176174
177175 const auto * runnerDataPtr = static_cast <RunnerData*>(runnerOpaque);
178176
179177 const uint32_t range = endRange - startRange;
180178 const uint32_t numTasks = std::min (
181- jxlPool .nTasks (
179+ ThreadPool::global () .nTasks (
182180 0u ,
183181 range,
184182 numeric_limits<uint32_t >::max () // Max parallelism up to range tasks & hardware concurrency
@@ -191,26 +189,23 @@ Task<vector<ImageData>> JxlImageLoader::load(
191189 return initResult;
192190 }
193191
194- jxlPool
195- .parallelFor (
196- 0u ,
197- numTasks,
198- numeric_limits<uint32_t >::max (), // Maximum parallelism up to numTasks threads
199- [&](uint32_t i) {
200- const uint32_t taskStart = startRange + (range * i / numTasks);
201- const uint32_t taskEnd = startRange + (range * (i + 1 ) / numTasks);
202- TEV_ASSERT (taskStart != taskEnd, " Should not produce tasks with empty range." );
203-
204- for (uint32_t j = taskStart; j < taskEnd; ++j) {
205- func (jpegxlOpaque, j, (uint32_t )i);
206- }
207- },
208- runnerDataPtr->priority
209- )
210- // The synchronous parallel for loop is janky, because it doesn't follow the coroutine paradigm. But it is the only way to
211- // get the thread pool to cooperate with the JXL API that expects a non-coroutine function here. We will offload the
212- // JxlImageLoader::load() function into a wholly separate thread to avoid blocking the thread pool as a consequence.
213- .get ();
192+ // Since the JXL API's contract doesn't let us async/await, call the synchronous variant of parallelFor, which will block this
193+ // thread by running other thread pool tasks until the parallel for is done.
194+ ThreadPool::global ().parallelForSync (
195+ 0u ,
196+ numTasks,
197+ numeric_limits<uint32_t >::max (), // Maximum parallelism up to numTasks threads
198+ [&](uint32_t i) {
199+ const uint32_t taskStart = startRange + (range * i / numTasks);
200+ const uint32_t taskEnd = startRange + (range * (i + 1 ) / numTasks);
201+ TEV_ASSERT (taskStart != taskEnd, " Should not produce tasks with empty range." );
202+
203+ for (uint32_t j = taskStart; j < taskEnd; ++j) {
204+ func (jpegxlOpaque, j, (uint32_t )i);
205+ }
206+ },
207+ runnerDataPtr->priority
208+ );
214209
215210 return 0 ;
216211 };
0 commit comments