1/**************************************************************************/
2/* message_queue.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 MESSAGE_QUEUE_H
32#define MESSAGE_QUEUE_H
33
34#include "core/object/object_id.h"
35#include "core/os/thread_safe.h"
36#include "core/templates/local_vector.h"
37#include "core/templates/paged_allocator.h"
38#include "core/variant/variant.h"
39
40class Object;
41
42class CallQueue {
43 friend class MessageQueue;
44
45public:
46 enum {
47 PAGE_SIZE_BYTES = 4096
48 };
49
50 struct Page {
51 uint8_t data[PAGE_SIZE_BYTES];
52 };
53
54 // Needs to be public to be able to define it outside the class.
55 // Needs to lock because there can be multiple of these allocators in several threads.
56 typedef PagedAllocator<Page, true> Allocator;
57
58private:
59 enum {
60 TYPE_CALL,
61 TYPE_NOTIFICATION,
62 TYPE_SET,
63 TYPE_END, // End marker.
64 FLAG_NULL_IS_OK = 1 << 13,
65 FLAG_SHOW_ERROR = 1 << 14,
66 FLAG_MASK = FLAG_NULL_IS_OK - 1,
67 };
68
69 Mutex mutex;
70
71 Allocator *allocator = nullptr;
72 bool allocator_is_custom = false;
73
74 LocalVector<Page *> pages;
75 LocalVector<uint32_t> page_bytes;
76 uint32_t max_pages = 0;
77 uint32_t pages_used = 0;
78 bool flushing = false;
79
80#ifdef DEV_ENABLED
81 bool is_current_thread_override = false;
82#endif
83
84 struct Message {
85 Callable callable;
86 int16_t type;
87 union {
88 int16_t notification;
89 int16_t args;
90 };
91 };
92
93 _FORCE_INLINE_ void _ensure_first_page() {
94 if (unlikely(pages.is_empty())) {
95 pages.push_back(allocator->alloc());
96 page_bytes.push_back(0);
97 pages_used = 1;
98 }
99 }
100
101 Error _transfer_messages_to_main_queue();
102
103 void _add_page();
104
105 void _call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error);
106
107 String error_text;
108
109public:
110 Error push_callp(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
111 template <typename... VarArgs>
112 Error push_call(ObjectID p_id, const StringName &p_method, VarArgs... p_args) {
113 Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
114 const Variant *argptrs[sizeof...(p_args) + 1];
115 for (uint32_t i = 0; i < sizeof...(p_args); i++) {
116 argptrs[i] = &args[i];
117 }
118 return push_callp(p_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
119 }
120
121 Error push_callablep(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false);
122 Error push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value);
123 Error push_notification(ObjectID p_id, int p_notification);
124
125 template <typename... VarArgs>
126 Error push_callable(const Callable &p_callable, VarArgs... p_args) {
127 Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
128 const Variant *argptrs[sizeof...(p_args) + 1];
129 for (uint32_t i = 0; i < sizeof...(p_args); i++) {
130 argptrs[i] = &args[i];
131 }
132 return push_callablep(p_callable, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
133 }
134
135 Error push_callp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
136 template <typename... VarArgs>
137 Error push_call(Object *p_object, const StringName &p_method, VarArgs... p_args) {
138 Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
139 const Variant *argptrs[sizeof...(p_args) + 1];
140 for (uint32_t i = 0; i < sizeof...(p_args); i++) {
141 argptrs[i] = &args[i];
142 }
143 return push_callp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
144 }
145
146 Error push_notification(Object *p_object, int p_notification);
147 Error push_set(Object *p_object, const StringName &p_prop, const Variant &p_value);
148
149 Error flush();
150 void clear();
151 void statistics();
152
153 bool has_messages() const;
154
155 bool is_flushing() const;
156 int get_max_buffer_usage() const;
157
158 CallQueue(Allocator *p_custom_allocator = 0, uint32_t p_max_pages = 8192, const String &p_error_text = String());
159 virtual ~CallQueue();
160};
161
162class MessageQueue : public CallQueue {
163 static CallQueue *main_singleton;
164 static thread_local CallQueue *thread_singleton;
165 friend class CallQueue;
166
167public:
168 _FORCE_INLINE_ static CallQueue *get_singleton() { return thread_singleton ? thread_singleton : main_singleton; }
169
170 static void set_thread_singleton_override(CallQueue *p_thread_singleton);
171
172 MessageQueue();
173 ~MessageQueue();
174};
175
176#endif // MESSAGE_QUEUE_H
177