Maze Builder Docs 7.5.6
Loading...
Searching...
No Matches
create2.h
Go to the documentation of this file.
1#ifndef CREATE_2_H
2#define CREATE_2_H
3
6
7#include <deque>
8#include <functional>
9#include <mutex>
10#include <sstream>
11#include <string>
12#include <thread>
13#include <vector>
14
17namespace mazes
18{
20 namespace detail
21 {
24 {
25 private:
27 struct work_item
28 {
29 unsigned int id;
30
31 std::string work_str; // This will store the result of create() calls
32
33 std::vector<configurator> configs; // Store the configs assigned to this worker
34
35 int start, count;
36
37 work_item(unsigned int id, std::vector<configurator> configs, int start, int count)
38 : id{id}, work_str{}, configs(std::move(configs)), start{start}, count{count}
39 {
40 }
41 };
42
43 std::condition_variable work_cond;
44
45 std::mutex work_mtx;
46
47 std::atomic<int> pending_work_count;
48
49 std::atomic<bool> should_exit;
50
51 std::deque<work_item> work_queue;
52
53 std::vector<std::thread> workers;
54
55 std::string *target_str_ptr; // Pointer to the target string
56
57 std::mutex target_str_mutex; // Mutex to protect target_str access
58
59 public:
60 // Add constructor and destructor
61 worker_concurrent() : should_exit(false), pending_work_count(0), target_str_ptr(nullptr) {}
62
64 {
65
66 cleanup();
67 }
68
69 // Initialize worker threads
70 void initThreads() noexcept
71 {
72
73 using namespace std;
74
75 auto thread_func = [](worker_concurrent *worker_ptr)
76 {
77 while (!worker_ptr->should_exit.load())
78 {
79 unique_lock<mutex> lock(worker_ptr->work_mtx);
80
81 worker_ptr->work_cond.wait(lock, [worker_ptr]
82 { return worker_ptr->should_exit.load() || !worker_ptr->work_queue.empty(); });
83
84 if (worker_ptr->should_exit.load())
85 {
86
87 break;
88 }
89
90 // Ready to do work
91 if (!worker_ptr->work_queue.empty())
92 {
93
94 auto temp_worker = std::move(worker_ptr->work_queue.front());
95
96 worker_ptr->work_queue.pop_front();
97
98 lock.unlock();
99
100 worker_ptr->do_work(temp_worker);
101
102 lock.lock();
103
104 if (--worker_ptr->pending_work_count <= 0)
105 {
106 worker_ptr->work_cond.notify_one();
107 }
108 }
109 } // while
110 }; // lambda
111
112 static constexpr auto NUM_WORKERS = 4;
113
114 // Create worker threads
115 for (auto w{0}; w < NUM_WORKERS; w++)
116 {
117
118 workers.emplace_back(thread_func, this);
119 }
120 }
121
122 void generate(const std::vector<configurator> &configs, std::string &target_str) noexcept
123 {
124 using namespace std;
125
126 // Set the target string pointer
127 target_str_ptr = &target_str;
128
129 {
130 unique_lock<std::mutex> lock(this->work_mtx);
131
132 work_queue.clear();
133
134 static constexpr auto BLOCK_COUNT = 4;
135
136 size_t items_per_worker = configs.size() / BLOCK_COUNT;
137 size_t remaining_items = configs.size() % BLOCK_COUNT;
138
139 size_t current_index = 0;
140
141 for (auto w{0}; w < BLOCK_COUNT; w++)
142 {
143 size_t start_index = current_index;
144
145 // Distribute items evenly, with remainder distributed to first workers
146 size_t count = items_per_worker + (w < remaining_items ? 1 : 0);
147
148 if (count > 0)
149 {
150 // Extract configs for this worker
151 std::vector<configurator> worker_configs;
152 for (size_t i = start_index; i < start_index + count && i < configs.size(); ++i)
153 {
154 worker_configs.push_back(configs[i]);
155 }
156
157 work_queue.emplace_back(static_cast<unsigned int>(w), std::move(worker_configs), static_cast<int>(start_index), static_cast<int>(count));
158 }
159
160 current_index += count;
161 }
162
163 this->pending_work_count.store(static_cast<int>(work_queue.size()));
164 }
165
166 this->work_cond.notify_all();
167 } // generate
168
169 void wait_for_completion() noexcept
170 {
171 using namespace std;
172
173 unique_lock<mutex> lock(work_mtx);
174 work_cond.wait(lock, [&]
175 { return pending_work_count.load() <= 0; });
176 }
177
178 void do_work(work_item &item) noexcept
179 {
180 using namespace std;
181
182 for (const auto &config : item.configs)
183 {
184 item.work_str += create(config);
185 }
186
187 // Append the worker's result to the shared target string
188 if (target_str_ptr && !item.work_str.empty())
189 {
190 lock_guard<mutex> lock(target_str_mutex);
191 *target_str_ptr += item.work_str;
192 }
193 }
194
195 void cleanup() noexcept
196 {
197 {
198 std::lock_guard<std::mutex> lock(work_mtx);
199
200 should_exit = true;
201
202 pending_work_count = 0;
203 }
204
205 work_cond.notify_all();
206
207 for (auto &t : workers)
208 {
209
210 if (t.joinable())
211 {
212
213 t.join();
214 }
215 }
216
217 workers.clear();
218 }
219 };
220 } // namespace details
221
222 static inline std::string create2(const std::vector<configurator> &configs)
223 {
224 if (configs.empty())
225 {
226 return {};
227 }
228
229 // For single config, just use the existing create function
230 if (configs.size() == 1)
231 {
232 return create(configs[0]);
233 }
234
235 // Static worker pool for thread reuse
236 static detail::worker_concurrent foreman{};
237 static std::once_flag init_flag;
238
239 // Initialize threads only once
240 std::call_once(init_flag, [&]() {
241 foreman.initThreads();
242 });
243
244 std::string result{};
245
246 foreman.generate(configs, result);
247
248 foreman.wait_for_completion();
249
250 // Note: We don't call cleanup() here to keep threads alive for reuse
251
252 return result;
253 }
254
255} // namespace mazes
256
257#endif // CREATE_2_H
A worker class for concurrent maze generation.
Definition create2.h:24
Namespace for the maze builder.
Definition algo_interface.h:6