Loading...
Searching...
No Matches
ForEachImage.h
Go to the documentation of this file.
1#pragma once
2// Bounded thread pool for per-image work (NEB image forces).
3
4#include <algorithm>
5#include <atomic>
6#include <exception>
7#include <mutex>
8#include <thread>
9#include <vector>
10
11namespace eonc {
12
13// Calls work(1) .. work(n) on at most hardware_concurrency() threads. Each
14// thread takes the next index until none are left, so a band with more images
15// than cores does not oversubscribe. The first exception thrown by any call is
16// rethrown here after every thread has joined; the remaining indices are still
17// evaluated. std::thread rather than std::jthread: Apple Clang's libc++ does
18// not ship the latter.
19template <typename Work> inline void forEachImage(long n, Work &&work) {
20 if (n <= 0)
21 return;
22 const long hw =
23 std::max(1L, static_cast<long>(std::thread::hardware_concurrency()));
24 const long nThreads = std::min(n, hw);
25 std::atomic<long> next{1};
26 std::exception_ptr failure;
27 std::mutex failureMutex;
28 auto worker = [&] {
29 for (long i = next.fetch_add(1); i <= n; i = next.fetch_add(1)) {
30 try {
31 work(i);
32 } catch (...) {
33 std::lock_guard<std::mutex> lock(failureMutex);
34 if (!failure)
35 failure = std::current_exception();
36 }
37 }
38 };
39 std::vector<std::thread> threads;
40 threads.reserve(static_cast<size_t>(nThreads - 1));
41 try {
42 for (long t = 1; t < nThreads; t++)
43 threads.emplace_back(worker);
44 } catch (...) {
45 // Thread creation failed; the threads already running finish the band.
46 std::lock_guard<std::mutex> lock(failureMutex);
47 if (!failure)
48 failure = std::current_exception();
49 }
50 worker();
51 for (auto &t : threads)
52 t.join();
53 if (failure)
54 std::rethrow_exception(failure);
55}
56
57} // namespace eonc
RAII resource manager for the ARTn C library with global synchronization.
void forEachImage(long n, Work &&work)