diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 6dbede6d7e5..04e668049c4 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -2772,7 +2772,7 @@ void _Thread::_start_func(void *ud) {
 
 Error _Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) {
 
-	ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "Thread already started.");
+	ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started.");
 	ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER);
 	ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER);
 	ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER);
@@ -2781,7 +2781,7 @@ Error _Thread::start(Object *p_instance, const StringName &p_method, const Varia
 	target_method = p_method;
 	target_instance = p_instance;
 	userdata = p_userdata;
-	active = true;
+	active.set();
 
 	Ref<_Thread> *ud = memnew(Ref<_Thread>(this));
 
@@ -2799,17 +2799,17 @@ String _Thread::get_id() const {
 
 bool _Thread::is_active() const {
 
-	return active;
+	return active.is_set();
 }
 Variant _Thread::wait_to_finish() {
 
-	ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion.");
+	ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion.");
 	thread.wait_to_finish();
 	Variant r = ret;
-	active = false;
 	target_method = StringName();
 	target_instance = NULL;
 	userdata = Variant();
+	active.clear();
 
 	return r;
 }
@@ -2827,13 +2827,12 @@ void _Thread::_bind_methods() {
 }
 _Thread::_Thread() {
 
-	active = false;
 	target_instance = NULL;
 }
 
 _Thread::~_Thread() {
 
-	ERR_FAIL_COND_MSG(active, "Reference to a Thread object was lost while the thread is still running...");
+	ERR_FAIL_COND_MSG(active.is_set(), "Reference to a Thread object was lost while the thread is still running...");
 }
 
 /////////////////////////////////////
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 08f6c0b5c8c..fb7a276e934 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -40,6 +40,7 @@
 #include "core/os/os.h"
 #include "core/os/semaphore.h"
 #include "core/os/thread.h"
+#include "core/safe_refcount.h"
 
 class _ResourceLoader : public Object {
 	GDCLASS(_ResourceLoader, Object);
@@ -681,7 +682,7 @@ class _Thread : public Reference {
 protected:
 	Variant ret;
 	Variant userdata;
-	volatile bool active;
+	SafeFlag active;
 	Object *target_instance;
 	StringName target_method;
 	Thread thread;
diff --git a/core/cowdata.h b/core/cowdata.h
index 8739e5b57ef..d43688512ab 100644
--- a/core/cowdata.h
+++ b/core/cowdata.h
@@ -44,6 +44,9 @@ class CharString;
 template <class T, class V>
 class VMap;
 
+// CowData is relying on this to be true
+static_assert(sizeof(SafeNumeric<uint32_t>) == sizeof(uint32_t), "");
+
 template <class T>
 class CowData {
 	template <class TV>
@@ -58,12 +61,12 @@ private:
 
 	// internal helpers
 
-	_FORCE_INLINE_ uint32_t *_get_refcount() const {
+	_FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const {
 
 		if (!_ptr)
 			return NULL;
 
-		return reinterpret_cast<uint32_t *>(_ptr) - 2;
+		return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2;
 	}
 
 	_FORCE_INLINE_ uint32_t *_get_size() const {
@@ -193,9 +196,9 @@ void CowData<T>::_unref(void *p_data) {
 	if (!p_data)
 		return;
 
-	uint32_t *refc = _get_refcount();
+	SafeNumeric<uint32_t> *refc = _get_refcount();
 
-	if (atomic_decrement(refc) > 0)
+	if (refc->decrement() > 0)
 		return; // still in use
 	// clean up
 
@@ -219,15 +222,15 @@ void CowData<T>::_copy_on_write() {
 	if (!_ptr)
 		return;
 
-	uint32_t *refc = _get_refcount();
+	SafeNumeric<uint32_t> *refc = _get_refcount();
 
-	if (unlikely(*refc > 1)) {
+	if (unlikely(refc->get() > 1)) {
 		/* in use by more than me */
 		uint32_t current_size = *_get_size();
 
 		uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true);
 
-		*(mem_new - 2) = 1; //refcount
+		reinterpret_cast<SafeNumeric<uint32_t> *>(mem_new - 2)->set(1); //refcount
 		*(mem_new - 1) = current_size; //size
 
 		T *_data = (T *)(mem_new);
@@ -279,7 +282,7 @@ Error CowData<T>::resize(int p_size) {
 				uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true);
 				ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY);
 				*(ptr - 1) = 0; //size, currently none
-				*(ptr - 2) = 1; //refcount
+				reinterpret_cast<SafeNumeric<uint32_t> *>(ptr - 2)->set(1); //refcount
 
 				_ptr = (T *)ptr;
 
@@ -360,7 +363,7 @@ void CowData<T>::_ref(const CowData &p_from) {
 	if (!p_from._ptr)
 		return; //nothing to do
 
-	if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference
+	if (p_from._get_refcount()->increment() > 0) { // could reference
 		_ptr = p_from._ptr;
 	}
 }
diff --git a/core/error_macros.h b/core/error_macros.h
index 27d48c26b55..c9ab2d58039 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -31,7 +31,9 @@
 #ifndef ERROR_MACROS_H
 #define ERROR_MACROS_H
 
+#include "core/safe_refcount.h"
 #include "core/typedefs.h"
+
 /**
  * Error macros. Unlike exceptions and asserts, these macros try to maintain consistency and stability
  * inside the code. It is recommended to always return processable data, so in case of an error,
@@ -532,10 +534,10 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
  */
 #define WARN_DEPRECATED                                                                                                                                    \
 	{                                                                                                                                                      \
-		static volatile bool warning_shown = false;                                                                                                        \
-		if (!warning_shown) {                                                                                                                              \
+		static SafeFlag warning_shown;                                                                                                                     \
+		if (!warning_shown.is_set()) {                                                                                                                     \
 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \
-			warning_shown = true;                                                                                                                          \
+			warning_shown.set();                                                                                                                           \
 		}                                                                                                                                                  \
 	}
 
@@ -545,10 +547,10 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
  */
 #define WARN_DEPRECATED_MSG(m_msg)                                                                                                                                \
 	{                                                                                                                                                             \
-		static volatile bool warning_shown = false;                                                                                                               \
-		if (!warning_shown) {                                                                                                                                     \
+		static SafeFlag warning_shown;                                                                                                                            \
+		if (!warning_shown.is_set()) {                                                                                                                            \
 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, ERR_HANDLER_WARNING); \
-			warning_shown = true;                                                                                                                                 \
+			warning_shown.set();                                                                                                                                  \
 		}                                                                                                                                                         \
 	}
 
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index 152776dec40..3c755be451d 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -42,13 +42,13 @@ struct _IP_ResolverPrivate {
 
 	struct QueueItem {
 
-		volatile IP::ResolverStatus status;
+		SafeNumeric<IP::ResolverStatus> status;
 		IP_Address response;
 		String hostname;
 		IP::Type type;
 
 		void clear() {
-			status = IP::RESOLVER_STATUS_NONE;
+			status.set(IP::RESOLVER_STATUS_NONE);
 			response = IP_Address();
 			type = IP::TYPE_NONE;
 			hostname = "";
@@ -64,7 +64,7 @@ struct _IP_ResolverPrivate {
 	IP::ResolverID find_empty_id() const {
 
 		for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) {
-			if (queue[i].status == IP::RESOLVER_STATUS_NONE)
+			if (queue[i].status.get() == IP::RESOLVER_STATUS_NONE)
 				return i;
 		}
 		return IP::RESOLVER_INVALID_ID;
@@ -81,14 +81,14 @@ struct _IP_ResolverPrivate {
 
 		for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) {
 
-			if (queue[i].status != IP::RESOLVER_STATUS_WAITING)
+			if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING)
 				continue;
 			queue[i].response = IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type);
 
 			if (!queue[i].response.is_valid())
-				queue[i].status = IP::RESOLVER_STATUS_ERROR;
+				queue[i].status.set(IP::RESOLVER_STATUS_ERROR);
 			else
-				queue[i].status = IP::RESOLVER_STATUS_DONE;
+				queue[i].status.set(IP::RESOLVER_STATUS_DONE);
 		}
 	}
 
@@ -147,10 +147,10 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ
 	resolver->queue[id].type = p_type;
 	if (resolver->cache.has(key) && resolver->cache[key].is_valid()) {
 		resolver->queue[id].response = resolver->cache[key];
-		resolver->queue[id].status = IP::RESOLVER_STATUS_DONE;
+		resolver->queue[id].status.set(IP::RESOLVER_STATUS_DONE);
 	} else {
 		resolver->queue[id].response = IP_Address();
-		resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING;
+		resolver->queue[id].status.set(IP::RESOLVER_STATUS_WAITING);
 		if (resolver->thread.is_started())
 			resolver->sem.post();
 		else
@@ -166,12 +166,12 @@ IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const {
 	ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE);
 
 	resolver->mutex.lock();
-	if (resolver->queue[p_id].status == IP::RESOLVER_STATUS_NONE) {
+	if (resolver->queue[p_id].status.get() == IP::RESOLVER_STATUS_NONE) {
 		ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE");
 		resolver->mutex.unlock();
 		return IP::RESOLVER_STATUS_NONE;
 	}
-	IP::ResolverStatus res = resolver->queue[p_id].status;
+	IP::ResolverStatus res = resolver->queue[p_id].status.get();
 
 	resolver->mutex.unlock();
 	return res;
@@ -183,7 +183,7 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const {
 
 	resolver->mutex.lock();
 
-	if (resolver->queue[p_id].status != IP::RESOLVER_STATUS_DONE) {
+	if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) {
 		ERR_PRINTS("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
 		resolver->mutex.unlock();
 		return IP_Address();
@@ -201,7 +201,7 @@ void IP::erase_resolve_item(ResolverID p_id) {
 
 	resolver->mutex.lock();
 
-	resolver->queue[p_id].status = IP::RESOLVER_STATUS_NONE;
+	resolver->queue[p_id].status.set(IP::RESOLVER_STATUS_NONE);
 
 	resolver->mutex.unlock();
 }
diff --git a/core/object.cpp b/core/object.cpp
index 5a7f759806a..27d95dcf252 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -1946,7 +1946,7 @@ void *Object::get_script_instance_binding(int p_script_language_index) {
 	if (!_script_instance_bindings[p_script_language_index]) {
 		void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this);
 		if (script_data) {
-			atomic_increment(&instance_binding_count);
+			instance_binding_count.increment();
 			_script_instance_bindings[p_script_language_index] = script_data;
 		}
 	}
@@ -1976,7 +1976,6 @@ Object::Object() {
 	_can_translate = true;
 	_is_queued_for_deletion = false;
 	_emitting = false;
-	instance_binding_count = 0;
 	memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
 	script_instance = NULL;
 #ifdef DEBUG_ENABLED
diff --git a/core/object.h b/core/object.h
index 50e8468b2d3..8e20eea74dd 100644
--- a/core/object.h
+++ b/core/object.h
@@ -36,6 +36,7 @@
 #include "core/map.h"
 #include "core/object_id.h"
 #include "core/os/rw_lock.h"
+#include "core/safe_refcount.h"
 #include "core/set.h"
 #include "core/variant.h"
 #include "core/vmap.h"
@@ -512,7 +513,7 @@ private:
 	Variant _get_indexed_bind(const NodePath &p_name) const;
 
 	friend class Reference;
-	uint32_t instance_binding_count;
+	SafeNumeric<uint32_t> instance_binding_count;
 	void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS];
 
 protected:
diff --git a/core/os/memory.cpp b/core/os/memory.cpp
index 9a37e2194d3..5c47efc7628 100644
--- a/core/os/memory.cpp
+++ b/core/os/memory.cpp
@@ -65,11 +65,11 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
 #endif
 
 #ifdef DEBUG_ENABLED
-uint64_t Memory::mem_usage = 0;
-uint64_t Memory::max_usage = 0;
+SafeNumeric<uint64_t> Memory::mem_usage;
+SafeNumeric<uint64_t> Memory::max_usage;
 #endif
 
-uint64_t Memory::alloc_count = 0;
+SafeNumeric<uint64_t> Memory::alloc_count;
 
 void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
 
@@ -83,7 +83,7 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
 
 	ERR_FAIL_COND_V(!mem, NULL);
 
-	atomic_increment(&alloc_count);
+	alloc_count.increment();
 
 	if (prepad) {
 		uint64_t *s = (uint64_t *)mem;
@@ -92,8 +92,8 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
 		uint8_t *s8 = (uint8_t *)mem;
 
 #ifdef DEBUG_ENABLED
-		atomic_add(&mem_usage, p_bytes);
-		atomic_exchange_if_greater(&max_usage, mem_usage);
+		uint64_t new_mem_usage = mem_usage.add(p_bytes);
+		max_usage.exchange_if_greater(new_mem_usage);
 #endif
 		return s8 + PAD_ALIGN;
 	} else {
@@ -121,10 +121,10 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
 
 #ifdef DEBUG_ENABLED
 		if (p_bytes > *s) {
-			atomic_add(&mem_usage, p_bytes - *s);
-			atomic_exchange_if_greater(&max_usage, mem_usage);
+			uint64_t new_mem_usage = mem_usage.add(p_bytes - *s);
+			max_usage.exchange_if_greater(new_mem_usage);
 		} else {
-			atomic_sub(&mem_usage, *s - p_bytes);
+			mem_usage.sub(*s - p_bytes);
 		}
 #endif
 
@@ -165,14 +165,14 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) {
 	bool prepad = p_pad_align;
 #endif
 
-	atomic_decrement(&alloc_count);
+	alloc_count.decrement();
 
 	if (prepad) {
 		mem -= PAD_ALIGN;
 
 #ifdef DEBUG_ENABLED
 		uint64_t *s = (uint64_t *)mem;
-		atomic_sub(&mem_usage, *s);
+		mem_usage.sub(*s);
 #endif
 
 		free(mem);
@@ -189,7 +189,7 @@ uint64_t Memory::get_mem_available() {
 
 uint64_t Memory::get_mem_usage() {
 #ifdef DEBUG_ENABLED
-	return mem_usage;
+	return mem_usage.get();
 #else
 	return 0;
 #endif
@@ -197,7 +197,7 @@ uint64_t Memory::get_mem_usage() {
 
 uint64_t Memory::get_mem_max_usage() {
 #ifdef DEBUG_ENABLED
-	return max_usage;
+	return max_usage.get();
 #else
 	return 0;
 #endif
diff --git a/core/os/memory.h b/core/os/memory.h
index 1541464b869..84ff44c3a21 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -44,11 +44,11 @@ class Memory {
 
 	Memory();
 #ifdef DEBUG_ENABLED
-	static uint64_t mem_usage;
-	static uint64_t max_usage;
+	static SafeNumeric<uint64_t> mem_usage;
+	static SafeNumeric<uint64_t> max_usage;
 #endif
 
-	static uint64_t alloc_count;
+	static SafeNumeric<uint64_t> alloc_count;
 
 public:
 	static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index 7ace779da16..ff1c03ca8e7 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -34,13 +34,15 @@
 
 #if !defined(NO_THREADS)
 
+#include "core/safe_refcount.h"
+
 Error (*Thread::set_name_func)(const String &) = nullptr;
 void (*Thread::set_priority_func)(Thread::Priority) = nullptr;
 void (*Thread::init_func)() = nullptr;
 void (*Thread::term_func)() = nullptr;
 
 Thread::ID Thread::main_thread_id = 1;
-Thread::ID Thread::last_thread_id = 1;
+SafeNumeric<Thread::ID> Thread::last_thread_id{ 1 };
 thread_local Thread::ID Thread::caller_id = 1;
 
 void Thread::_set_platform_funcs(
@@ -79,7 +81,7 @@ void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_
 		std::thread empty_thread;
 		thread.swap(empty_thread);
 	}
-	id = atomic_increment(&last_thread_id);
+	id = last_thread_id.increment();
 	std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
 	thread.swap(new_thread);
 }
diff --git a/core/os/thread.h b/core/os/thread.h
index 38861a568bc..837f9323c37 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -34,6 +34,7 @@
 #include "core/typedefs.h"
 
 #if !defined(NO_THREADS)
+#include "core/safe_refcount.h"
 #include <thread>
 #endif
 
@@ -61,7 +62,7 @@ private:
 	friend class Main;
 
 	static ID main_thread_id;
-	static ID last_thread_id;
+	static SafeNumeric<ID> last_thread_id;
 
 	ID id;
 	static thread_local ID caller_id;
diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h
index 797dcc9b998..94783ddb660 100644
--- a/core/os/threaded_array_processor.h
+++ b/core/os/threaded_array_processor.h
@@ -40,7 +40,7 @@
 template <class C, class U>
 struct ThreadArrayProcessData {
 	uint32_t elements;
-	uint32_t index;
+	SafeNumeric<uint32_t> index;
 	C *instance;
 	U userdata;
 	void (C::*method)(uint32_t, U);
@@ -57,7 +57,7 @@ void process_array_thread(void *ud) {
 
 	T &data = *(T *)ud;
 	while (true) {
-		uint32_t index = atomic_increment(&data.index);
+		uint32_t index = data.index.increment();
 		if (index >= data.elements)
 			break;
 		data.process(index);
@@ -71,9 +71,9 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us
 	data.method = p_method;
 	data.instance = p_instance;
 	data.userdata = p_userdata;
-	data.index = 0;
+	data.index.set(0);
 	data.elements = p_elements;
-	data.process(data.index); //process first, let threads increment for next
+	data.process(0); //process first, let threads increment for next
 
 	int thread_count = OS::get_singleton()->get_processor_count();
 	Thread *threads = memnew_arr(Thread, thread_count);
diff --git a/core/pool_vector.h b/core/pool_vector.h
index 8798390d935..dc7a5fe81c3 100644
--- a/core/pool_vector.h
+++ b/core/pool_vector.h
@@ -33,6 +33,7 @@
 
 #include "core/os/copymem.h"
 #include "core/os/memory.h"
+#include "core/os/mutex.h"
 #include "core/os/rw_lock.h"
 #include "core/pool_allocator.h"
 #include "core/safe_refcount.h"
@@ -49,7 +50,7 @@ struct MemoryPool {
 	struct Alloc {
 
 		SafeRefCount refcount;
-		uint32_t lock;
+		SafeNumeric<uint32_t> lock;
 		void *mem;
 		PoolAllocator::ID pool_id;
 		size_t size;
@@ -113,7 +114,7 @@ class PoolVector {
 		alloc->size = old_alloc->size;
 		alloc->refcount.init();
 		alloc->pool_id = POOL_ALLOCATOR_INVALID_ID;
-		alloc->lock = 0;
+		alloc->lock.set(0);
 
 #ifdef DEBUG_ENABLED
 		MemoryPool::total_memory += alloc->size;
@@ -263,7 +264,7 @@ public:
 		_FORCE_INLINE_ void _ref(MemoryPool::Alloc *p_alloc) {
 			alloc = p_alloc;
 			if (alloc) {
-				if (atomic_increment(&alloc->lock) == 1) {
+				if (alloc->lock.increment() == 1) {
 					if (MemoryPool::memory_pool) {
 						//lock it and get mem
 					}
@@ -276,7 +277,7 @@ public:
 		_FORCE_INLINE_ void _unref() {
 
 			if (alloc) {
-				if (atomic_decrement(&alloc->lock) == 0) {
+				if (alloc->lock.decrement() == 0) {
 					if (MemoryPool::memory_pool) {
 						//put mem back
 					}
@@ -452,7 +453,7 @@ public:
 		return rs;
 	}
 
-	bool is_locked() const { return alloc && alloc->lock > 0; }
+	bool is_locked() const { return alloc && alloc->lock.get() > 0; }
 
 	inline T operator[](int p_index) const;
 
@@ -543,7 +544,7 @@ Error PoolVector<T>::resize(int p_size) {
 
 	} else {
 
-		ERR_FAIL_COND_V_MSG(alloc->lock > 0, ERR_LOCKED, "Can't resize PoolVector if locked."); //can't resize if locked!
+		ERR_FAIL_COND_V_MSG(alloc->lock.get() > 0, ERR_LOCKED, "Can't resize PoolVector if locked."); //can't resize if locked!
 	}
 
 	size_t new_size = sizeof(T) * p_size;
diff --git a/core/reference.cpp b/core/reference.cpp
index 1ba9e6151aa..9fd732e4ba4 100644
--- a/core/reference.cpp
+++ b/core/reference.cpp
@@ -67,7 +67,7 @@ bool Reference::reference() {
 		if (get_script_instance()) {
 			get_script_instance()->refcount_incremented();
 		}
-		if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
+		if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 				if (_script_instance_bindings[i]) {
 					ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
@@ -89,7 +89,7 @@ bool Reference::unreference() {
 			bool script_ret = get_script_instance()->refcount_decremented();
 			die = die && script_ret;
 		}
-		if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
+		if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 				if (_script_instance_bindings[i]) {
 					bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this);
diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp
deleted file mode 100644
index 376d9a99e4d..00000000000
--- a/core/safe_refcount.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*************************************************************************/
-/*  safe_refcount.cpp                                                    */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "safe_refcount.h"
-
-#if defined(_MSC_VER)
-
-/* Implementation for MSVC-Windows */
-
-// don't pollute my namespace!
-#include <windows.h>
-
-#define ATOMIC_CONDITIONAL_INCREMENT_BODY(m_pw, m_win_type, m_win_cmpxchg, m_cpp_type) \
-	/* try to increment until it actually works */                                     \
-	/* taken from boost */                                                             \
-	while (true) {                                                                     \
-		m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw));            \
-		if (tmp == 0)                                                                  \
-			return 0; /* if zero, can't add to it anymore */                           \
-		if (m_win_cmpxchg((m_win_type volatile *)(m_pw), tmp + 1, tmp) == tmp)         \
-			return tmp + 1;                                                            \
-	}
-
-#define ATOMIC_EXCHANGE_IF_GREATER_BODY(m_pw, m_val, m_win_type, m_win_cmpxchg, m_cpp_type) \
-	while (true) {                                                                          \
-		m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw));                 \
-		if (tmp >= m_val)                                                                   \
-			return tmp; /* already greater, or equal */                                     \
-		if (m_win_cmpxchg((m_win_type volatile *)(m_pw), m_val, tmp) == tmp)                \
-			return m_val;                                                                   \
-	}
-
-_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw){
-
-	ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t)
-}
-
-_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) {
-
-	return InterlockedDecrement((LONG volatile *)pw);
-}
-
-_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) {
-
-	return InterlockedIncrement((LONG volatile *)pw);
-}
-
-_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) {
-
-	return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val;
-}
-
-_ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) {
-
-	return InterlockedAdd((LONG volatile *)pw, val);
-}
-
-_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val){
-
-	ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t)
-}
-
-_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw){
-
-	ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t)
-}
-
-_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) {
-
-	return InterlockedDecrement64((LONGLONG volatile *)pw);
-}
-
-_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) {
-
-	return InterlockedIncrement64((LONGLONG volatile *)pw);
-}
-
-_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) {
-
-	return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val;
-}
-
-_ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) {
-
-	return InterlockedAdd64((LONGLONG volatile *)pw, val);
-}
-
-_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val){
-
-	ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t)
-}
-
-// The actual advertised functions; they'll call the right implementation
-
-uint32_t atomic_conditional_increment(volatile uint32_t *pw) {
-	return _atomic_conditional_increment_impl(pw);
-}
-
-uint32_t atomic_decrement(volatile uint32_t *pw) {
-	return _atomic_decrement_impl(pw);
-}
-
-uint32_t atomic_increment(volatile uint32_t *pw) {
-	return _atomic_increment_impl(pw);
-}
-
-uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val) {
-	return _atomic_sub_impl(pw, val);
-}
-
-uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val) {
-	return _atomic_add_impl(pw, val);
-}
-
-uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val) {
-	return _atomic_exchange_if_greater_impl(pw, val);
-}
-
-uint64_t atomic_conditional_increment(volatile uint64_t *pw) {
-	return _atomic_conditional_increment_impl(pw);
-}
-
-uint64_t atomic_decrement(volatile uint64_t *pw) {
-	return _atomic_decrement_impl(pw);
-}
-
-uint64_t atomic_increment(volatile uint64_t *pw) {
-	return _atomic_increment_impl(pw);
-}
-
-uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val) {
-	return _atomic_sub_impl(pw, val);
-}
-
-uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val) {
-	return _atomic_add_impl(pw, val);
-}
-
-uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val) {
-	return _atomic_exchange_if_greater_impl(pw, val);
-}
-#endif
diff --git a/core/safe_refcount.h b/core/safe_refcount.h
index f37d9d7d0d5..4840d90dd06 100644
--- a/core/safe_refcount.h
+++ b/core/safe_refcount.h
@@ -31,181 +31,292 @@
 #ifndef SAFE_REFCOUNT_H
 #define SAFE_REFCOUNT_H
 
-#include "core/os/mutex.h"
 #include "core/typedefs.h"
-#include "platform_config.h"
 
-// Atomic functions, these are used for multithread safe reference counters!
+#if !defined(NO_THREADS)
 
-#ifdef NO_THREADS
+#include <atomic>
 
-/* Bogus implementation unaware of multiprocessing */
+// Design goals for these classes:
+// - No automatic conversions or arithmetic operators,
+//   to keep explicit the use of atomics everywhere.
+// - Using acquire-release semantics, even to set the first value.
+//   The first value may be set relaxedly in many cases, but adding the distinction
+//   between relaxed and unrelaxed operation to the interface would make it needlessly
+//   flexible. There's negligible waste in having release semantics for the initial
+//   value and, as an important benefit, you can be sure the value is properly synchronized
+//   even with threads that are already running.
 
 template <class T>
-static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) {
-
-	if (*pw == 0)
-		return 0;
-
-	(*pw)++;
-
-	return *pw;
-}
-
-template <class T>
-static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) {
-
-	(*pw)--;
-
-	return *pw;
-}
-
-template <class T>
-static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) {
-
-	(*pw)++;
-
-	return *pw;
-}
-
-template <class T, class V>
-static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) {
-
-	(*pw) -= val;
-
-	return *pw;
-}
-
-template <class T, class V>
-static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) {
-
-	(*pw) += val;
-
-	return *pw;
-}
-
-template <class T, class V>
-static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) {
-
-	if (val > *pw)
-		*pw = val;
-
-	return *pw;
-}
-
-#elif defined(__GNUC__)
-
-/* Implementation for GCC & Clang */
-
-// GCC guarantees atomic intrinsics for sizes of 1, 2, 4 and 8 bytes.
-// Clang states it supports GCC atomic builtins.
-
-template <class T>
-static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) {
-
-	while (true) {
-		T tmp = static_cast<T const volatile &>(*pw);
-		if (tmp == 0)
-			return 0; // if zero, can't add to it anymore
-		if (__sync_val_compare_and_swap(pw, tmp, tmp + 1) == tmp)
-			return tmp + 1;
-	}
-}
-
-template <class T>
-static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) {
-
-	return __sync_sub_and_fetch(pw, 1);
-}
-
-template <class T>
-static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) {
-
-	return __sync_add_and_fetch(pw, 1);
-}
-
-template <class T, class V>
-static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) {
-
-	return __sync_sub_and_fetch(pw, val);
-}
-
-template <class T, class V>
-static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) {
-
-	return __sync_add_and_fetch(pw, val);
-}
-
-template <class T, class V>
-static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) {
-
-	while (true) {
-		T tmp = static_cast<T const volatile &>(*pw);
-		if (tmp >= val)
-			return tmp; // already greater, or equal
-		if (__sync_val_compare_and_swap(pw, tmp, val) == tmp)
-			return val;
-	}
-}
-
-#elif defined(_MSC_VER)
-// For MSVC use a separate compilation unit to prevent windows.h from polluting
-// the global namespace.
-uint32_t atomic_conditional_increment(volatile uint32_t *pw);
-uint32_t atomic_decrement(volatile uint32_t *pw);
-uint32_t atomic_increment(volatile uint32_t *pw);
-uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val);
-uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val);
-uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val);
-
-uint64_t atomic_conditional_increment(volatile uint64_t *pw);
-uint64_t atomic_decrement(volatile uint64_t *pw);
-uint64_t atomic_increment(volatile uint64_t *pw);
-uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val);
-uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val);
-uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val);
-
-#else
-//no threads supported?
-#error Must provide atomic functions for this platform or compiler!
-#endif
-
-struct SafeRefCount {
-
-	uint32_t count;
+class SafeNumeric {
+	std::atomic<T> value;
 
 public:
-	// destroy() is called when weak_count_ drops to zero.
+	_ALWAYS_INLINE_ void set(T p_value) {
+		value.store(p_value, std::memory_order_release);
+	}
 
+	_ALWAYS_INLINE_ T get() const {
+		return value.load(std::memory_order_acquire);
+	}
+
+	_ALWAYS_INLINE_ T increment() {
+		return value.fetch_add(1, std::memory_order_acq_rel) + 1;
+	}
+
+	// Returns the original value instead of the new one
+	_ALWAYS_INLINE_ T postincrement() {
+		return value.fetch_add(1, std::memory_order_acq_rel);
+	}
+
+	_ALWAYS_INLINE_ T decrement() {
+		return value.fetch_sub(1, std::memory_order_acq_rel) - 1;
+	}
+
+	// Returns the original value instead of the new one
+	_ALWAYS_INLINE_ T postdecrement() {
+		return value.fetch_sub(1, std::memory_order_acq_rel);
+	}
+
+	_ALWAYS_INLINE_ T add(T p_value) {
+		return value.fetch_add(p_value, std::memory_order_acq_rel) + p_value;
+	}
+
+	// Returns the original value instead of the new one
+	_ALWAYS_INLINE_ T postadd(T p_value) {
+		return value.fetch_add(p_value, std::memory_order_acq_rel);
+	}
+
+	_ALWAYS_INLINE_ T sub(T p_value) {
+		return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value;
+	}
+
+	// Returns the original value instead of the new one
+	_ALWAYS_INLINE_ T postsub(T p_value) {
+		return value.fetch_sub(p_value, std::memory_order_acq_rel);
+	}
+
+	_ALWAYS_INLINE_ T exchange_if_greater(T p_value) {
+		while (true) {
+			T tmp = value.load(std::memory_order_acquire);
+			if (tmp >= p_value) {
+				return tmp; // already greater, or equal
+			}
+			if (value.compare_exchange_weak(tmp, p_value, std::memory_order_release)) {
+				return p_value;
+			}
+		}
+	}
+
+	_ALWAYS_INLINE_ T conditional_increment() {
+		while (true) {
+			T c = value.load(std::memory_order_acquire);
+			if (c == 0) {
+				return 0;
+			}
+			if (value.compare_exchange_weak(c, c + 1, std::memory_order_release)) {
+				return c + 1;
+			}
+		}
+	}
+
+	_ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) {
+		set(p_value);
+	}
+};
+
+class SafeFlag {
+	std::atomic_bool flag;
+
+public:
+	_ALWAYS_INLINE_ bool is_set() const {
+		return flag.load(std::memory_order_acquire);
+	}
+
+	_ALWAYS_INLINE_ void set() {
+		flag.store(true, std::memory_order_release);
+	}
+
+	_ALWAYS_INLINE_ void clear() {
+		flag.store(false, std::memory_order_release);
+	}
+
+	_ALWAYS_INLINE_ void set_to(bool p_value) {
+		flag.store(p_value, std::memory_order_release);
+	}
+
+	_ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) {
+		set_to(p_value);
+	}
+};
+
+class SafeRefCount {
+	SafeNumeric<uint32_t> count;
+
+public:
 	_ALWAYS_INLINE_ bool ref() { // true on success
-
-		return atomic_conditional_increment(&count) != 0;
+		return count.conditional_increment() != 0;
 	}
 
 	_ALWAYS_INLINE_ uint32_t refval() { // none-zero on success
-
-		return atomic_conditional_increment(&count);
+		return count.conditional_increment();
 	}
 
 	_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
-
-		return atomic_decrement(&count) == 0;
+		return count.decrement() == 0;
 	}
 
 	_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
-
-		return atomic_decrement(&count);
+		return count.decrement();
 	}
 
-	_ALWAYS_INLINE_ uint32_t get() const { // nothrow
+	_ALWAYS_INLINE_ uint32_t get() const {
+		return count.get();
+	}
 
+	_ALWAYS_INLINE_ void init(uint32_t p_value = 1) {
+		count.set(p_value);
+	}
+};
+
+#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;
+
+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;
 	}
+
+	SafeRefCount() :
+			count(0) {}
 };
 
 #endif
+
+#endif // SAFE_REFCOUNT_H
diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp
index 9ecbd415f69..7f5cfb3a8c9 100644
--- a/editor/audio_stream_preview.cpp
+++ b/editor/audio_stream_preview.cpp
@@ -158,7 +158,7 @@ void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) {
 
 	preview->playback->stop();
 
-	preview->generating = false;
+	preview->generating.clear();
 }
 
 Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<AudioStream> &p_stream) {
@@ -175,7 +175,7 @@ Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<
 	Preview *preview = &previews[p_stream->get_instance_id()];
 	preview->base_stream = p_stream;
 	preview->playback = preview->base_stream->instance_playback();
-	preview->generating = true;
+	preview->generating.set();
 	preview->id = p_stream->get_instance_id();
 
 	float len_s = preview->base_stream->get_length();
@@ -220,7 +220,7 @@ void AudioStreamPreviewGenerator::_notification(int p_what) {
 	if (p_what == NOTIFICATION_PROCESS) {
 		List<ObjectID> to_erase;
 		for (Map<ObjectID, Preview>::Element *E = previews.front(); E; E = E->next()) {
-			if (!E->get().generating) {
+			if (!E->get().generating.is_set()) {
 				if (E->get().thread) {
 					E->get().thread->wait_to_finish();
 					memdelete(E->get().thread);
diff --git a/editor/audio_stream_preview.h b/editor/audio_stream_preview.h
index a2fa5c63c21..4725fb727c8 100644
--- a/editor/audio_stream_preview.h
+++ b/editor/audio_stream_preview.h
@@ -32,6 +32,7 @@
 #define AUDIO_STREAM_PREVIEW_H
 
 #include "core/os/thread.h"
+#include "core/safe_refcount.h"
 #include "scene/main/node.h"
 #include "servers/audio/audio_stream.h"
 
@@ -60,9 +61,20 @@ class AudioStreamPreviewGenerator : public Node {
 		Ref<AudioStreamPreview> preview;
 		Ref<AudioStream> base_stream;
 		Ref<AudioStreamPlayback> playback;
-		volatile bool generating;
+		SafeFlag generating;
 		ObjectID id;
 		Thread *thread;
+
+		// Needed for the bookkeeping of the Map
+		Preview &operator=(const Preview &p_rhs) {
+			preview = p_rhs.preview;
+			base_stream = p_rhs.base_stream;
+			playback = p_rhs.playback;
+			generating.set_to(generating.is_set());
+			id = p_rhs.id;
+			thread = p_rhs.thread;
+			return *this;
+		}
 	};
 
 	Map<ObjectID, Preview> previews;
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 764365f45d2..2c037594ebd 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1445,10 +1445,10 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
 
 void EditorFileSystem::update_script_classes() {
 
-	if (!update_script_classes_queued)
+	if (!update_script_classes_queued.is_set())
 		return;
 
-	update_script_classes_queued = false;
+	update_script_classes_queued.clear();
 	ScriptServer::global_classes_clear();
 	if (get_filesystem()) {
 		_scan_script_classes(get_filesystem());
@@ -1467,11 +1467,11 @@ void EditorFileSystem::update_script_classes() {
 }
 
 void EditorFileSystem::_queue_update_script_classes() {
-	if (update_script_classes_queued) {
+	if (update_script_classes_queued.is_set()) {
 		return;
 	}
 
-	update_script_classes_queued = true;
+	update_script_classes_queued.set();
 	call_deferred("update_script_classes");
 }
 
@@ -2146,7 +2146,7 @@ EditorFileSystem::EditorFileSystem() {
 	memdelete(da);
 
 	scan_total = 0;
-	update_script_classes_queued = false;
+	update_script_classes_queued.clear();
 	first_scan = true;
 	scan_changes_pending = false;
 	revalidate_import_files = false;
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 1e39f330c01..d768b919c64 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -34,8 +34,10 @@
 #include "core/os/dir_access.h"
 #include "core/os/thread.h"
 #include "core/os/thread_safe.h"
+#include "core/safe_refcount.h"
 #include "core/set.h"
 #include "scene/main/node.h"
+
 class FileAccess;
 
 struct EditorProgressBG;
@@ -231,7 +233,7 @@ class EditorFileSystem : public Node {
 	};
 
 	void _scan_script_classes(EditorFileSystemDirectory *p_dir);
-	volatile bool update_script_classes_queued;
+	SafeFlag update_script_classes_queued;
 	void _queue_update_script_classes();
 
 	String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 1d281b17d68..cf299f71dab 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5742,7 +5742,7 @@ static void _execute_thread(void *p_ud) {
 		eta->exitcode = err;
 	}
 
-	eta->done = true;
+	eta->done.set();
 }
 
 int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) {
@@ -5757,13 +5757,12 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p
 	eta.path = p_path;
 	eta.args = p_arguments;
 	eta.exitcode = 255;
-	eta.done = false;
 
 	int prev_len = 0;
 
 	eta.execute_output_thread.start(_execute_thread, &eta);
 
-	while (!eta.done) {
+	while (!eta.done.is_set()) {
 		eta.execute_output_mutex.lock();
 		if (prev_len != eta.output.length()) {
 			String to_add = eta.output.substr(prev_len, eta.output.length());
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 7d8768c3658..cddc0c84e49 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -31,6 +31,7 @@
 #ifndef EDITOR_NODE_H
 #define EDITOR_NODE_H
 
+#include "core/safe_refcount.h"
 #include "editor/editor_data.h"
 #include "editor/editor_folding.h"
 #include "editor/editor_run.h"
@@ -109,7 +110,7 @@ public:
 		Thread execute_output_thread;
 		Mutex execute_output_mutex;
 		int exitcode;
-		volatile bool done;
+		SafeFlag done;
 	};
 
 private:
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 303b4d16da4..18ddd05c398 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -217,8 +217,8 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
 
 void EditorResourcePreview::_thread() {
 
-	exited = false;
-	while (!exit) {
+	exited.clear();
+	while (!exit.is_set()) {
 
 		preview_sem.wait();
 		preview_mutex.lock();
@@ -350,7 +350,7 @@ void EditorResourcePreview::_thread() {
 			preview_mutex.unlock();
 		}
 	}
-	exited = true;
+	exited.set();
 }
 
 void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) {
@@ -462,9 +462,9 @@ void EditorResourcePreview::start() {
 
 void EditorResourcePreview::stop() {
 	if (thread.is_started()) {
-		exit = true;
+		exit.set();
 		preview_sem.post();
-		while (!exited) {
+		while (!exited.is_set()) {
 			OS::get_singleton()->delay_usec(10000);
 			VisualServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
 		}
@@ -475,8 +475,6 @@ void EditorResourcePreview::stop() {
 EditorResourcePreview::EditorResourcePreview() {
 	singleton = this;
 	order = 0;
-	exit = false;
-	exited = false;
 }
 
 EditorResourcePreview::~EditorResourcePreview() {
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 089158d2276..1283fabe118 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -33,6 +33,7 @@
 
 #include "core/os/semaphore.h"
 #include "core/os/thread.h"
+#include "core/safe_refcount.h"
 #include "scene/main/node.h"
 #include "scene/resources/texture.h"
 
@@ -73,8 +74,8 @@ class EditorResourcePreview : public Node {
 	Mutex preview_mutex;
 	Semaphore preview_sem;
 	Thread thread;
-	volatile bool exit;
-	volatile bool exited;
+	SafeFlag exit;
+	SafeFlag exited;
 
 	struct Item {
 		Ref<Texture> preview;
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 96a0cba4855..f24feae7e70 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -308,7 +308,7 @@ EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() {
 
 void EditorMaterialPreviewPlugin::_preview_done(const Variant &p_udata) {
 
-	preview_done = true;
+	preview_done.set();
 }
 
 void EditorMaterialPreviewPlugin::_bind_methods() {
@@ -336,10 +336,10 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Size
 
 		VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture
 
-		preview_done = false;
+		preview_done.clear();
 		VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant());
 
-		while (!preview_done) {
+		while (!preview_done.is_set()) {
 			OS::get_singleton()->delay_usec(10);
 		}
 
@@ -699,7 +699,7 @@ EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() {
 
 void EditorMeshPreviewPlugin::_preview_done(const Variant &p_udata) {
 
-	preview_done = true;
+	preview_done.set();
 }
 
 void EditorMeshPreviewPlugin::_bind_methods() {
@@ -737,10 +737,10 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 &p
 
 	VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture
 
-	preview_done = false;
+	preview_done.clear();
 	VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant());
 
-	while (!preview_done) {
+	while (!preview_done.is_set()) {
 		OS::get_singleton()->delay_usec(10);
 	}
 
@@ -819,7 +819,7 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
 
 void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) {
 
-	preview_done = true;
+	preview_done.set();
 }
 
 void EditorFontPreviewPlugin::_bind_methods() {
@@ -861,11 +861,11 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, c
 
 	font->draw(canvas_item, pos, sampled_text);
 
-	preview_done = false;
+	preview_done.clear();
 	VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture
 	VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant());
 
-	while (!preview_done) {
+	while (!preview_done.is_set()) {
 		OS::get_singleton()->delay_usec(10);
 	}
 
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index dce3754e8f1..8d2b8d6d13f 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -33,6 +33,8 @@
 
 #include "editor/editor_resource_preview.h"
 
+#include "core/safe_refcount.h"
+
 void post_process_preview(Ref<Image> p_image);
 
 class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
@@ -92,7 +94,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
 	RID light2;
 	RID light_instance2;
 	RID camera;
-	mutable volatile bool preview_done;
+	mutable SafeFlag preview_done;
 
 	void _preview_done(const Variant &p_udata);
 
@@ -137,7 +139,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
 	RID light2;
 	RID light_instance2;
 	RID camera;
-	mutable volatile bool preview_done;
+	mutable SafeFlag preview_done;
 
 	void _preview_done(const Variant &p_udata);
 
@@ -160,7 +162,7 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
 	RID viewport_texture;
 	RID canvas;
 	RID canvas_item;
-	mutable volatile bool preview_done;
+	mutable SafeFlag preview_done;
 
 	void _preview_done(const Variant &p_udata);
 
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 70424c58b69..7f9a90828a9 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -33,6 +33,7 @@
 #include "core/os/os.h"
 #include "core/os/thread.h"
 #include "core/print_string.h"
+#include "core/safe_refcount.h"
 
 #include <ConvectionKernels.h>
 
@@ -56,7 +57,7 @@ struct CVTTCompressionJobQueue {
 	CVTTCompressionJobParams job_params;
 	const CVTTCompressionRowTask *job_tasks;
 	uint32_t num_tasks;
-	uint32_t current_task;
+	SafeNumeric<uint32_t> current_task;
 };
 
 static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const CVTTCompressionRowTask &p_row_task) {
@@ -131,7 +132,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
 static void _digest_job_queue(void *p_job_queue) {
 	CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue);
 
-	for (uint32_t next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) {
+	for (uint32_t next_task = job_queue->current_task.increment(); next_task <= job_queue->num_tasks; next_task = job_queue->current_task.increment()) {
 		_digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]);
 	}
 }
@@ -263,7 +264,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS
 		PoolVector<CVTTCompressionRowTask>::Read tasks_rb = tasks.read();
 
 		job_queue.job_tasks = &tasks_rb[0];
-		job_queue.current_task = 0;
+		job_queue.current_task.set(0);
 		job_queue.num_tasks = static_cast<uint32_t>(tasks.size());
 
 		for (int i = 0; i < num_job_threads; i++) {
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 008e9cedc4f..43e8228ca45 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -995,9 +995,6 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
 
 NativeScriptLanguage::NativeScriptLanguage() {
 	NativeScriptLanguage::singleton = this;
-#ifndef NO_THREADS
-	has_objects_to_register = false;
-#endif
 
 #ifdef DEBUG_ENABLED
 	profiling = false;
@@ -1446,7 +1443,7 @@ void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeSc
 	MutexLock lock(mutex);
 	libs_to_init.insert(lib);
 	scripts_to_register.insert(script);
-	has_objects_to_register = true;
+	has_objects_to_register.set();
 }
 #endif
 
@@ -1539,7 +1536,7 @@ void NativeScriptLanguage::call_libraries_cb(const StringName &name) {
 
 void NativeScriptLanguage::frame() {
 #ifndef NO_THREADS
-	if (has_objects_to_register) {
+	if (has_objects_to_register.is_set()) {
 		MutexLock lock(mutex);
 		for (Set<Ref<GDNativeLibrary> >::Element *L = libs_to_init.front(); L; L = L->next()) {
 			init_library(L->get());
@@ -1549,7 +1546,7 @@ void NativeScriptLanguage::frame() {
 			register_script(S->get());
 		}
 		scripts_to_register.clear();
-		has_objects_to_register = false;
+		has_objects_to_register.clear();
 	}
 #endif
 
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index f36a5366a0a..45ebe950e32 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -37,6 +37,7 @@
 #include "core/ordered_hash_map.h"
 #include "core/os/thread_safe.h"
 #include "core/resource.h"
+#include "core/safe_refcount.h"
 #include "core/script_language.h"
 #include "core/self_list.h"
 #include "scene/main/node.h"
@@ -240,7 +241,7 @@ private:
 
 	Set<Ref<GDNativeLibrary> > libs_to_init;
 	Set<NativeScript *> scripts_to_register;
-	volatile bool has_objects_to_register; // so that we don't lock mutex every frame - it's rarely needed
+	SafeFlag has_objects_to_register; // so that we don't lock mutex every frame - it's rarely needed
 	void defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script);
 #endif
 
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 6f651feb9a1..467da332622 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -36,6 +36,7 @@
 #include "core/os/semaphore.h"
 #include "core/os/thread.h"
 #include "core/ring_buffer.h"
+#include "core/safe_refcount.h"
 #include "scene/resources/video_stream.h"
 #include "servers/audio_server.h"
 
@@ -114,7 +115,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
 	bool thread_eof;
 	Semaphore thread_sem;
 	Thread thread;
-	volatile bool thread_exit;
+	SafeFlag thread_exit;
 
 	static void _streaming_thread(void *ud);
 
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 9001dd2e8a2..b54c27d985e 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -37,6 +37,7 @@
 #include "core/os/file_access.h"
 #include "core/os/os.h"
 #include "core/project_settings.h"
+#include "core/safe_refcount.h"
 #include "core/version.h"
 #include "drivers/png/png_driver_common.h"
 #include "editor/editor_export.h"
@@ -267,38 +268,38 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 	Vector<PluginConfigAndroid> plugins;
 	String last_plugin_names;
 	uint64_t last_custom_build_time = 0;
-	volatile bool plugins_changed;
+	SafeFlag plugins_changed;
 	Mutex plugins_lock;
 	Vector<Device> devices;
-	volatile bool devices_changed;
+	SafeFlag devices_changed;
 	Mutex device_lock;
 	Thread check_for_changes_thread;
-	volatile bool quit_request;
+	SafeFlag quit_request;
 
 	static void _check_for_changes_poll_thread(void *ud) {
 		EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
 
-		while (!ea->quit_request) {
+		while (!ea->quit_request.is_set()) {
 			// Check for plugins updates
 			{
 				// Nothing to do if we already know the plugins have changed.
-				if (!ea->plugins_changed) {
+				if (!ea->plugins_changed.is_set()) {
 					Vector<PluginConfigAndroid> loaded_plugins = get_plugins();
 
 					ea->plugins_lock.lock();
 
 					if (ea->plugins.size() != loaded_plugins.size()) {
-						ea->plugins_changed = true;
+						ea->plugins_changed.set();
 					} else {
 						for (int i = 0; i < ea->plugins.size(); i++) {
 							if (ea->plugins[i].name != loaded_plugins[i].name) {
-								ea->plugins_changed = true;
+								ea->plugins_changed.set();
 								break;
 							}
 						}
 					}
 
-					if (ea->plugins_changed) {
+					if (ea->plugins_changed.is_set()) {
 						ea->plugins = loaded_plugins;
 					}
 
@@ -415,7 +416,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 					}
 
 					ea->devices = ndevices;
-					ea->devices_changed = true;
+					ea->devices_changed.set();
 				}
 
 				ea->device_lock.unlock();
@@ -426,7 +427,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 			uint64_t time = OS::get_singleton()->get_ticks_usec();
 			while (OS::get_singleton()->get_ticks_usec() - time < wait) {
 				OS::get_singleton()->delay_usec(1000 * sleep);
-				if (ea->quit_request)
+				if (ea->quit_request.is_set())
 					break;
 			}
 		}
@@ -1679,7 +1680,7 @@ public:
 			print_verbose("Found Android plugin " + plugins_configs[i].name);
 			r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false));
 		}
-		plugins_changed = false;
+		plugins_changed.clear();
 
 		Vector<String> abis = get_abis();
 		for (int i = 0; i < abis.size(); ++i) {
@@ -1750,20 +1751,20 @@ public:
 	}
 
 	virtual bool should_update_export_options() {
-		bool export_options_changed = plugins_changed;
+		bool export_options_changed = plugins_changed.is_set();
 		if (export_options_changed) {
 			// don't clear unless we're reporting true, to avoid race
-			plugins_changed = false;
+			plugins_changed.clear();
 		}
 		return export_options_changed;
 	}
 
 	virtual bool poll_export() {
 
-		bool dc = devices_changed;
+		bool dc = devices_changed.is_set();
 		if (dc) {
 			// don't clear unless we're reporting true, to avoid race
-			devices_changed = false;
+			devices_changed.clear();
 		}
 		return dc;
 	}
@@ -3280,15 +3281,13 @@ public:
 		run_icon.instance();
 		run_icon->create_from_image(img);
 
-		devices_changed = true;
-
-		plugins_changed = true;
-		quit_request = false;
+		devices_changed.set();
+		plugins_changed.set();
 		check_for_changes_thread.start(_check_for_changes_poll_thread, this);
 	}
 
 	~EditorExportPlatformAndroid() {
-		quit_request = true;
+		quit_request.set();
 		check_for_changes_thread.wait_to_finish();
 	}
 };
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index d773c87af13..b82a7e9f810 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -218,14 +218,14 @@ void GodotIOJavaWrapper::stop_video() {
 	}
 }
 
-// volatile because it can be changed from non-main thread and we need to
+// SafeNumeric because it can be changed from non-main thread and we need to
 // ensure the change is immediately visible to other threads.
-static volatile int virtual_keyboard_height;
+static SafeNumeric<int> virtual_keyboard_height;
 
 int GodotIOJavaWrapper::get_vk_height() {
-	return virtual_keyboard_height;
+	return virtual_keyboard_height.get();
 }
 
 void GodotIOJavaWrapper::set_vk_height(int p_height) {
-	virtual_keyboard_height = p_height;
+	virtual_keyboard_height.set(p_height);
 }
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index ceb296f5480..3ee86f76521 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -35,6 +35,7 @@
 #include "core/os/file_access.h"
 #include "core/os/os.h"
 #include "core/project_settings.h"
+#include "core/safe_refcount.h"
 #include "core/version.h"
 #include "editor/editor_export.h"
 #include "editor/editor_node.h"
@@ -55,9 +56,9 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 	Ref<ImageTexture> logo;
 
 	// Plugins
-	volatile bool plugins_changed;
+	SafeFlag plugins_changed;
 	Thread check_for_changes_thread;
-	volatile bool quit_request;
+	SafeFlag quit_request;
 	Mutex plugins_lock;
 	Vector<PluginConfigIOS> plugins;
 
@@ -144,20 +145,20 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 	static void _check_for_changes_poll_thread(void *ud) {
 		EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
 
-		while (!ea->quit_request) {
+		while (!ea->quit_request.is_set()) {
 			// Nothing to do if we already know the plugins have changed.
-			if (!ea->plugins_changed) {
+			if (!ea->plugins_changed.is_set()) {
 
 				ea->plugins_lock.lock();
 
 				Vector<PluginConfigIOS> loaded_plugins = get_plugins();
 
 				if (ea->plugins.size() != loaded_plugins.size()) {
-					ea->plugins_changed = true;
+					ea->plugins_changed.set();
 				} else {
 					for (int i = 0; i < ea->plugins.size(); i++) {
 						if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) {
-							ea->plugins_changed = true;
+							ea->plugins_changed.set();
 							break;
 						}
 					}
@@ -171,7 +172,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
 			while (OS::get_singleton()->get_ticks_usec() - time < wait) {
 				OS::get_singleton()->delay_usec(300000);
 
-				if (ea->quit_request) {
+				if (ea->quit_request.is_set()) {
 					break;
 				}
 			}
@@ -188,10 +189,10 @@ public:
 	virtual Ref<Texture> get_logo() const { return logo; }
 
 	virtual bool should_update_export_options() {
-		bool export_options_changed = plugins_changed;
+		bool export_options_changed = plugins_changed.is_set();
 		if (export_options_changed) {
 			// don't clear unless we're reporting true, to avoid race
-			plugins_changed = false;
+			plugins_changed.clear();
 		}
 		return export_options_changed;
 	}
@@ -371,7 +372,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
 		r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false));
 	}
 
-	plugins_changed = false;
+	plugins_changed.clear();
 	plugins = found_plugins;
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false));
@@ -1836,14 +1837,13 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
 	logo.instance();
 	logo->create_from_image(img);
 
-	plugins_changed = true;
-	quit_request = false;
+	plugins_changed.set();
 
 	check_for_changes_thread.start(_check_for_changes_poll_thread, this);
 }
 
 EditorExportPlatformIOS::~EditorExportPlatformIOS() {
-	quit_request = true;
+	quit_request.set();
 	check_for_changes_thread.wait_to_finish();
 }
 
diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp
index 6e3c1101d17..b8a7fe78a53 100644
--- a/platform/x11/joypad_linux.cpp
+++ b/platform/x11/joypad_linux.cpp
@@ -92,13 +92,12 @@ JoypadLinux::JoypadLinux(InputDefault *in) {
 #else
 	print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
 #endif
-	exit_monitor = false;
 	input = in;
 	joy_thread.start(joy_thread_func, this);
 }
 
 JoypadLinux::~JoypadLinux() {
-	exit_monitor = true;
+	exit_monitor.set();
 	joy_thread.wait_to_finish();
 	close_joypad();
 }
@@ -172,7 +171,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
 	udev_monitor_enable_receiving(mon);
 	int fd = udev_monitor_get_fd(mon);
 
-	while (!exit_monitor) {
+	while (!exit_monitor.is_set()) {
 
 		fd_set fds;
 		struct timeval tv;
@@ -220,7 +219,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
 
 void JoypadLinux::monitor_joypads() {
 
-	while (!exit_monitor) {
+	while (!exit_monitor.is_set()) {
 		joy_mutex.lock();
 
 		DIR *input_directory;
diff --git a/platform/x11/joypad_linux.h b/platform/x11/joypad_linux.h
index 21374d1b825..6d7d61395fe 100644
--- a/platform/x11/joypad_linux.h
+++ b/platform/x11/joypad_linux.h
@@ -74,7 +74,7 @@ private:
 #ifdef UDEV_ENABLED
 	bool use_udev;
 #endif
-	bool exit_monitor;
+	SafeFlag exit_monitor;
 	Mutex joy_mutex;
 	Thread joy_thread;
 	InputDefault *input;
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index e9348b4c0f7..1fa9264bb94 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -36,14 +36,14 @@
 
 void AudioStreamPlayer2D::_mix_audio() {
 
-	if (!stream_playback.is_valid() || !active ||
+	if (!stream_playback.is_valid() || !active.is_set() ||
 			(stream_paused && !stream_paused_fade_out)) {
 		return;
 	}
 
-	if (setseek >= 0.0) {
-		stream_playback->start(setseek);
-		setseek = -1.0; //reset seek
+	if (setseek.get() >= 0.0) {
+		stream_playback->start(setseek.get());
+		setseek.set(-1.0); //reset seek
 	}
 
 	//get data
@@ -58,7 +58,8 @@ void AudioStreamPlayer2D::_mix_audio() {
 	stream_playback->mix(buffer, pitch_scale, buffer_size);
 
 	//write all outputs
-	for (int i = 0; i < output_count; i++) {
+	int oc = output_count.get();
+	for (int i = 0; i < oc; i++) {
 
 		Output current = outputs[i];
 
@@ -132,14 +133,14 @@ void AudioStreamPlayer2D::_mix_audio() {
 		prev_outputs[i] = current;
 	}
 
-	prev_output_count = output_count;
+	prev_output_count = oc;
 
 	//stream is no longer active, disable this.
 	if (!stream_playback->is_playing()) {
-		active = false;
+		active.clear();
 	}
 
-	output_ready = false;
+	output_ready.clear();
 	stream_paused_fade_in = false;
 	stream_paused_fade_out = false;
 }
@@ -174,7 +175,7 @@ void AudioStreamPlayer2D::_notification(int p_what) {
 
 		//update anything related to position first, if possible of course
 
-		if (!output_ready) {
+		if (!output_ready.is_set()) {
 			List<Viewport *> viewports;
 			Ref<World2D> world_2d = get_world_2d();
 			ERR_FAIL_COND(world_2d.is_null());
@@ -245,21 +246,21 @@ void AudioStreamPlayer2D::_notification(int p_what) {
 				}
 			}
 
-			output_count = new_output_count;
-			output_ready = true;
+			output_count.set(new_output_count);
+			output_ready.set();
 		}
 
 		//start playing if requested
-		if (setplay >= 0.0) {
-			setseek = setplay;
-			active = true;
-			setplay = -1;
+		if (setplay.get() >= 0.0) {
+			setseek.set(setplay.get());
+			active.set();
+			setplay.set(-1);
 			//do not update, this makes it easier to animate (will shut off otherwise)
 			//_change_notify("playing"); //update property in editor
 		}
 
 		//stop playing if no longer active
-		if (!active) {
+		if (!active.is_set()) {
 			set_physics_process_internal(false);
 			//do not update, this makes it easier to animate (will shut off otherwise)
 			//_change_notify("playing"); //update property in editor
@@ -277,8 +278,8 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
 	if (stream_playback.is_valid()) {
 		stream_playback.unref();
 		stream.unref();
-		active = false;
-		setseek = -1;
+		active.clear();
+		setseek.set(-1);
 	}
 
 	if (p_stream.is_valid()) {
@@ -323,8 +324,8 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
 	}
 
 	if (stream_playback.is_valid()) {
-		setplay = p_from_pos;
-		output_ready = false;
+		setplay.set(p_from_pos);
+		output_ready.clear();
 		set_physics_process_internal(true);
 	}
 }
@@ -332,23 +333,23 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
 void AudioStreamPlayer2D::seek(float p_seconds) {
 
 	if (stream_playback.is_valid()) {
-		setseek = p_seconds;
+		setseek.set(p_seconds);
 	}
 }
 
 void AudioStreamPlayer2D::stop() {
 
 	if (stream_playback.is_valid()) {
-		active = false;
+		active.clear();
 		set_physics_process_internal(false);
-		setplay = -1;
+		setplay.set(-1);
 	}
 }
 
 bool AudioStreamPlayer2D::is_playing() const {
 
 	if (stream_playback.is_valid()) {
-		return active || setplay >= 0;
+		return active.is_set() || setplay.get() >= 0;
 	}
 
 	return false;
@@ -357,8 +358,9 @@ bool AudioStreamPlayer2D::is_playing() const {
 float AudioStreamPlayer2D::get_playback_position() {
 
 	if (stream_playback.is_valid()) {
-		if (setseek >= 0.0) {
-			return setseek;
+		float ss = setseek.get();
+		if (ss >= 0.0) {
+			return ss;
 		}
 		return stream_playback->get_playback_position();
 	}
@@ -401,7 +403,7 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
 }
 bool AudioStreamPlayer2D::_is_active() const {
 
-	return active;
+	return active.is_set();
 }
 
 void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
@@ -535,14 +537,11 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() {
 	volume_db = 0;
 	pitch_scale = 1.0;
 	autoplay = false;
-	setseek = -1;
-	active = false;
-	output_count = 0;
+	setseek.set(-1);
 	prev_output_count = 0;
 	max_distance = 2000;
 	attenuation = 1;
-	setplay = -1;
-	output_ready = false;
+	setplay.set(-1);
 	area_mask = 1;
 	stream_paused = false;
 	stream_paused_fade_in = false;
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index d156c5120e3..23708990a16 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -31,6 +31,7 @@
 #ifndef AUDIO_STREAM_PLAYER_2D_H
 #define AUDIO_STREAM_PLAYER_2D_H
 
+#include "core/safe_refcount.h"
 #include "scene/2d/node_2d.h"
 #include "servers/audio/audio_stream.h"
 #include "servers/audio_server.h"
@@ -54,8 +55,8 @@ private:
 	};
 
 	Output outputs[MAX_OUTPUTS];
-	volatile int output_count;
-	volatile bool output_ready;
+	SafeNumeric<int> output_count;
+	SafeFlag output_ready;
 
 	//these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks)
 	Output prev_outputs[MAX_OUTPUTS];
@@ -65,9 +66,9 @@ private:
 	Ref<AudioStream> stream;
 	Vector<AudioFrame> mix_buffer;
 
-	volatile float setseek;
-	volatile bool active;
-	volatile float setplay;
+	SafeNumeric<float> setseek;
+	SafeFlag active;
+	SafeNumeric<float> setplay;
 
 	float volume_db;
 	float pitch_scale;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 474a0b0ccdc..b740c403974 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -130,15 +130,15 @@ void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tig
 
 void AudioStreamPlayer3D::_mix_audio() {
 
-	if (!stream_playback.is_valid() || !active ||
+	if (!stream_playback.is_valid() || !active.is_set() ||
 			(stream_paused && !stream_paused_fade_out)) {
 		return;
 	}
 
 	bool started = false;
-	if (setseek >= 0.0) {
-		stream_playback->start(setseek);
-		setseek = -1.0; //reset seek
+	if (setseek.get() >= 0.0) {
+		stream_playback->start(setseek.get());
+		setseek.set(-1.0); //reset seek
 		started = true;
 	}
 
@@ -152,15 +152,15 @@ void AudioStreamPlayer3D::_mix_audio() {
 	}
 
 	// Mix if we're not paused or we're fading out
-	if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) {
+	if ((output_count.get() > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) {
 
 		float output_pitch_scale = 0.0;
-		if (output_count) {
+		if (output_count.get()) {
 			//used for doppler, not realistic but good enough
-			for (int i = 0; i < output_count; i++) {
+			for (int i = 0; i < output_count.get(); i++) {
 				output_pitch_scale += outputs[i].pitch_scale;
 			}
-			output_pitch_scale /= float(output_count);
+			output_pitch_scale /= float(output_count.get());
 		} else {
 			output_pitch_scale = 1.0;
 		}
@@ -169,7 +169,7 @@ void AudioStreamPlayer3D::_mix_audio() {
 	}
 
 	//write all outputs
-	for (int i = 0; i < output_count; i++) {
+	for (int i = 0; i < output_count.get(); i++) {
 
 		Output current = outputs[i];
 
@@ -284,14 +284,14 @@ void AudioStreamPlayer3D::_mix_audio() {
 		prev_outputs[i] = current;
 	}
 
-	prev_output_count = output_count;
+	prev_output_count = output_count.get();
 
 	//stream is no longer active, disable this.
 	if (!stream_playback->is_playing()) {
-		active = false;
+		active.clear();
 	}
 
-	output_ready = false;
+	output_ready.clear();
 	stream_paused_fade_in = false;
 	stream_paused_fade_out = false;
 }
@@ -367,7 +367,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 
 		//update anything related to position first, if possible of course
 
-		if (!output_ready) {
+		if (!output_ready.is_set()) {
 
 			Vector3 linear_velocity;
 
@@ -611,21 +611,21 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 					break;
 			}
 
-			output_count = new_output_count;
-			output_ready = true;
+			output_count.set(new_output_count);
+			output_ready.set();
 		}
 
 		//start playing if requested
-		if (setplay >= 0.0) {
-			setseek = setplay;
-			active = true;
-			setplay = -1;
+		if (setplay.get() >= 0.0) {
+			setseek.set(setplay.get());
+			active.set();
+			setplay.set(-1);
 			//do not update, this makes it easier to animate (will shut off otherwise)
 			///_change_notify("playing"); //update property in editor
 		}
 
 		//stop playing if no longer active
-		if (!active) {
+		if (!active.is_set()) {
 			set_physics_process_internal(false);
 			//do not update, this makes it easier to animate (will shut off otherwise)
 			//_change_notify("playing"); //update property in editor
@@ -643,8 +643,8 @@ void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
 	if (stream_playback.is_valid()) {
 		stream_playback.unref();
 		stream.unref();
-		active = false;
-		setseek = -1;
+		active.clear();
+		setseek.set(-1);
 	}
 
 	if (p_stream.is_valid()) {
@@ -707,8 +707,8 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
 	}
 
 	if (stream_playback.is_valid()) {
-		setplay = p_from_pos;
-		output_ready = false;
+		setplay.set(p_from_pos);
+		output_ready.clear();
 		set_physics_process_internal(true);
 	}
 }
@@ -716,23 +716,23 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
 void AudioStreamPlayer3D::seek(float p_seconds) {
 
 	if (stream_playback.is_valid()) {
-		setseek = p_seconds;
+		setseek.set(p_seconds);
 	}
 }
 
 void AudioStreamPlayer3D::stop() {
 
 	if (stream_playback.is_valid()) {
-		active = false;
+		active.clear();
 		set_physics_process_internal(false);
-		setplay = -1;
+		setplay.set(-1);
 	}
 }
 
 bool AudioStreamPlayer3D::is_playing() const {
 
 	if (stream_playback.is_valid()) {
-		return active || setplay >= 0;
+		return active.is_set() || setplay.get() >= 0;
 	}
 
 	return false;
@@ -741,8 +741,9 @@ bool AudioStreamPlayer3D::is_playing() const {
 float AudioStreamPlayer3D::get_playback_position() {
 
 	if (stream_playback.is_valid()) {
-		if (setseek >= 0.0) {
-			return setseek;
+		float ss = setseek.get();
+		if (ss >= 0.0) {
+			return ss;
 		}
 		return stream_playback->get_playback_position();
 	}
@@ -785,7 +786,7 @@ void AudioStreamPlayer3D::_set_playing(bool p_enable) {
 }
 bool AudioStreamPlayer3D::_is_active() const {
 
-	return active;
+	return active.is_set();
 }
 
 void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const {
@@ -1055,13 +1056,10 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() {
 	max_db = 3;
 	pitch_scale = 1.0;
 	autoplay = false;
-	setseek = -1;
-	active = false;
-	output_count = 0;
+	setseek.set(-1);
 	prev_output_count = 0;
 	max_distance = 0;
-	setplay = -1;
-	output_ready = false;
+	setplay.set(-1);
 	area_mask = 1;
 	emission_angle = 45;
 	emission_angle_enabled = false;
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 2fd66d4f37d..c5b6d1cc936 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -31,6 +31,7 @@
 #ifndef AUDIO_STREAM_PLAYER_3D_H
 #define AUDIO_STREAM_PLAYER_3D_H
 
+#include "core/safe_refcount.h"
 #include "scene/3d/spatial.h"
 #include "scene/3d/spatial_velocity_tracker.h"
 #include "servers/audio/audio_filter_sw.h"
@@ -89,8 +90,8 @@ private:
 	};
 
 	Output outputs[MAX_OUTPUTS];
-	volatile int output_count;
-	volatile bool output_ready;
+	SafeNumeric<int> output_count;
+	SafeFlag output_ready;
 
 	//these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks)
 	Output prev_outputs[MAX_OUTPUTS];
@@ -100,9 +101,9 @@ private:
 	Ref<AudioStream> stream;
 	Vector<AudioFrame> mix_buffer;
 
-	volatile float setseek;
-	volatile bool active;
-	volatile float setplay;
+	SafeNumeric<float> setseek;
+	SafeFlag active;
+	SafeNumeric<float> setplay;
 
 	AttenuationModel attenuation_model;
 	float unit_db;
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index 8e83b2466bb..9b4d92092c1 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -1113,7 +1113,7 @@ void CPUParticles::_update_particle_data_buffer() {
 			ptr += 17;
 		}
 
-		can_update = true;
+		can_update.set();
 	}
 
 	update_mutex.unlock();
@@ -1142,9 +1142,9 @@ void CPUParticles::_update_render_thread() {
 
 	update_mutex.lock();
 
-	if (can_update) {
+	if (can_update.is_set()) {
 		VS::get_singleton()->multimesh_set_as_bulk_array(multimesh, particle_data);
-		can_update = false; //wait for next time
+		can_update.clear(); //wait for next time
 	}
 
 	update_mutex.unlock();
@@ -1210,7 +1210,7 @@ void CPUParticles::_notification(int p_what) {
 				ptr += 17;
 			}
 
-			can_update = true;
+			can_update.set();
 		}
 	}
 }
@@ -1550,8 +1550,6 @@ CPUParticles::CPUParticles() {
 		flags[i] = false;
 	}
 
-	can_update = false;
-
 	set_color(Color(1, 1, 1, 1));
 }
 
diff --git a/scene/3d/cpu_particles.h b/scene/3d/cpu_particles.h
index b88f783160c..f94e42b04fb 100644
--- a/scene/3d/cpu_particles.h
+++ b/scene/3d/cpu_particles.h
@@ -32,6 +32,7 @@
 #define CPU_PARTICLES_H
 
 #include "core/rid.h"
+#include "core/safe_refcount.h"
 #include "scene/3d/visual_instance.h"
 
 class CPUParticles : public GeometryInstance {
@@ -144,7 +145,7 @@ private:
 
 	Transform inv_emission_transform;
 
-	volatile bool can_update;
+	SafeFlag can_update;
 
 	DrawOrder draw_order;
 
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index dce47ec82d8..cd1efa78eac 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -101,7 +101,7 @@ void AudioStreamPlayer::_mix_audio() {
 		use_fadeout = false;
 	}
 
-	if (!stream_playback.is_valid() || !active ||
+	if (!stream_playback.is_valid() || !active.is_set() ||
 			(stream_paused && !stream_paused_fade)) {
 		return;
 	}
@@ -114,25 +114,25 @@ void AudioStreamPlayer::_mix_audio() {
 		return;
 	}
 
-	if (setstop) {
+	if (setstop.is_set()) {
 		_mix_internal(true);
 		stream_playback->stop();
-		setstop = false;
+		setstop.clear();
 	}
 
-	if (setseek >= 0.0 && !stop_has_priority) {
+	if (setseek.get() >= 0.0 && !stop_has_priority.is_set()) {
 		if (stream_playback->is_playing()) {
 
 			//fade out to avoid pops
 			_mix_internal(true);
 		}
 
-		stream_playback->start(setseek);
-		setseek = -1.0; //reset seek
+		stream_playback->start(setseek.get());
+		setseek.set(-1.0); //reset seek
 		mix_volume_db = volume_db; //reset ramp
 	}
 
-	stop_has_priority = false;
+	stop_has_priority.clear();
 
 	_mix_internal(false);
 }
@@ -149,8 +149,8 @@ void AudioStreamPlayer::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
 
-		if (!active || (setseek < 0 && !stream_playback->is_playing())) {
-			active = false;
+		if (!active.is_set() || (setseek.get() < 0 && !stream_playback->is_playing())) {
+			active.clear();
 			set_process_internal(false);
 			emit_signal("finished");
 		}
@@ -177,7 +177,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
 
 	AudioServer::get_singleton()->lock();
 
-	if (active && stream_playback.is_valid() && !stream_paused) {
+	if (active.is_set() && stream_playback.is_valid() && !stream_paused) {
 		//changing streams out of the blue is not a great idea, but at least
 		//lets try to somehow avoid a click
 
@@ -204,9 +204,9 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
 	if (stream_playback.is_valid()) {
 		stream_playback.unref();
 		stream.unref();
-		active = false;
-		setseek = -1;
-		setstop = false;
+		active.clear();
+		setseek.set(-1);
+		setstop.clear();
 	}
 
 	if (p_stream.is_valid()) {
@@ -247,9 +247,9 @@ void AudioStreamPlayer::play(float p_from_pos) {
 
 	if (stream_playback.is_valid()) {
 		//mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks
-		setseek = p_from_pos;
-		stop_has_priority = false;
-		active = true;
+		setseek.set(p_from_pos);
+		stop_has_priority.clear();
+		active.set();
 		set_process_internal(true);
 	}
 }
@@ -257,22 +257,22 @@ void AudioStreamPlayer::play(float p_from_pos) {
 void AudioStreamPlayer::seek(float p_seconds) {
 
 	if (stream_playback.is_valid()) {
-		setseek = p_seconds;
+		setseek.set(p_seconds);
 	}
 }
 
 void AudioStreamPlayer::stop() {
 
-	if (stream_playback.is_valid() && active) {
-		setstop = true;
-		stop_has_priority = true;
+	if (stream_playback.is_valid() && active.is_set()) {
+		setstop.set();
+		stop_has_priority.set();
 	}
 }
 
 bool AudioStreamPlayer::is_playing() const {
 
 	if (stream_playback.is_valid()) {
-		return active && !setstop; //&& stream_playback->is_playing();
+		return active.is_set() && !setstop.is_set(); //&& stream_playback->is_playing();
 	}
 
 	return false;
@@ -281,8 +281,9 @@ bool AudioStreamPlayer::is_playing() const {
 float AudioStreamPlayer::get_playback_position() {
 
 	if (stream_playback.is_valid()) {
-		if (setseek >= 0.0) {
-			return setseek;
+		float ss = setseek.get();
+		if (ss >= 0.0) {
+			return ss;
 		}
 		return stream_playback->get_playback_position();
 	}
@@ -335,7 +336,7 @@ void AudioStreamPlayer::_set_playing(bool p_enable) {
 }
 bool AudioStreamPlayer::_is_active() const {
 
-	return active;
+	return active.is_set();
 }
 
 void AudioStreamPlayer::set_stream_paused(bool p_pause) {
@@ -435,13 +436,11 @@ AudioStreamPlayer::AudioStreamPlayer() {
 	pitch_scale = 1.0;
 	volume_db = 0;
 	autoplay = false;
-	setseek = -1;
-	active = false;
+	setseek.set(-1);
 	stream_paused = false;
 	stream_paused_fade = false;
 	mix_target = MIX_TARGET_STEREO;
 	fadeout_buffer.resize(512);
-	setstop = false;
 	use_fadeout = false;
 
 	AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index bb1c21e5271..18f5360618b 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -31,6 +31,7 @@
 #ifndef AUDIO_STREAM_PLAYER_H
 #define AUDIO_STREAM_PLAYER_H
 
+#include "core/safe_refcount.h"
 #include "scene/main/node.h"
 #include "servers/audio/audio_stream.h"
 
@@ -52,10 +53,10 @@ private:
 	Vector<AudioFrame> fadeout_buffer;
 	bool use_fadeout;
 
-	volatile float setseek;
-	volatile bool active;
-	volatile bool setstop;
-	volatile bool stop_has_priority;
+	SafeNumeric<float> setseek;
+	SafeFlag active;
+	SafeFlag setstop;
+	SafeFlag stop_has_priority;
 
 	float mix_volume_db;
 	float pitch_scale;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 84aee07105f..2e62b58efe9 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -49,7 +49,7 @@ Error HTTPRequest::_parse_url(const String &p_url) {
 	got_response = false;
 	body_len = -1;
 	body.resize(0);
-	downloaded = 0;
+	downloaded.set(0);
 	redirections = 0;
 
 	String url_lower = url.to_lower();
@@ -108,10 +108,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h
 
 	requesting = true;
 
-	if (use_threads) {
+	if (use_threads.is_set()) {
 
-		thread_done = false;
-		thread_request_quit = false;
+		thread_done.clear();
+		thread_request_quit.clear();
 		client->set_blocking_mode(true);
 		thread.start(_thread_func, this);
 	} else {
@@ -137,7 +137,7 @@ void HTTPRequest::_thread_func(void *p_userdata) {
 	if (err != OK) {
 		hr->call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
 	} else {
-		while (!hr->thread_request_quit) {
+		while (!hr->thread_request_quit.is_set()) {
 
 			bool exit = hr->_update_connection();
 			if (exit)
@@ -146,7 +146,7 @@ void HTTPRequest::_thread_func(void *p_userdata) {
 		}
 	}
 
-	hr->thread_done = true;
+	hr->thread_done.set();
 }
 
 void HTTPRequest::cancel_request() {
@@ -156,10 +156,10 @@ void HTTPRequest::cancel_request() {
 	if (!requesting)
 		return;
 
-	if (!use_threads) {
+	if (!use_threads.is_set()) {
 		set_process_internal(false);
 	} else {
-		thread_request_quit = true;
+		thread_request_quit.set();
 		thread.wait_to_finish();
 	}
 
@@ -188,7 +188,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
 	List<String> rheaders;
 	client->get_response_headers(&rheaders);
 	response_headers.resize(0);
-	downloaded = 0;
+	downloaded.set(0);
 	for (List<String>::Element *E = rheaders.front(); E; E = E->next()) {
 		response_headers.push_back(E->get());
 	}
@@ -229,7 +229,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
 				got_response = false;
 				body_len = -1;
 				body.resize(0);
-				downloaded = 0;
+				downloaded.set(0);
 				redirections = new_redirs;
 				*ret_value = false;
 				return true;
@@ -349,7 +349,7 @@ bool HTTPRequest::_update_connection() {
 			client->poll();
 
 			PoolByteArray chunk = client->read_response_body_chunk();
-			downloaded += chunk.size();
+			downloaded.add(chunk.size());
 
 			if (file) {
 				PoolByteArray::Read r = chunk.read();
@@ -362,14 +362,14 @@ bool HTTPRequest::_update_connection() {
 				body.append_array(chunk);
 			}
 
-			if (body_size_limit >= 0 && downloaded > body_size_limit) {
+			if (body_size_limit >= 0 && downloaded.get() > body_size_limit) {
 				call_deferred("_request_done", RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PoolByteArray());
 				return true;
 			}
 
 			if (body_len >= 0) {
 
-				if (downloaded == body_len) {
+				if (downloaded.get() == body_len) {
 					call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
 					return true;
 				}
@@ -404,7 +404,7 @@ void HTTPRequest::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
 
-		if (use_threads)
+		if (use_threads.is_set())
 			return;
 		bool done = _update_connection();
 		if (done) {
@@ -424,12 +424,12 @@ void HTTPRequest::_notification(int p_what) {
 void HTTPRequest::set_use_threads(bool p_use) {
 
 	ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
-	use_threads = p_use;
+	use_threads.set_to(p_use);
 }
 
 bool HTTPRequest::is_using_threads() const {
 
-	return use_threads;
+	return use_threads.is_set();
 }
 
 void HTTPRequest::set_body_size_limit(int p_bytes) {
@@ -483,7 +483,7 @@ int HTTPRequest::get_max_redirects() const {
 
 int HTTPRequest::get_downloaded_bytes() const {
 
-	return downloaded;
+	return downloaded.get();
 }
 int HTTPRequest::get_body_size() const {
 	return body_len;
@@ -577,9 +577,6 @@ HTTPRequest::HTTPRequest() {
 	request_sent = false;
 	requesting = false;
 	client.instance();
-	use_threads = false;
-	thread_done = false;
-	downloaded = 0;
 	body_size_limit = -1;
 	file = NULL;
 
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 0a4bd01d454..d5cdbcbed41 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -34,6 +34,7 @@
 #include "core/io/http_client.h"
 #include "core/os/file_access.h"
 #include "core/os/thread.h"
+#include "core/safe_refcount.h"
 #include "node.h"
 #include "scene/main/timer.h"
 
@@ -74,7 +75,7 @@ private:
 	bool request_sent;
 	Ref<HTTPClient> client;
 	PoolByteArray body;
-	volatile bool use_threads;
+	SafeFlag use_threads;
 
 	bool got_response;
 	int response_code;
@@ -85,7 +86,7 @@ private:
 	FileAccess *file;
 
 	int body_len;
-	volatile int downloaded;
+	SafeNumeric<int> downloaded;
 	int body_size_limit;
 
 	int redirections;
@@ -103,8 +104,8 @@ private:
 	Error _parse_url(const String &p_url);
 	Error _request();
 
-	volatile bool thread_done;
-	volatile bool thread_request_quit;
+	SafeFlag thread_done;
+	SafeFlag thread_request_quit;
 
 	Thread thread;
 
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 97001fca8cf..329cffad9f3 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1276,10 +1276,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
 }
 
 void VisualShader::_update_shader() const {
-	if (!dirty)
+	if (!dirty.is_set())
 		return;
 
-	dirty = false;
+	dirty.clear();
 
 	StringBuilder global_code;
 	StringBuilder global_code_per_node;
@@ -1439,11 +1439,11 @@ void VisualShader::_update_shader() const {
 }
 
 void VisualShader::_queue_update() {
-	if (dirty) {
+	if (dirty.is_set()) {
 		return;
 	}
 
-	dirty = true;
+	dirty.set();
 	call_deferred("_update_shader");
 }
 
@@ -1463,7 +1463,7 @@ void VisualShader::_input_type_changed(Type p_type, int p_id) {
 }
 
 void VisualShader::rebuild() {
-	dirty = true;
+	dirty.set();
 	_update_shader();
 }
 
@@ -1524,7 +1524,7 @@ VisualShader::VisualShader() {
 		graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150);
 	}
 
-	dirty = true;
+	dirty.set();
 }
 
 ///////////////////////////////////////////////////////////
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index aebbeec9f80..61fd96a5c27 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -90,7 +90,7 @@ private:
 
 	static RenderModeEnums render_mode_enums[];
 
-	volatile mutable bool dirty;
+	mutable SafeFlag dirty;
 	void _queue_update();
 
 	union ConnectionKey {
diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp
index d38ecf02ae2..a3a8fd2c567 100644
--- a/servers/audio/audio_rb_resampler.cpp
+++ b/servers/audio/audio_rb_resampler.cpp
@@ -128,7 +128,7 @@ bool AudioRBResampler::mix(AudioFrame *p_dest, int p_frames) {
 		if (src_read > read_space)
 			src_read = read_space;
 
-		rb_read_pos = (rb_read_pos + src_read) & rb_mask;
+		rb_read_pos.set((rb_read_pos.get() + src_read) & rb_mask);
 
 		// Create fadeout effect for the end of stream (note that it can be because of slow writer)
 		if (p_frames - target_todo > 0) {
@@ -182,8 +182,8 @@ Error AudioRBResampler::setup(int p_channels, int p_src_mix_rate, int p_target_m
 	src_mix_rate = p_src_mix_rate;
 	target_mix_rate = p_target_mix_rate;
 	offset = 0;
-	rb_read_pos = 0;
-	rb_write_pos = 0;
+	rb_read_pos.set(0);
+	rb_write_pos.set(0);
 
 	//avoid maybe strange noises upon load
 	for (unsigned int i = 0; i < (rb_len * channels); i++) {
@@ -205,8 +205,8 @@ void AudioRBResampler::clear() {
 	memdelete_arr(read_buf);
 	rb = NULL;
 	offset = 0;
-	rb_read_pos = 0;
-	rb_write_pos = 0;
+	rb_read_pos.set(0);
+	rb_write_pos.set(0);
 	read_buf = NULL;
 }
 
@@ -215,8 +215,8 @@ AudioRBResampler::AudioRBResampler() {
 	rb = NULL;
 	offset = 0;
 	read_buf = NULL;
-	rb_read_pos = 0;
-	rb_write_pos = 0;
+	rb_read_pos.set(0);
+	rb_write_pos.set(0);
 
 	rb_bits = 0;
 	rb_len = 0;
diff --git a/servers/audio/audio_rb_resampler.h b/servers/audio/audio_rb_resampler.h
index 0e40d4bf9aa..6c43b0a9c1f 100644
--- a/servers/audio/audio_rb_resampler.h
+++ b/servers/audio/audio_rb_resampler.h
@@ -32,6 +32,7 @@
 #define AUDIO_RB_RESAMPLER_H
 
 #include "core/os/memory.h"
+#include "core/safe_refcount.h"
 #include "core/typedefs.h"
 #include "servers/audio_server.h"
 
@@ -45,8 +46,8 @@ struct AudioRBResampler {
 	uint32_t src_mix_rate;
 	uint32_t target_mix_rate;
 
-	volatile int rb_read_pos;
-	volatile int rb_write_pos;
+	SafeNumeric<int> rb_read_pos;
+	SafeNumeric<int> rb_write_pos;
 
 	int32_t offset; //contains the fractional remainder of the resampler
 	enum {
@@ -63,8 +64,8 @@ struct AudioRBResampler {
 
 public:
 	_FORCE_INLINE_ void flush() {
-		rb_read_pos = 0;
-		rb_write_pos = 0;
+		rb_read_pos.set(0);
+		rb_write_pos.set(0);
 		offset = 0;
 	}
 
@@ -79,8 +80,8 @@ public:
 	_FORCE_INLINE_ int get_writer_space() const {
 		int space, r, w;
 
-		r = rb_read_pos;
-		w = rb_write_pos;
+		r = rb_read_pos.get();
+		w = rb_write_pos.get();
 
 		if (r == w) {
 			space = rb_len - 1;
@@ -96,8 +97,8 @@ public:
 	_FORCE_INLINE_ int get_reader_space() const {
 		int space, r, w;
 
-		r = rb_read_pos;
-		w = rb_write_pos;
+		r = rb_read_pos.get();
+		w = rb_write_pos.get();
 
 		if (r == w) {
 			space = 0;
@@ -111,7 +112,7 @@ public:
 	}
 
 	_FORCE_INLINE_ bool has_data() const {
-		return rb && rb_read_pos != rb_write_pos;
+		return rb && rb_read_pos.get() != rb_write_pos.get();
 	}
 
 	_FORCE_INLINE_ float *get_write_buffer() { return read_buf; }
@@ -119,49 +120,53 @@ public:
 
 		ERR_FAIL_COND(p_frames >= rb_len);
 
+		int wp = rb_write_pos.get();
+
 		switch (channels) {
 			case 1: {
 
 				for (uint32_t i = 0; i < p_frames; i++) {
 
-					rb[rb_write_pos] = read_buf[i];
-					rb_write_pos = (rb_write_pos + 1) & rb_mask;
+					rb[wp] = read_buf[i];
+					wp = (wp + 1) & rb_mask;
 				}
 			} break;
 			case 2: {
 
 				for (uint32_t i = 0; i < p_frames; i++) {
 
-					rb[(rb_write_pos << 1) + 0] = read_buf[(i << 1) + 0];
-					rb[(rb_write_pos << 1) + 1] = read_buf[(i << 1) + 1];
-					rb_write_pos = (rb_write_pos + 1) & rb_mask;
+					rb[(wp << 1) + 0] = read_buf[(i << 1) + 0];
+					rb[(wp << 1) + 1] = read_buf[(i << 1) + 1];
+					wp = (wp + 1) & rb_mask;
 				}
 			} break;
 			case 4: {
 
 				for (uint32_t i = 0; i < p_frames; i++) {
 
-					rb[(rb_write_pos << 2) + 0] = read_buf[(i << 2) + 0];
-					rb[(rb_write_pos << 2) + 1] = read_buf[(i << 2) + 1];
-					rb[(rb_write_pos << 2) + 2] = read_buf[(i << 2) + 2];
-					rb[(rb_write_pos << 2) + 3] = read_buf[(i << 2) + 3];
-					rb_write_pos = (rb_write_pos + 1) & rb_mask;
+					rb[(wp << 2) + 0] = read_buf[(i << 2) + 0];
+					rb[(wp << 2) + 1] = read_buf[(i << 2) + 1];
+					rb[(wp << 2) + 2] = read_buf[(i << 2) + 2];
+					rb[(wp << 2) + 3] = read_buf[(i << 2) + 3];
+					wp = (wp + 1) & rb_mask;
 				}
 			} break;
 			case 6: {
 
 				for (uint32_t i = 0; i < p_frames; i++) {
 
-					rb[(rb_write_pos * 6) + 0] = read_buf[(i * 6) + 0];
-					rb[(rb_write_pos * 6) + 1] = read_buf[(i * 6) + 1];
-					rb[(rb_write_pos * 6) + 2] = read_buf[(i * 6) + 2];
-					rb[(rb_write_pos * 6) + 3] = read_buf[(i * 6) + 3];
-					rb[(rb_write_pos * 6) + 4] = read_buf[(i * 6) + 4];
-					rb[(rb_write_pos * 6) + 5] = read_buf[(i * 6) + 5];
-					rb_write_pos = (rb_write_pos + 1) & rb_mask;
+					rb[(wp * 6) + 0] = read_buf[(i * 6) + 0];
+					rb[(wp * 6) + 1] = read_buf[(i * 6) + 1];
+					rb[(wp * 6) + 2] = read_buf[(i * 6) + 2];
+					rb[(wp * 6) + 3] = read_buf[(i * 6) + 3];
+					rb[(wp * 6) + 4] = read_buf[(i * 6) + 4];
+					rb[(wp * 6) + 5] = read_buf[(i * 6) + 5];
+					wp = (wp + 1) & rb_mask;
 				}
 			} break;
 		}
+
+		rb_write_pos.set(wp);
 	}
 
 	int get_channel_count() const;
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.cpp b/servers/physics_2d/physics_2d_server_wrap_mt.cpp
index 046c3acd49c..76e9da280c1 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.cpp
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.cpp
@@ -34,7 +34,7 @@
 
 void Physics2DServerWrapMT::thread_exit() {
 
-	exit = true;
+	exit.set();
 }
 
 void Physics2DServerWrapMT::thread_step(real_t p_delta) {
@@ -56,9 +56,9 @@ void Physics2DServerWrapMT::thread_loop() {
 
 	physics_2d_server->init();
 
-	exit = false;
-	step_thread_up = true;
-	while (!exit) {
+	exit.clear();
+	step_thread_up.set();
+	while (!exit.is_set()) {
 		// flush commands one by one, until exit is requested
 		command_queue.wait_and_flush_one();
 	}
@@ -110,7 +110,7 @@ void Physics2DServerWrapMT::init() {
 
 		//OS::get_singleton()->release_rendering_thread();
 		thread.start(_thread_callback, this);
-		while (!step_thread_up) {
+		while (!step_thread_up.is_set()) {
 			OS::get_singleton()->delay_usec(1000);
 		}
 	} else {
@@ -149,7 +149,6 @@ Physics2DServerWrapMT::Physics2DServerWrapMT(Physics2DServer *p_contained, bool
 	physics_2d_server = p_contained;
 	create_thread = p_create_thread;
 	step_pending = 0;
-	step_thread_up = false;
 
 	pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc");
 
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h
index 0c9e560a504..1939feb2699 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -34,6 +34,7 @@
 #include "core/command_queue_mt.h"
 #include "core/os/thread.h"
 #include "core/project_settings.h"
+#include "core/safe_refcount.h"
 #include "servers/physics_2d_server.h"
 
 #ifdef DEBUG_SYNC
@@ -53,9 +54,9 @@ class Physics2DServerWrapMT : public Physics2DServer {
 
 	Thread::ID server_thread;
 	Thread::ID main_thread;
-	volatile bool exit;
+	SafeFlag exit;
 	Thread thread;
-	volatile bool step_thread_up;
+	SafeFlag step_thread_up;
 	bool create_thread;
 
 	Semaphore step_sem;
diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h
index b5977c82a68..6c01c5a5092 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -38,6 +38,7 @@
 #include "core/math/octree.h"
 #include "core/os/semaphore.h"
 #include "core/os/thread.h"
+#include "core/safe_refcount.h"
 #include "core/self_list.h"
 #include "servers/arvr/arvr_interface.h"
 
@@ -591,7 +592,7 @@ public:
 	void _gi_probe_bake_thread();
 	static void _gi_probe_bake_threads(void *);
 
-	volatile bool probe_bake_thread_exit;
+	bool probe_bake_thread_exit;
 	Thread probe_bake_thread;
 	Semaphore probe_bake_sem;
 	Mutex probe_bake_mutex;
diff --git a/servers/visual/visual_server_wrap_mt.cpp b/servers/visual/visual_server_wrap_mt.cpp
index 64288ad1565..26a61e6c9a4 100644
--- a/servers/visual/visual_server_wrap_mt.cpp
+++ b/servers/visual/visual_server_wrap_mt.cpp
@@ -34,12 +34,12 @@
 
 void VisualServerWrapMT::thread_exit() {
 
-	exit = true;
+	exit.set();
 }
 
 void VisualServerWrapMT::thread_draw(bool p_swap_buffers, double frame_step) {
 
-	if (!atomic_decrement(&draw_pending)) {
+	if (!draw_pending.decrement()) {
 
 		visual_server->draw(p_swap_buffers, frame_step);
 	}
@@ -47,7 +47,7 @@ void VisualServerWrapMT::thread_draw(bool p_swap_buffers, double frame_step) {
 
 void VisualServerWrapMT::thread_flush() {
 
-	atomic_decrement(&draw_pending);
+	draw_pending.decrement();
 }
 
 void VisualServerWrapMT::_thread_callback(void *_instance) {
@@ -65,9 +65,9 @@ void VisualServerWrapMT::thread_loop() {
 
 	visual_server->init();
 
-	exit = false;
-	draw_thread_up = true;
-	while (!exit) {
+	exit.clear();
+	draw_thread_up.set();
+	while (!exit.is_set()) {
 		// flush commands one by one, until exit is requested
 		command_queue.wait_and_flush_one();
 	}
@@ -83,7 +83,7 @@ void VisualServerWrapMT::sync() {
 
 	if (create_thread) {
 
-		atomic_increment(&draw_pending);
+		draw_pending.increment();
 		command_queue.push_and_sync(this, &VisualServerWrapMT::thread_flush);
 	} else {
 
@@ -95,7 +95,7 @@ void VisualServerWrapMT::draw(bool p_swap_buffers, double frame_step) {
 
 	if (create_thread) {
 
-		atomic_increment(&draw_pending);
+		draw_pending.increment();
 		command_queue.push(this, &VisualServerWrapMT::thread_draw, p_swap_buffers, frame_step);
 	} else {
 
@@ -113,7 +113,7 @@ void VisualServerWrapMT::init() {
 			thread.start(_thread_callback, this);
 			print_verbose("VisualServerWrapMT: Starting render thread");
 		}
-		while (!draw_thread_up) {
+		while (!draw_thread_up.is_set()) {
 			OS::get_singleton()->delay_usec(1000);
 		}
 		print_verbose("VisualServerWrapMT: Finished render thread");
@@ -174,8 +174,6 @@ VisualServerWrapMT::VisualServerWrapMT(VisualServer *p_contained, bool p_create_
 
 	visual_server = p_contained;
 	create_thread = p_create_thread;
-	draw_pending = 0;
-	draw_thread_up = false;
 	pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc");
 
 	if (!p_create_thread) {
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index 1b620c509ce..e4e6dd0da15 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -33,6 +33,7 @@
 
 #include "core/command_queue_mt.h"
 #include "core/os/thread.h"
+#include "core/safe_refcount.h"
 #include "servers/visual_server.h"
 
 class VisualServerWrapMT : public VisualServer {
@@ -46,12 +47,12 @@ class VisualServerWrapMT : public VisualServer {
 	void thread_loop();
 
 	Thread::ID server_thread;
-	volatile bool exit;
+	SafeFlag exit;
 	Thread thread;
-	volatile bool draw_thread_up;
+	SafeFlag draw_thread_up;
 	bool create_thread;
 
-	uint64_t draw_pending;
+	SafeNumeric<uint64_t> draw_pending;
 	void thread_draw(bool p_swap_buffers, double frame_step);
 	void thread_flush();