Merge pull request #66803 from akien-mga/core-remove-NO_THREADS

Remove `NO_THREADS` fallback code, Godot 4 requires thread support
This commit is contained in:
Rémi Verschelde 2022-10-03 14:51:02 +02:00
commit d598fff7c2
29 changed files with 13 additions and 561 deletions

View file

@ -429,10 +429,6 @@ void OS::delay_msec(int p_msec) const {
::OS::get_singleton()->delay_usec(int64_t(p_msec) * 1000);
}
bool OS::can_use_threads() const {
return ::OS::get_singleton()->can_use_threads();
}
bool OS::is_userfs_persistent() const {
return ::OS::get_singleton()->is_userfs_persistent();
}
@ -566,8 +562,6 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &OS::is_userfs_persistent);
ClassDB::bind_method(D_METHOD("is_stdout_verbose"), &OS::is_stdout_verbose);
ClassDB::bind_method(D_METHOD("can_use_threads"), &OS::can_use_threads);
ClassDB::bind_method(D_METHOD("is_debug_build"), &OS::is_debug_build);
ClassDB::bind_method(D_METHOD("get_static_memory_usage"), &OS::get_static_memory_usage);

View file

@ -219,8 +219,6 @@ public:
uint64_t get_ticks_msec() const;
uint64_t get_ticks_usec() const;
bool can_use_threads() const;
bool is_userfs_persistent() const;
bool is_stdout_verbose() const;

View file

@ -79,10 +79,8 @@ RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
tcp_client = p_tcp;
if (tcp_client.is_valid()) { // Attaching to an already connected stream.
connected = true;
#ifndef NO_THREADS
running = true;
thread.start(_thread_func, this);
#endif
} else {
tcp_client.instantiate();
}
@ -183,10 +181,8 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po
return FAILED;
}
connected = true;
#ifndef NO_THREADS
running = true;
thread.start(_thread_func, this);
#endif
return OK;
}
@ -208,9 +204,7 @@ void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) {
}
void RemoteDebuggerPeerTCP::poll() {
#ifdef NO_THREADS
_poll();
#endif
// Nothing to do, polling is done in thread.
}
void RemoteDebuggerPeerTCP::_poll() {

View file

@ -43,12 +43,12 @@ protected:
public:
virtual bool is_peer_connected() = 0;
virtual int get_max_message_size() const = 0;
virtual bool has_message() = 0;
virtual Error put_message(const Array &p_arr) = 0;
virtual Array get_message() = 0;
virtual void close() = 0;
virtual void poll() = 0;
virtual int get_max_message_size() const = 0;
virtual bool can_block() const { return true; } // If blocking io is allowed on main thread (debug).
RemoteDebuggerPeer();
@ -81,12 +81,12 @@ public:
Error connect_to_host(const String &p_host, uint16_t p_port);
void poll() override;
bool is_peer_connected() override;
bool has_message() override;
Array get_message() override;
Error put_message(const Array &p_arr) override;
int get_max_message_size() const override;
bool has_message() override;
Error put_message(const Array &p_arr) override;
Array get_message() override;
void poll() override;
void close() override;
RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());

View file

@ -40,11 +40,7 @@ void _global_unlock() {
_global_mutex.unlock();
}
#ifndef NO_THREADS
template class MutexImpl<std::recursive_mutex>;
template class MutexImpl<std::mutex>;
template class MutexLock<MutexImpl<std::recursive_mutex>>;
template class MutexLock<MutexImpl<std::mutex>>;
#endif

View file

@ -34,8 +34,6 @@
#include "core/error/error_list.h"
#include "core/typedefs.h"
#if !defined(NO_THREADS)
#include <mutex>
template <class StdMutexT>
@ -79,29 +77,4 @@ extern template class MutexImpl<std::mutex>;
extern template class MutexLock<MutexImpl<std::recursive_mutex>>;
extern template class MutexLock<MutexImpl<std::mutex>>;
#else
class FakeMutex {
FakeMutex() {}
};
template <class MutexT>
class MutexImpl {
public:
_ALWAYS_INLINE_ void lock() const {}
_ALWAYS_INLINE_ void unlock() const {}
_ALWAYS_INLINE_ Error try_lock() const { return OK; }
};
template <class MutexT>
class MutexLock {
public:
explicit MutexLock(const MutexT &p_mutex) {}
};
using Mutex = MutexImpl<FakeMutex>;
using BinaryMutex = MutexImpl<FakeMutex>; // Non-recursive, handle with care
#endif // !NO_THREADS
#endif // MUTEX_H

View file

@ -328,14 +328,6 @@ String OS::get_processor_name() const {
return "";
}
bool OS::can_use_threads() const {
#ifdef NO_THREADS
return false;
#else
return true;
#endif
}
void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
has_server_feature_callback = p_callback;
}

View file

@ -299,8 +299,6 @@ public:
virtual String get_unique_id() const;
virtual bool can_use_threads() const;
bool has_feature(const String &p_feature);
void set_has_server_feature_callback(HasServerFeatureCallback p_callback);

View file

@ -33,8 +33,6 @@
#include "core/error/error_list.h"
#if !defined(NO_THREADS)
#include <shared_mutex>
class RWLock {
@ -72,21 +70,6 @@ public:
}
};
#else
class RWLock {
public:
void read_lock() const {}
void read_unlock() const {}
Error read_try_lock() const { return OK; }
void write_lock() {}
void write_unlock() {}
Error write_try_lock() { return OK; }
};
#endif
class RWLockRead {
const RWLock &lock;

View file

@ -34,8 +34,6 @@
#include "core/error/error_list.h"
#include "core/typedefs.h"
#if !defined(NO_THREADS)
#include <condition_variable>
#include <mutex>
@ -70,15 +68,4 @@ public:
}
};
#else
class Semaphore {
public:
_ALWAYS_INLINE_ void post() const {}
_ALWAYS_INLINE_ void wait() const {}
_ALWAYS_INLINE_ bool try_wait() const { return true; }
};
#endif
#endif // SEMAPHORE_H

View file

@ -34,9 +34,6 @@
#include "thread.h"
#include "core/object/script_language.h"
#if !defined(NO_THREADS)
#include "core/templates/safe_refcount.h"
Error (*Thread::set_name_func)(const String &) = nullptr;
@ -128,5 +125,4 @@ Thread::~Thread() {
}
}
#endif
#endif // PLATFORM_THREAD_OVERRIDE

View file

@ -35,15 +35,14 @@
#ifdef PLATFORM_THREAD_OVERRIDE
#include "platform_thread.h"
#else
#ifndef THREAD_H
#define THREAD_H
#include "core/templates/safe_refcount.h"
#include "core/typedefs.h"
#if !defined(NO_THREADS)
#include "core/templates/safe_refcount.h"
#include <thread>
#endif
class String;
@ -65,7 +64,6 @@ public:
};
private:
#if !defined(NO_THREADS)
friend class Main;
static ID main_thread_id;
@ -82,7 +80,6 @@ private:
static void (*set_priority_func)(Thread::Priority);
static void (*init_func)();
static void (*term_func)();
#endif
public:
static void _set_platform_funcs(
@ -91,7 +88,6 @@ public:
void (*p_init_func)() = nullptr,
void (*p_term_func)() = nullptr);
#if !defined(NO_THREADS)
_FORCE_INLINE_ ID get_id() const { return id; }
// get the ID of the caller thread
_FORCE_INLINE_ static ID get_caller_id() { return caller_id; }
@ -107,19 +103,6 @@ public:
Thread();
~Thread();
#else
_FORCE_INLINE_ ID get_id() const { return 0; }
// get the ID of the caller thread
_FORCE_INLINE_ static ID get_caller_id() { return 0; }
// get the ID of the main thread
_FORCE_INLINE_ static ID get_main_id() { return 0; }
static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; }
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {}
bool is_started() const { return false; }
void wait_to_finish() {}
#endif
};
#endif // THREAD_H

View file

@ -49,8 +49,6 @@ struct ThreadArrayProcessData {
}
};
#ifndef NO_THREADS
template <class T>
void process_array_thread(void *ud) {
T &data = *(T *)ud;
@ -86,21 +84,4 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us
memdelete_arr(threads);
}
#else
template <class C, class M, class U>
void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
ThreadArrayProcessData<C, U> data;
data.method = p_method;
data.instance = p_instance;
data.userdata = p_userdata;
data.index.set(0);
data.elements = p_elements;
for (uint32_t i = 0; i < p_elements; i++) {
data.process(i);
}
}
#endif
#endif // THREADED_ARRAY_PROCESSOR_H

View file

@ -46,9 +46,7 @@ class CharString;
template <class T, class V>
class VMap;
#if !defined(NO_THREADS)
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
#endif
// Silence a false positive warning (see GH-52119).
#if defined(__GNUC__) && !defined(__clang__)

View file

@ -33,11 +33,9 @@
#include "core/os/memory.h"
#include "core/typedefs.h"
#include <functional>
#if !defined(NO_THREADS)
#include <atomic>
#include <functional>
#include <type_traits>
// Design goals for these classes:
@ -239,159 +237,4 @@ public:
}
};
#else // NO_THREADS
// Effectively the same structure without the atomics. It's probably possible to simplify it but the semantics shouldn't differ greatly.
template <class T, class A = DefaultAllocator>
class SafeList {
struct SafeListNode {
SafeListNode *next = nullptr;
// If the node is logically deleted, this pointer will typically point to the previous list item in time that was also logically deleted.
SafeListNode *graveyard_next = nullptr;
std::function<void(T)> deletion_fn = [](T t) { return; };
T val;
};
SafeListNode *head = nullptr;
SafeListNode *graveyard_head = nullptr;
unsigned int active_iterator_count = 0;
public:
class Iterator {
friend class SafeList;
SafeListNode *cursor = nullptr;
SafeList *list = nullptr;
public:
Iterator(SafeListNode *p_cursor, SafeList *p_list) :
cursor(p_cursor), list(p_list) {
list->active_iterator_count++;
}
~Iterator() {
list->active_iterator_count--;
}
T &operator*() {
return cursor->val;
}
Iterator &operator++() {
cursor = cursor->next;
return *this;
}
// These two operators are mostly useful for comparisons to nullptr.
bool operator==(const void *p_other) const {
return cursor == p_other;
}
bool operator!=(const void *p_other) const {
return cursor != p_other;
}
// These two allow easy range-based for loops.
bool operator==(const Iterator &p_other) const {
return cursor == p_other.cursor;
}
bool operator!=(const Iterator &p_other) const {
return cursor != p_other.cursor;
}
};
public:
// Calling this will cause an allocation.
void insert(T p_value) {
SafeListNode *new_node = memnew_allocator(SafeListNode, A);
new_node->val = p_value;
new_node->next = head;
head = new_node;
}
Iterator find(T p_value) {
for (Iterator it = begin(); it != end(); ++it) {
if (*it == p_value) {
return it;
}
}
return end();
}
void erase(T p_value, std::function<void(T)> p_deletion_fn) {
erase(find(p_value), p_deletion_fn);
}
void erase(T p_value) {
erase(find(p_value), [](T t) { return; });
}
void erase(Iterator p_iterator, std::function<void(T)> p_deletion_fn) {
p_iterator.cursor->deletion_fn = p_deletion_fn;
erase(p_iterator);
}
void erase(Iterator p_iterator) {
Iterator prev = begin();
for (; prev != end(); ++prev) {
if (prev.cursor && prev.cursor->next == p_iterator.cursor) {
break;
}
}
if (prev == end()) {
// Not in the list, nothing to do.
return;
}
// First, remove the node from the list.
prev.cursor->next = p_iterator.cursor->next;
// Then queue it for deletion by putting it in the node graveyard. Don't touch `next` because an iterator might still be pointing at this node.
p_iterator.cursor->graveyard_next = graveyard_head;
graveyard_head = p_iterator.cursor;
}
Iterator begin() {
return Iterator(head, this);
}
Iterator end() {
return Iterator(nullptr, this);
}
// Calling this will cause zero to many deallocations.
bool maybe_cleanup() {
SafeListNode *cursor = graveyard_head;
if (active_iterator_count != 0) {
// It's not safe to clean up with an active iterator, because that iterator could be pointing to an element that we want to delete.
return false;
}
graveyard_head = nullptr;
// Our graveyard list is now unreachable by any active iterators, detached from the main graveyard head and ready for deletion.
while (cursor) {
SafeListNode *tmp = cursor;
cursor = cursor->next;
tmp->deletion_fn(tmp->val);
memdelete_allocator<SafeListNode, A>(tmp);
}
return true;
}
~SafeList() {
#ifdef DEBUG_ENABLED
if (!maybe_cleanup()) {
ERR_PRINT("There are still iterators around when destructing a SafeList. Memory will be leaked. This is a bug.");
}
#else
maybe_cleanup();
#endif
}
};
#endif
#endif // SAFE_LIST_H

View file

@ -33,8 +33,6 @@
#include "core/typedefs.h"
#if !defined(NO_THREADS)
#include <atomic>
#include <type_traits>
@ -191,141 +189,4 @@ public:
}
};
#else
template <class T>
class SafeNumeric {
protected:
T value;
public:
_ALWAYS_INLINE_ void set(T p_value) {
value = p_value;
}
_ALWAYS_INLINE_ T get() const {
return value;
}
_ALWAYS_INLINE_ T increment() {
return ++value;
}
_ALWAYS_INLINE_ T postincrement() {
return value++;
}
_ALWAYS_INLINE_ T decrement() {
return --value;
}
_ALWAYS_INLINE_ T postdecrement() {
return value--;
}
_ALWAYS_INLINE_ T add(T p_value) {
return value += p_value;
}
_ALWAYS_INLINE_ T postadd(T p_value) {
T old = value;
value += p_value;
return old;
}
_ALWAYS_INLINE_ T sub(T p_value) {
return value -= p_value;
}
_ALWAYS_INLINE_ T postsub(T p_value) {
T old = value;
value -= p_value;
return old;
}
_ALWAYS_INLINE_ T exchange_if_greater(T p_value) {
if (value < p_value) {
value = p_value;
}
return value;
}
_ALWAYS_INLINE_ T conditional_increment() {
if (value == 0) {
return 0;
} else {
return ++value;
}
}
_ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) :
value(p_value) {
}
};
class SafeFlag {
protected:
bool flag;
public:
_ALWAYS_INLINE_ bool is_set() const {
return flag;
}
_ALWAYS_INLINE_ void set() {
flag = true;
}
_ALWAYS_INLINE_ void clear() {
flag = false;
}
_ALWAYS_INLINE_ void set_to(bool p_value) {
flag = p_value;
}
_ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) :
flag(p_value) {}
};
class SafeRefCount {
uint32_t count = 0;
public:
_ALWAYS_INLINE_ bool ref() { // true on success
if (count != 0) {
++count;
return true;
} else {
return false;
}
}
_ALWAYS_INLINE_ uint32_t refval() { // none-zero on success
if (count != 0) {
return ++count;
} else {
return 0;
}
}
_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
return --count == 0;
}
_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
return --count;
}
_ALWAYS_INLINE_ uint32_t get() const {
return count;
}
_ALWAYS_INLINE_ void init(uint32_t p_value = 1) {
count = p_value;
}
};
#endif
#endif // SAFE_REFCOUNT_H

View file

@ -18,12 +18,6 @@
Displays a modal dialog box using the host OS' facilities. Execution is blocked until the dialog is closed.
</description>
</method>
<method name="can_use_threads" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the current host platform is using multiple threads.
</description>
</method>
<method name="close_midi_inputs">
<return type="void" />
<description>

View file

@ -126,9 +126,7 @@ int OS_Unix::unix_initialize_audio(int p_audio_driver) {
}
void OS_Unix::initialize_core() {
#if !defined(NO_THREADS)
init_thread_posix();
#endif
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);

View file

@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS)
#if defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)
#include "thread_posix.h"
@ -73,4 +73,4 @@ void init_thread_posix() {
Thread::_set_platform_funcs(&set_name, nullptr);
}
#endif
#endif // UNIX_ENABLED || PTHREAD_ENABLED

View file

@ -31,8 +31,6 @@
#ifndef THREAD_POSIX_H
#define THREAD_POSIX_H
#if !defined(NO_THREADS)
void init_thread_posix();
#endif
#endif // THREAD_POSIX_H

View file

@ -1706,13 +1706,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (rtm >= 0 && rtm < 3) {
#ifdef NO_THREADS
rtm = OS::RENDER_THREAD_UNSAFE; // No threads available on this platform.
#else
if (editor) {
rtm = OS::RENDER_THREAD_SAFE;
}
#endif
OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm);
}
@ -1932,11 +1928,9 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
// Print engine name and version
print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
#if !defined(NO_THREADS)
if (p_main_tid_override) {
Thread::main_thread_id = p_main_tid_override;
}
#endif
#ifdef TOOLS_ENABLED
if (editor || project_manager || cmdline_tool) {

View file

@ -197,9 +197,6 @@ void NoiseTexture2D::_update_texture() {
use_thread = false;
first_time = false;
}
#ifdef NO_THREADS
use_thread = false;
#endif
if (use_thread) {
if (!noise_thread.is_started()) {
noise_thread.start(_thread_function, this);

View file

@ -51,6 +51,7 @@ public:
static RemoteDebuggerPeer *create(const String &p_uri);
Error connect_to_host(const String &p_uri);
bool is_peer_connected() override;
int get_max_message_size() const override;
bool has_message() override;

View file

@ -184,51 +184,6 @@ Error AudioDriverWeb::capture_stop() {
return OK;
}
#ifdef NO_THREADS
/// ScriptProcessorNode implementation
AudioDriverScriptProcessor *AudioDriverScriptProcessor::singleton = nullptr;
void AudioDriverScriptProcessor::_process_callback() {
AudioDriverScriptProcessor::singleton->_audio_driver_capture();
AudioDriverScriptProcessor::singleton->_audio_driver_process();
}
Error AudioDriverScriptProcessor::create(int &p_buffer_samples, int p_channels) {
if (!godot_audio_has_script_processor()) {
return ERR_UNAVAILABLE;
}
return (Error)godot_audio_script_create(&p_buffer_samples, p_channels);
}
void AudioDriverScriptProcessor::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
godot_audio_script_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, &_process_callback);
}
/// AudioWorkletNode implementation (no threads)
AudioDriverWorklet *AudioDriverWorklet::singleton = nullptr;
Error AudioDriverWorklet::create(int &p_buffer_size, int p_channels) {
if (!godot_audio_has_worklet()) {
return ERR_UNAVAILABLE;
}
return (Error)godot_audio_worklet_create(p_channels);
}
void AudioDriverWorklet::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
_audio_driver_process();
godot_audio_worklet_start_no_threads(p_out_buf, p_out_buf_size, &_process_callback, p_in_buf, p_in_buf_size, &_capture_callback);
}
void AudioDriverWorklet::_process_callback(int p_pos, int p_samples) {
AudioDriverWorklet *driver = AudioDriverWorklet::singleton;
driver->_audio_driver_process(p_pos, p_samples);
}
void AudioDriverWorklet::_capture_callback(int p_pos, int p_samples) {
AudioDriverWorklet *driver = AudioDriverWorklet::singleton;
driver->_audio_driver_capture(p_pos, p_samples);
}
#else
/// AudioWorkletNode implementation (threads)
void AudioDriverWorklet::_audio_thread_func(void *p_data) {
AudioDriverWorklet *driver = static_cast<AudioDriverWorklet *>(p_data);
@ -290,4 +245,3 @@ void AudioDriverWorklet::finish_driver() {
quit = true; // Ask thread to quit.
thread.wait_to_finish();
}
#endif

View file

@ -89,46 +89,6 @@ public:
AudioDriverWeb() {}
};
#ifdef NO_THREADS
class AudioDriverScriptProcessor : public AudioDriverWeb {
private:
static void _process_callback();
static AudioDriverScriptProcessor *singleton;
protected:
Error create(int &p_buffer_samples, int p_channels) override;
void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
public:
virtual const char *get_name() const override { return "ScriptProcessor"; }
virtual void lock() override {}
virtual void unlock() override {}
AudioDriverScriptProcessor() { singleton = this; }
};
class AudioDriverWorklet : public AudioDriverWeb {
private:
static void _process_callback(int p_pos, int p_samples);
static void _capture_callback(int p_pos, int p_samples);
static AudioDriverWorklet *singleton;
protected:
virtual Error create(int &p_buffer_size, int p_output_channels) override;
virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
public:
virtual const char *get_name() const override { return "AudioWorklet"; }
virtual void lock() override {}
virtual void unlock() override {}
AudioDriverWorklet() { singleton = this; }
};
#else
class AudioDriverWorklet : public AudioDriverWeb {
private:
enum {
@ -156,6 +116,5 @@ public:
void lock() override;
void unlock() override;
};
#endif
#endif // AUDIO_DRIVER_WEB_H

View file

@ -239,9 +239,6 @@ OS_Web::OS_Web() {
godot_js_pwa_cb(&OS_Web::update_pwa_state_callback);
if (AudioDriverWeb::is_available()) {
#ifdef NO_THREADS
audio_drivers.push_back(memnew(AudioDriverScriptProcessor));
#endif
audio_drivers.push_back(memnew(AudioDriverWorklet));
}
for (int i = 0; i < audio_drivers.size(); i++) {

View file

@ -261,12 +261,7 @@ void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) {
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
args->nav_region = this;
if (p_on_thread && !OS::get_singleton()->can_use_threads()) {
WARN_PRINT("NavigationMesh bake 'on_thread' will be disabled as the current OS does not support multiple threads."
"\nAs a fallback the navigation mesh will bake on the main thread which can cause framerate issues.");
}
if (p_on_thread && OS::get_singleton()->can_use_threads()) {
if (p_on_thread) {
bake_thread.start(_bake_navigation_mesh, args);
} else {
_bake_navigation_mesh(args);

View file

@ -116,19 +116,11 @@ void AudioEffectRecordInstance::init() {
recording_data.clear(); //Clear data completely and reset length
is_recording = true;
#ifdef NO_THREADS
AudioServer::get_singleton()->add_update_callback(&AudioEffectRecordInstance::_update, this);
#else
io_thread.start(_thread_callback, this);
#endif
}
void AudioEffectRecordInstance::finish() {
#ifdef NO_THREADS
AudioServer::get_singleton()->remove_update_callback(&AudioEffectRecordInstance::_update, this);
#else
io_thread.wait_to_finish();
#endif
}
AudioEffectRecordInstance::~AudioEffectRecordInstance() {

View file

@ -38,8 +38,6 @@
#include "core/templates/command_queue_mt.h"
#include "tests/test_macros.h"
#if !defined(NO_THREADS)
namespace TestCommandQueue {
class ThreadWork {
@ -426,6 +424,4 @@ TEST_CASE("[Stress][CommandQueue] Stress test command queue") {
}
} // namespace TestCommandQueue
#endif // !defined(NO_THREADS)
#endif // TEST_COMMAND_QUEUE_H