98 Iterator first, Iterator last, Callback &&callback,
99 const std::size_t parallelism = std::thread::hardware_concurrency(),
100 const std::size_t stack_size_bytes = 0) ->
void {
101 const auto effective_parallelism{(std::max)(parallelism, 1uz)};
110 if (effective_parallelism == 1 || std::next(first) == last) {
111 std::size_t cursor{1};
112 for (
auto iterator = first; iterator != last; ++iterator, ++cursor) {
113 callback(*iterator, effective_parallelism, cursor);
119 std::queue<Iterator> tasks;
120 for (
auto iterator = first; iterator != last; ++iterator) {
121 tasks.push(iterator);
124 std::mutex queue_mutex;
125 std::mutex exception_mutex;
127 auto effective_callback = std::forward<Callback>(callback);
129 std::exception_ptr exception =
nullptr;
130 auto handle_exception = [&exception_mutex,
131 &exception](std::exception_ptr pointer) {
132 std::lock_guard<std::mutex> lock{exception_mutex};
138 std::vector<std::thread> workers;
139 workers.reserve(effective_parallelism);
141 const auto total{tasks.size()};
146 auto worker_callable = [&tasks, &queue_mutex, &effective_callback,
147 &handle_exception, effective_parallelism, total] {
151 std::size_t cursor{0};
153 std::lock_guard<std::mutex> lock{queue_mutex};
157 iterator = tasks.front();
158 cursor = total - tasks.size() + 1;
161 effective_callback(*iterator, effective_parallelism, cursor);
164 handle_exception(std::current_exception());
171 for (std::size_t index = 0; index < effective_parallelism; ++index) {
172 auto *heap_function =
new std::function<void()>(worker_callable);
173 if (stack_size_bytes >
static_cast<std::size_t
>(UINT_MAX)) {
174 delete heap_function;
175 parallel_for_each_drain_and_join(tasks, queue_mutex, workers);
176 throw std::runtime_error(
177 "The requested stack size is too large for this platform");
180 auto raw_handle = _beginthreadex(
181 nullptr,
static_cast<unsigned>(stack_size_bytes),
182 ¶llel_for_each_windows_thread_start, heap_function, 0,
nullptr);
183 if (raw_handle == 0) {
184 delete heap_function;
185 parallel_for_each_drain_and_join(tasks, queue_mutex, workers);
186 throw std::runtime_error(
"Could not create thread");
189 HANDLE thread_handle =
reinterpret_cast<HANDLE
>(raw_handle);
190 workers.emplace_back([thread_handle] {
191 WaitForSingleObject(thread_handle, INFINITE);
192 CloseHandle(thread_handle);
196 for (std::size_t index = 0; index < effective_parallelism; ++index) {
200 pthread_attr_init(&attr);
201 if (stack_size_bytes > 0) {
202 pthread_attr_setstacksize(&attr, stack_size_bytes);
205 auto *heap_function =
new std::function<void()>(worker_callable);
206 pthread_t pthread_handle;
207 auto raw_handle = pthread_create(
208 &pthread_handle, &attr,
209 [](
void *arg) ->
void * {
210 auto *function_ptr =
static_cast<std::function<
void()
> *>(arg);
216 if (raw_handle != 0) {
217 pthread_attr_destroy(&attr);
218 delete heap_function;
219 parallel_for_each_drain_and_join(tasks, queue_mutex, workers);
220 throw std::runtime_error(
"Could not create thread");
222 workers.emplace_back(
223 [pthread_handle] { pthread_join(pthread_handle,
nullptr); });
224 pthread_attr_destroy(&attr);
228 for (
auto &worker_thread : workers) {
229 worker_thread.join();
233 std::rethrow_exception(exception);