Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
dispatch-pool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Author: Liam White
4 * Copyright (C) 2024 Authors
5 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
6 */
7
8#include "dispatch-pool.h"
9
10namespace Inkscape {
11
13{
14 int const num_threads = std::max(size, 1) - 1;
15
16 _threads.reserve(num_threads);
17
18 for (int i = 0; i < num_threads; ++i) {
19 // local_id of created threads is offset by 1 to allow calling thread to always be 0
20 _threads.emplace_back([i, this] { thread_func(local_id{i + 1}); });
21 }
22}
23
25{
26 // TODO C++20: this would be completely trivial with jthread
27 // TODO C++20: dispatch_pool::~dispatch_pool() = default;
28 {
29 std::scoped_lock lk(_lock);
30 _shutdown = true;
31 }
32
33 _available_cv.notify_all();
34
35 for (auto &thread : _threads) {
36 thread.join();
37 }
38}
39
40void dispatch_pool::dispatch(int count, dispatch_func function)
41{
42 std::scoped_lock lk(_dispatch_lock);
43 std::unique_lock lk2(_lock);
44
47 _target_work = global_id{count};
48 _function = std::move(function);
49
50 // Execute the caller's batch, and signal to the next waiting thread
51 execute_batch(lk2, local_id{}, size());
52
53 // Wait for other threads to finish
54 _completed_cv.wait(lk2, [&] { return _completed_work == _target_work; });
55
56 // Release any extra memory held by the function
57 _function = {};
58}
59
61{
62 int const thread_count = size();
63
64 std::unique_lock lk(_lock);
65
66 // TODO C++20: no need for _shutdown member once stop_token is available
67 // TODO C++20: while (_cv.wait(lk, stop_token, [&] { ... }))
68 while (true) {
69 _available_cv.wait(lk, [&] { return _shutdown || _available_work < _target_work; });
70
71 if (_shutdown) {
72 // When shutdown is requested, stop immediately
73 return;
74 }
75
76 // Otherwise, execute the batch
77 execute_batch(lk, id, thread_count);
78 }
79}
80
81void dispatch_pool::execute_batch(std::unique_lock<std::mutex> &lk, local_id id, int thread_count)
82{
83 // Determine how much work to take
84 global_id const batch_size = (_target_work + thread_count - 1) / thread_count;
86 global_id const end = std::min(start + batch_size, _target_work);
87
88 // Take that much work
90
91 // Unlock and begin executing the function
92 {
93 lk.unlock();
94
95 // Now that the lock is released, potentially signal work availability
96 // to the next waiting thread
97 _available_cv.notify_one();
98
99 // Execute the function
100 for (global_id index = start; index < end; index++) {
101 _function(index, id);
102 }
103
104 lk.lock();
105 }
106
107 // Signal completion
109
111 _completed_cv.notify_one();
112 }
113}
114
115} // namespace Inkscape
116
117/*
118 Local Variables:
119 mode:c++
120 c-file-style:"stroustrup"
121 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
122 indent-tabs-mode:nil
123 fill-column:99
124 End:
125*/
126// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
std::function< void(global_id, local_id)> dispatch_func
void execute_batch(std::unique_lock< std::mutex > &lk, local_id id, int thread_count)
std::condition_variable _completed_cv
void thread_func(local_id id)
void dispatch(int count, dispatch_func function)
std::condition_variable _available_cv
std::vector< std::thread > _threads
Geom::Point start
Geom::Point end
Helper class to stream background task notifications as a series of messages.
int index