1 | /**************************************************************************/ |
2 | /* command_queue_mt.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifndef COMMAND_QUEUE_MT_H |
32 | #define COMMAND_QUEUE_MT_H |
33 | |
34 | #include "core/os/memory.h" |
35 | #include "core/os/mutex.h" |
36 | #include "core/os/semaphore.h" |
37 | #include "core/string/print_string.h" |
38 | #include "core/templates/local_vector.h" |
39 | #include "core/templates/simple_type.h" |
40 | #include "core/typedefs.h" |
41 | |
42 | #define COMMA(N) _COMMA_##N |
43 | #define _COMMA_0 |
44 | #define _COMMA_1 , |
45 | #define _COMMA_2 , |
46 | #define _COMMA_3 , |
47 | #define _COMMA_4 , |
48 | #define _COMMA_5 , |
49 | #define _COMMA_6 , |
50 | #define _COMMA_7 , |
51 | #define _COMMA_8 , |
52 | #define _COMMA_9 , |
53 | #define _COMMA_10 , |
54 | #define _COMMA_11 , |
55 | #define _COMMA_12 , |
56 | #define _COMMA_13 , |
57 | #define _COMMA_14 , |
58 | #define _COMMA_15 , |
59 | |
60 | // 1-based comma separated list of ITEMs |
61 | #define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM) |
62 | #define _COMMA_SEP_LIST_15(ITEM) \ |
63 | _COMMA_SEP_LIST_14(ITEM) \ |
64 | , ITEM(15) |
65 | #define _COMMA_SEP_LIST_14(ITEM) \ |
66 | _COMMA_SEP_LIST_13(ITEM) \ |
67 | , ITEM(14) |
68 | #define _COMMA_SEP_LIST_13(ITEM) \ |
69 | _COMMA_SEP_LIST_12(ITEM) \ |
70 | , ITEM(13) |
71 | #define _COMMA_SEP_LIST_12(ITEM) \ |
72 | _COMMA_SEP_LIST_11(ITEM) \ |
73 | , ITEM(12) |
74 | #define _COMMA_SEP_LIST_11(ITEM) \ |
75 | _COMMA_SEP_LIST_10(ITEM) \ |
76 | , ITEM(11) |
77 | #define _COMMA_SEP_LIST_10(ITEM) \ |
78 | _COMMA_SEP_LIST_9(ITEM) \ |
79 | , ITEM(10) |
80 | #define _COMMA_SEP_LIST_9(ITEM) \ |
81 | _COMMA_SEP_LIST_8(ITEM) \ |
82 | , ITEM(9) |
83 | #define _COMMA_SEP_LIST_8(ITEM) \ |
84 | _COMMA_SEP_LIST_7(ITEM) \ |
85 | , ITEM(8) |
86 | #define _COMMA_SEP_LIST_7(ITEM) \ |
87 | _COMMA_SEP_LIST_6(ITEM) \ |
88 | , ITEM(7) |
89 | #define _COMMA_SEP_LIST_6(ITEM) \ |
90 | _COMMA_SEP_LIST_5(ITEM) \ |
91 | , ITEM(6) |
92 | #define _COMMA_SEP_LIST_5(ITEM) \ |
93 | _COMMA_SEP_LIST_4(ITEM) \ |
94 | , ITEM(5) |
95 | #define _COMMA_SEP_LIST_4(ITEM) \ |
96 | _COMMA_SEP_LIST_3(ITEM) \ |
97 | , ITEM(4) |
98 | #define _COMMA_SEP_LIST_3(ITEM) \ |
99 | _COMMA_SEP_LIST_2(ITEM) \ |
100 | , ITEM(3) |
101 | #define _COMMA_SEP_LIST_2(ITEM) \ |
102 | _COMMA_SEP_LIST_1(ITEM) \ |
103 | , ITEM(2) |
104 | #define _COMMA_SEP_LIST_1(ITEM) \ |
105 | _COMMA_SEP_LIST_0(ITEM) \ |
106 | ITEM(1) |
107 | #define _COMMA_SEP_LIST_0(ITEM) |
108 | |
109 | // 1-based semicolon separated list of ITEMs |
110 | #define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM) |
111 | #define _SEMIC_SEP_LIST_15(ITEM) \ |
112 | _SEMIC_SEP_LIST_14(ITEM); \ |
113 | ITEM(15) |
114 | #define _SEMIC_SEP_LIST_14(ITEM) \ |
115 | _SEMIC_SEP_LIST_13(ITEM); \ |
116 | ITEM(14) |
117 | #define _SEMIC_SEP_LIST_13(ITEM) \ |
118 | _SEMIC_SEP_LIST_12(ITEM); \ |
119 | ITEM(13) |
120 | #define _SEMIC_SEP_LIST_12(ITEM) \ |
121 | _SEMIC_SEP_LIST_11(ITEM); \ |
122 | ITEM(12) |
123 | #define _SEMIC_SEP_LIST_11(ITEM) \ |
124 | _SEMIC_SEP_LIST_10(ITEM); \ |
125 | ITEM(11) |
126 | #define _SEMIC_SEP_LIST_10(ITEM) \ |
127 | _SEMIC_SEP_LIST_9(ITEM); \ |
128 | ITEM(10) |
129 | #define _SEMIC_SEP_LIST_9(ITEM) \ |
130 | _SEMIC_SEP_LIST_8(ITEM); \ |
131 | ITEM(9) |
132 | #define _SEMIC_SEP_LIST_8(ITEM) \ |
133 | _SEMIC_SEP_LIST_7(ITEM); \ |
134 | ITEM(8) |
135 | #define _SEMIC_SEP_LIST_7(ITEM) \ |
136 | _SEMIC_SEP_LIST_6(ITEM); \ |
137 | ITEM(7) |
138 | #define _SEMIC_SEP_LIST_6(ITEM) \ |
139 | _SEMIC_SEP_LIST_5(ITEM); \ |
140 | ITEM(6) |
141 | #define _SEMIC_SEP_LIST_5(ITEM) \ |
142 | _SEMIC_SEP_LIST_4(ITEM); \ |
143 | ITEM(5) |
144 | #define _SEMIC_SEP_LIST_4(ITEM) \ |
145 | _SEMIC_SEP_LIST_3(ITEM); \ |
146 | ITEM(4) |
147 | #define _SEMIC_SEP_LIST_3(ITEM) \ |
148 | _SEMIC_SEP_LIST_2(ITEM); \ |
149 | ITEM(3) |
150 | #define _SEMIC_SEP_LIST_2(ITEM) \ |
151 | _SEMIC_SEP_LIST_1(ITEM); \ |
152 | ITEM(2) |
153 | #define _SEMIC_SEP_LIST_1(ITEM) \ |
154 | _SEMIC_SEP_LIST_0(ITEM) \ |
155 | ITEM(1) |
156 | #define _SEMIC_SEP_LIST_0(ITEM) |
157 | |
158 | // 1-based space separated list of ITEMs |
159 | #define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM) |
160 | #define _SPACE_SEP_LIST_15(ITEM) \ |
161 | _SPACE_SEP_LIST_14(ITEM) \ |
162 | ITEM(15) |
163 | #define _SPACE_SEP_LIST_14(ITEM) \ |
164 | _SPACE_SEP_LIST_13(ITEM) \ |
165 | ITEM(14) |
166 | #define _SPACE_SEP_LIST_13(ITEM) \ |
167 | _SPACE_SEP_LIST_12(ITEM) \ |
168 | ITEM(13) |
169 | #define _SPACE_SEP_LIST_12(ITEM) \ |
170 | _SPACE_SEP_LIST_11(ITEM) \ |
171 | ITEM(12) |
172 | #define _SPACE_SEP_LIST_11(ITEM) \ |
173 | _SPACE_SEP_LIST_10(ITEM) \ |
174 | ITEM(11) |
175 | #define _SPACE_SEP_LIST_10(ITEM) \ |
176 | _SPACE_SEP_LIST_9(ITEM) \ |
177 | ITEM(10) |
178 | #define _SPACE_SEP_LIST_9(ITEM) \ |
179 | _SPACE_SEP_LIST_8(ITEM) \ |
180 | ITEM(9) |
181 | #define _SPACE_SEP_LIST_8(ITEM) \ |
182 | _SPACE_SEP_LIST_7(ITEM) \ |
183 | ITEM(8) |
184 | #define _SPACE_SEP_LIST_7(ITEM) \ |
185 | _SPACE_SEP_LIST_6(ITEM) \ |
186 | ITEM(7) |
187 | #define _SPACE_SEP_LIST_6(ITEM) \ |
188 | _SPACE_SEP_LIST_5(ITEM) \ |
189 | ITEM(6) |
190 | #define _SPACE_SEP_LIST_5(ITEM) \ |
191 | _SPACE_SEP_LIST_4(ITEM) \ |
192 | ITEM(5) |
193 | #define _SPACE_SEP_LIST_4(ITEM) \ |
194 | _SPACE_SEP_LIST_3(ITEM) \ |
195 | ITEM(4) |
196 | #define _SPACE_SEP_LIST_3(ITEM) \ |
197 | _SPACE_SEP_LIST_2(ITEM) \ |
198 | ITEM(3) |
199 | #define _SPACE_SEP_LIST_2(ITEM) \ |
200 | _SPACE_SEP_LIST_1(ITEM) \ |
201 | ITEM(2) |
202 | #define _SPACE_SEP_LIST_1(ITEM) \ |
203 | _SPACE_SEP_LIST_0(ITEM) \ |
204 | ITEM(1) |
205 | #define _SPACE_SEP_LIST_0(ITEM) |
206 | |
207 | #define ARG(N) p##N |
208 | #define PARAM(N) P##N p##N |
209 | #define TYPE_PARAM(N) class P##N |
210 | #define PARAM_DECL(N) typename GetSimpleTypeT<P##N>::type_t p##N |
211 | |
212 | #define DECL_CMD(N) \ |
213 | template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ |
214 | struct Command##N : public CommandBase { \ |
215 | T *instance; \ |
216 | M method; \ |
217 | SEMIC_SEP_LIST(PARAM_DECL, N); \ |
218 | virtual void call() override { \ |
219 | (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ |
220 | } \ |
221 | }; |
222 | |
223 | #define DECL_CMD_RET(N) \ |
224 | template <class T, class M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) class R> \ |
225 | struct CommandRet##N : public SyncCommand { \ |
226 | R *ret; \ |
227 | T *instance; \ |
228 | M method; \ |
229 | SEMIC_SEP_LIST(PARAM_DECL, N); \ |
230 | virtual void call() override { \ |
231 | *ret = (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ |
232 | } \ |
233 | }; |
234 | |
235 | #define DECL_CMD_SYNC(N) \ |
236 | template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ |
237 | struct CommandSync##N : public SyncCommand { \ |
238 | T *instance; \ |
239 | M method; \ |
240 | SEMIC_SEP_LIST(PARAM_DECL, N); \ |
241 | virtual void call() override { \ |
242 | (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ |
243 | } \ |
244 | }; |
245 | |
246 | #define TYPE_ARG(N) P##N |
247 | #define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)> |
248 | #define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N |
249 | |
250 | #define DECL_PUSH(N) \ |
251 | template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ |
252 | void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ |
253 | CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \ |
254 | cmd->instance = p_instance; \ |
255 | cmd->method = p_method; \ |
256 | SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ |
257 | unlock(); \ |
258 | if (sync) \ |
259 | sync->post(); \ |
260 | } |
261 | |
262 | #define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R> |
263 | |
264 | #define DECL_PUSH_AND_RET(N) \ |
265 | template <class T, class M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) class R> \ |
266 | void push_and_ret(T *p_instance, M p_method, COMMA_SEP_LIST(PARAM, N) COMMA(N) R *r_ret) { \ |
267 | SyncSemaphore *ss = _alloc_sync_sem(); \ |
268 | CMD_RET_TYPE(N) *cmd = allocate_and_lock<CMD_RET_TYPE(N)>(); \ |
269 | cmd->instance = p_instance; \ |
270 | cmd->method = p_method; \ |
271 | SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ |
272 | cmd->ret = r_ret; \ |
273 | cmd->sync_sem = ss; \ |
274 | unlock(); \ |
275 | if (sync) \ |
276 | sync->post(); \ |
277 | ss->sem.wait(); \ |
278 | ss->in_use = false; \ |
279 | } |
280 | |
281 | #define CMD_SYNC_TYPE(N) CommandSync##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)> |
282 | |
283 | #define DECL_PUSH_AND_SYNC(N) \ |
284 | template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ |
285 | void push_and_sync(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ |
286 | SyncSemaphore *ss = _alloc_sync_sem(); \ |
287 | CMD_SYNC_TYPE(N) *cmd = allocate_and_lock<CMD_SYNC_TYPE(N)>(); \ |
288 | cmd->instance = p_instance; \ |
289 | cmd->method = p_method; \ |
290 | SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ |
291 | cmd->sync_sem = ss; \ |
292 | unlock(); \ |
293 | if (sync) \ |
294 | sync->post(); \ |
295 | ss->sem.wait(); \ |
296 | ss->in_use = false; \ |
297 | } |
298 | |
299 | #define MAX_CMD_PARAMS 15 |
300 | |
301 | class CommandQueueMT { |
302 | struct SyncSemaphore { |
303 | Semaphore sem; |
304 | bool in_use = false; |
305 | }; |
306 | |
307 | struct CommandBase { |
308 | virtual void call() = 0; |
309 | virtual void post() {} |
310 | virtual ~CommandBase() {} |
311 | }; |
312 | |
313 | struct SyncCommand : public CommandBase { |
314 | SyncSemaphore *sync_sem = nullptr; |
315 | |
316 | virtual void post() override { |
317 | sync_sem->sem.post(); |
318 | } |
319 | }; |
320 | |
321 | DECL_CMD(0) |
322 | SPACE_SEP_LIST(DECL_CMD, 15) |
323 | |
324 | // Commands that return. |
325 | DECL_CMD_RET(0) |
326 | SPACE_SEP_LIST(DECL_CMD_RET, 15) |
327 | |
328 | /* commands that don't return but sync */ |
329 | DECL_CMD_SYNC(0) |
330 | SPACE_SEP_LIST(DECL_CMD_SYNC, 15) |
331 | |
332 | /***** BASE *******/ |
333 | |
334 | enum { |
335 | DEFAULT_COMMAND_MEM_SIZE_KB = 256, |
336 | SYNC_SEMAPHORES = 8 |
337 | }; |
338 | |
339 | LocalVector<uint8_t> command_mem; |
340 | SyncSemaphore sync_sems[SYNC_SEMAPHORES]; |
341 | Mutex mutex; |
342 | Semaphore *sync = nullptr; |
343 | |
344 | template <class T> |
345 | T *allocate() { |
346 | // alloc size is size+T+safeguard |
347 | uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1)); |
348 | uint64_t size = command_mem.size(); |
349 | command_mem.resize(size + alloc_size + 8); |
350 | *(uint64_t *)&command_mem[size] = alloc_size; |
351 | T *cmd = memnew_placement(&command_mem[size + 8], T); |
352 | return cmd; |
353 | } |
354 | |
355 | template <class T> |
356 | T *allocate_and_lock() { |
357 | lock(); |
358 | T *ret = allocate<T>(); |
359 | return ret; |
360 | } |
361 | |
362 | void _flush() { |
363 | lock(); |
364 | |
365 | uint64_t read_ptr = 0; |
366 | uint64_t limit = command_mem.size(); |
367 | |
368 | while (read_ptr < limit) { |
369 | uint64_t size = *(uint64_t *)&command_mem[read_ptr]; |
370 | read_ptr += 8; |
371 | CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[read_ptr]); |
372 | |
373 | cmd->call(); //execute the function |
374 | cmd->post(); //release in case it needs sync/ret |
375 | cmd->~CommandBase(); //should be done, so erase the command |
376 | |
377 | read_ptr += size; |
378 | } |
379 | |
380 | command_mem.clear(); |
381 | unlock(); |
382 | } |
383 | |
384 | void lock(); |
385 | void unlock(); |
386 | void wait_for_flush(); |
387 | SyncSemaphore *_alloc_sync_sem(); |
388 | |
389 | public: |
390 | /* NORMAL PUSH COMMANDS */ |
391 | DECL_PUSH(0) |
392 | SPACE_SEP_LIST(DECL_PUSH, 15) |
393 | |
394 | /* PUSH AND RET COMMANDS */ |
395 | DECL_PUSH_AND_RET(0) |
396 | SPACE_SEP_LIST(DECL_PUSH_AND_RET, 15) |
397 | |
398 | /* PUSH AND RET SYNC COMMANDS*/ |
399 | DECL_PUSH_AND_SYNC(0) |
400 | SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 15) |
401 | |
402 | _FORCE_INLINE_ void flush_if_pending() { |
403 | if (unlikely(command_mem.size() > 0)) { |
404 | _flush(); |
405 | } |
406 | } |
407 | void flush_all() { |
408 | _flush(); |
409 | } |
410 | |
411 | void wait_and_flush() { |
412 | ERR_FAIL_NULL(sync); |
413 | sync->wait(); |
414 | _flush(); |
415 | } |
416 | |
417 | CommandQueueMT(bool p_sync); |
418 | ~CommandQueueMT(); |
419 | }; |
420 | |
421 | #undef ARG |
422 | #undef PARAM |
423 | #undef TYPE_PARAM |
424 | #undef PARAM_DECL |
425 | #undef DECL_CMD |
426 | #undef DECL_CMD_RET |
427 | #undef DECL_CMD_SYNC |
428 | #undef TYPE_ARG |
429 | #undef CMD_TYPE |
430 | #undef CMD_ASSIGN_PARAM |
431 | #undef DECL_PUSH |
432 | #undef CMD_RET_TYPE |
433 | #undef DECL_PUSH_AND_RET |
434 | #undef CMD_SYNC_TYPE |
435 | #undef DECL_CMD_SYNC |
436 | |
437 | #endif // COMMAND_QUEUE_MT_H |
438 | |