Modernize atomics

- Based on C++11's `atomic`
- Reworked `SafeRefCount` (based on the rewrite by @hpvb)
- Replaced free atomic functions by the new `SafeNumeric<T>`
- Replaced wrong cases of `volatile` by the new `SafeFlag`
- Platform-specific implementations no longer needed

Co-authored-by: Hein-Pieter van Braam-Stewart <hp@tmm.cx>
This commit is contained in:
Pedro J. Estébanez 2021-01-31 13:34:42 +01:00
parent 6d89f675b1
commit 4485b43a57
54 changed files with 641 additions and 676 deletions

View file

@ -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...");
}
/////////////////////////////////////

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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(); \
} \
}

View file

@ -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();
}

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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());

View file

@ -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:

View file

@ -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() {

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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++) {

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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();
}
};

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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));
}

View file

@ -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;

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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();
}
///////////////////////////////////////////////////////////

View file

@ -90,7 +90,7 @@ private:
static RenderModeEnums render_mode_enums[];
volatile mutable bool dirty;
mutable SafeFlag dirty;
void _queue_update();
union ConnectionKey {

View file

@ -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;

View file

@ -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;

View file

@ -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");

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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();