summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2020-10-15 22:35:44 +0200
committerGitHub <noreply@github.com>2020-10-15 22:35:44 +0200
commit88a3db5bff3c5d62aec7e7ade4f6c62bf2d0ec3e (patch)
tree971d9f7c4567f806461381078837bd7c0193dc9b /core
parent1b443da1827e312db1aa99e3895c1879caf246ca (diff)
parent9f654b441fffa613568e30a4c53a57390be69e12 (diff)
Merge pull request #42315 from lyuma/command_queue_fix
core/command_queue_mt.h: Fix crash/hang when buffer fills up
Diffstat (limited to 'core')
-rw-r--r--core/command_queue_mt.cpp7
-rw-r--r--core/command_queue_mt.h36
2 files changed, 31 insertions, 12 deletions
diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp
index ace210ca2c..a55eed5d3c 100644
--- a/core/command_queue_mt.cpp
+++ b/core/command_queue_mt.cpp
@@ -31,6 +31,7 @@
#include "command_queue_mt.h"
#include "core/os/os.h"
+#include "core/project_settings.h"
void CommandQueueMT::lock() {
mutex.lock();
@@ -71,7 +72,7 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() {
bool CommandQueueMT::dealloc_one() {
tryagain:
- if (dealloc_ptr == write_ptr) {
+ if (dealloc_ptr == (write_ptr_and_epoch >> 1)) {
// The queue is empty
return false;
}
@@ -94,6 +95,10 @@ tryagain:
}
CommandQueueMT::CommandQueueMT(bool p_sync) {
+ command_mem_size = GLOBAL_DEF_RST("memory/limits/command_queue/multithreading_queue_size_kb", DEFAULT_COMMAND_MEM_SIZE_KB);
+ ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/command_queue/multithreading_queue_size_kb", PropertyInfo(Variant::INT, "memory/limits/command_queue/multithreading_queue_size_kb", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"));
+ command_mem_size *= 1024;
+ command_mem = (uint8_t *)memalloc(command_mem_size);
if (p_sync) {
sync = memnew(Semaphore);
}
diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h
index d7a6a5bc43..0e5bc7f369 100644
--- a/core/command_queue_mt.h
+++ b/core/command_queue_mt.h
@@ -330,15 +330,15 @@ class CommandQueueMT {
/***** BASE *******/
enum {
- COMMAND_MEM_SIZE_KB = 256,
- COMMAND_MEM_SIZE = COMMAND_MEM_SIZE_KB * 1024,
+ DEFAULT_COMMAND_MEM_SIZE_KB = 256,
SYNC_SEMAPHORES = 8
};
- uint8_t *command_mem = (uint8_t *)memalloc(COMMAND_MEM_SIZE);
- uint32_t read_ptr = 0;
- uint32_t write_ptr = 0;
+ uint8_t *command_mem = nullptr;
+ uint32_t read_ptr_and_epoch = 0;
+ uint32_t write_ptr_and_epoch = 0;
uint32_t dealloc_ptr = 0;
+ uint32_t command_mem_size = 0;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
Semaphore *sync = nullptr;
@@ -348,7 +348,11 @@ class CommandQueueMT {
// alloc size is size+T+safeguard
uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1)) + 8;
+ // Assert that the buffer is big enough to hold at least two messages.
+ ERR_FAIL_COND_V(alloc_size * 2 + sizeof(uint32_t) > command_mem_size, nullptr);
+
tryagain:
+ uint32_t write_ptr = write_ptr_and_epoch >> 1;
if (write_ptr < dealloc_ptr) {
// behind dealloc_ptr, check that there is room
@@ -362,7 +366,7 @@ class CommandQueueMT {
} else {
// ahead of dealloc_ptr, check that there is room
- if ((COMMAND_MEM_SIZE - write_ptr) < alloc_size + sizeof(uint32_t)) {
+ if ((command_mem_size - write_ptr) < alloc_size + sizeof(uint32_t)) {
// no room at the end, wrap down;
if (dealloc_ptr == 0) { // don't want write_ptr to become dealloc_ptr
@@ -375,12 +379,17 @@ class CommandQueueMT {
}
// if this happens, it's a bug
- ERR_FAIL_COND_V((COMMAND_MEM_SIZE - write_ptr) < 8, nullptr);
+ ERR_FAIL_COND_V((command_mem_size - write_ptr) < 8, nullptr);
// zero means, wrap to beginning
uint32_t *p = (uint32_t *)&command_mem[write_ptr];
- *p = 0;
- write_ptr = 0;
+ *p = 1;
+ write_ptr_and_epoch = 0 | (1 & ~write_ptr_and_epoch); // Invert epoch.
+ // See if we can get the thread to run and clear up some more space while we wait.
+ // This is required if alloc_size * 2 + 4 > COMMAND_MEM_SIZE
+ if (sync) {
+ sync->post();
+ }
goto tryagain;
}
}
@@ -394,6 +403,7 @@ class CommandQueueMT {
// allocate the command
T *cmd = memnew_placement(&command_mem[write_ptr], T);
write_ptr += size;
+ write_ptr_and_epoch = (write_ptr << 1) | (write_ptr_and_epoch & 1);
return cmd;
}
@@ -419,19 +429,21 @@ class CommandQueueMT {
tryagain:
// tried to read an empty queue
- if (read_ptr == write_ptr) {
+ if (read_ptr_and_epoch == write_ptr_and_epoch) {
if (p_lock) {
unlock();
}
return false;
}
+ uint32_t read_ptr = read_ptr_and_epoch >> 1;
uint32_t size_ptr = read_ptr;
uint32_t size = *(uint32_t *)&command_mem[read_ptr] >> 1;
if (size == 0) {
+ *(uint32_t *)&command_mem[read_ptr] = 0; // clear in-use bit.
//end of ringbuffer, wrap
- read_ptr = 0;
+ read_ptr_and_epoch = 0 | (1 & ~read_ptr_and_epoch); // Invert epoch.
goto tryagain;
}
@@ -441,6 +453,8 @@ class CommandQueueMT {
read_ptr += size;
+ read_ptr_and_epoch = (read_ptr << 1) | (read_ptr_and_epoch & 1);
+
if (p_lock) {
unlock();
}