Merge pull request #45618 from RandomShaper/modernize_mt_3.2

Backport of all the multi-threading modernization (3.2)
This commit is contained in:
Hein-Pieter van Braam
2021-02-18 20:47:24 +01:00
committed by GitHub
191 changed files with 1688 additions and 4522 deletions
+16 -50
View File
@@ -2689,12 +2689,14 @@ void _Marshalls::_bind_methods() {
Error _Semaphore::wait() {
return semaphore->wait();
semaphore.wait();
return OK; // Can't fail anymore; keep compat
}
Error _Semaphore::post() {
return semaphore->post();
semaphore.post();
return OK; // Can't fail anymore; keep compat
}
void _Semaphore::_bind_methods() {
@@ -2703,31 +2705,21 @@ void _Semaphore::_bind_methods() {
ClassDB::bind_method(D_METHOD("post"), &_Semaphore::post);
}
_Semaphore::_Semaphore() {
semaphore = Semaphore::create();
}
_Semaphore::~_Semaphore() {
memdelete(semaphore);
}
///////////////
void _Mutex::lock() {
mutex->lock();
mutex.lock();
}
Error _Mutex::try_lock() {
return mutex->try_lock();
return mutex.try_lock();
}
void _Mutex::unlock() {
mutex->unlock();
mutex.unlock();
}
void _Mutex::_bind_methods() {
@@ -2737,16 +2729,6 @@ void _Mutex::_bind_methods() {
ClassDB::bind_method(D_METHOD("unlock"), &_Mutex::unlock);
}
_Mutex::_Mutex() {
mutex = Mutex::create();
}
_Mutex::~_Mutex() {
memdelete(mutex);
}
///////////////
void _Thread::_start_func(void *ud) {
@@ -2790,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);
@@ -2799,49 +2781,35 @@ 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));
Thread::Settings s;
s.priority = (Thread::Priority)p_priority;
thread = Thread::create(_start_func, ud, s);
if (!thread) {
active = false;
target_method = StringName();
target_instance = NULL;
userdata = Variant();
return ERR_CANT_CREATE;
}
thread.start(_start_func, ud, s);
return OK;
}
String _Thread::get_id() const {
if (!thread)
return String();
return itos(thread->get_id());
return itos(thread.get_id());
}
bool _Thread::is_active() const {
return active;
return active.is_set();
}
Variant _Thread::wait_to_finish() {
ERR_FAIL_COND_V_MSG(!thread, Variant(), "Thread must exist to wait for its completion.");
ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion.");
Thread::wait_to_finish(thread);
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();
if (thread)
memdelete(thread);
thread = NULL;
active.clear();
return r;
}
@@ -2859,14 +2827,12 @@ void _Thread::_bind_methods() {
}
_Thread::_Thread() {
active = false;
thread = NULL;
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...");
}
/////////////////////////////////////
+5 -10
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);
@@ -652,7 +653,7 @@ public:
class _Mutex : public Reference {
GDCLASS(_Mutex, Reference);
Mutex *mutex;
Mutex mutex;
static void _bind_methods();
@@ -660,24 +661,18 @@ public:
void lock();
Error try_lock();
void unlock();
_Mutex();
~_Mutex();
};
class _Semaphore : public Reference {
GDCLASS(_Semaphore, Reference);
Semaphore *semaphore;
Semaphore semaphore;
static void _bind_methods();
public:
Error wait();
Error post();
_Semaphore();
~_Semaphore();
};
class _Thread : public Reference {
@@ -687,10 +682,10 @@ class _Thread : public Reference {
protected:
Variant ret;
Variant userdata;
volatile bool active;
SafeFlag active;
Object *target_instance;
StringName target_method;
Thread *thread;
Thread thread;
static void _bind_methods();
static void _start_func(void *ud);
+3 -10
View File
@@ -929,9 +929,9 @@ void ClassDB::add_property_group(StringName p_class, const String &p_name, const
void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {
lock->read_lock();
lock.read_lock();
ClassInfo *type = classes.getptr(p_class);
lock->read_unlock();
lock.read_unlock();
ERR_FAIL_COND(!type);
@@ -1447,12 +1447,7 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
return default_values[p_class][p_property];
}
RWLock *ClassDB::lock = NULL;
void ClassDB::init() {
lock = RWLock::create();
}
RWLock ClassDB::lock;
void ClassDB::cleanup_defaults() {
@@ -1479,8 +1474,6 @@ void ClassDB::cleanup() {
classes.clear();
resource_base_extensions.clear();
compat_classes.clear();
memdelete(lock);
}
//
+1 -2
View File
@@ -143,7 +143,7 @@ public:
return memnew(T);
}
static RWLock *lock;
static RWLock lock;
static HashMap<StringName, ClassInfo> classes;
static HashMap<StringName, StringName> resource_base_extensions;
static HashMap<StringName, StringName> compat_classes;
@@ -393,7 +393,6 @@ public:
static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions);
static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);
static void init();
static void set_current_api(APIType p_api);
static APIType get_current_api();
+3 -12
View File
@@ -35,14 +35,12 @@
void CommandQueueMT::lock() {
if (mutex)
mutex->lock();
mutex.lock();
}
void CommandQueueMT::unlock() {
if (mutex)
mutex->unlock();
mutex.unlock();
}
void CommandQueueMT::wait_for_flush() {
@@ -107,7 +105,6 @@ CommandQueueMT::CommandQueueMT(bool p_sync) {
read_ptr_and_epoch = 0;
write_ptr_and_epoch = 0;
dealloc_ptr = 0;
mutex = Mutex::create();
command_mem_size = GLOBAL_DEF_RST("memory/limits/command_queue/multithreading_queue_size_kb", DEFAULT_COMMAND_MEM_SIZE_KB);
ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/command_queue/multithreading_queue_size_kb", PropertyInfo(Variant::INT, "memory/limits/command_queue/multithreading_queue_size_kb", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"));
@@ -116,11 +113,10 @@ CommandQueueMT::CommandQueueMT(bool p_sync) {
for (int i = 0; i < SYNC_SEMAPHORES; i++) {
sync_sems[i].sem = Semaphore::create();
sync_sems[i].in_use = false;
}
if (p_sync) {
sync = Semaphore::create();
sync = memnew(Semaphore);
} else {
sync = NULL;
}
@@ -130,10 +126,5 @@ CommandQueueMT::~CommandQueueMT() {
if (sync)
memdelete(sync);
memdelete(mutex);
for (int i = 0; i < SYNC_SEMAPHORES; i++) {
memdelete(sync_sems[i].sem);
}
memfree(command_mem);
}
+5 -5
View File
@@ -250,7 +250,7 @@
cmd->sync_sem = ss; \
unlock(); \
if (sync) sync->post(); \
ss->sem->wait(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -267,7 +267,7 @@
cmd->sync_sem = ss; \
unlock(); \
if (sync) sync->post(); \
ss->sem->wait(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -277,7 +277,7 @@ class CommandQueueMT {
struct SyncSemaphore {
Semaphore *sem;
Semaphore sem;
bool in_use;
};
@@ -293,7 +293,7 @@ class CommandQueueMT {
SyncSemaphore *sync_sem;
virtual void post() {
sync_sem->sem->post();
sync_sem->sem.post();
}
};
@@ -321,7 +321,7 @@ class CommandQueueMT {
uint32_t dealloc_ptr;
uint32_t command_mem_size;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex *mutex;
Mutex mutex;
Semaphore *sync;
template <class T>
+12 -9
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;
}
}
+8 -6
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(); \
} \
}
+28 -45
View File
@@ -42,14 +42,14 @@
void FileAccessNetworkClient::lock_mutex() {
mutex->lock();
mutex.lock();
lockcount++;
}
void FileAccessNetworkClient::unlock_mutex() {
lockcount--;
mutex->unlock();
mutex.unlock();
}
void FileAccessNetworkClient::put_32(int p_32) {
@@ -88,16 +88,14 @@ void FileAccessNetworkClient::_thread_func() {
while (!quit) {
DEBUG_PRINT("SEM WAIT - " + itos(sem->get()));
Error err = sem->wait();
if (err != OK)
ERR_PRINT("sem->wait() failed");
sem.wait();
DEBUG_TIME("sem_unlock");
//DEBUG_PRINT("semwait returned "+itos(werr));
DEBUG_PRINT("MUTEX LOCK " + itos(lockcount));
lock_mutex();
DEBUG_PRINT("MUTEX PASS");
blockrequest_mutex->lock();
blockrequest_mutex.lock();
while (block_requests.size()) {
put_32(block_requests.front()->get().id);
put_32(FileAccessNetwork::COMMAND_READ_BLOCK);
@@ -105,7 +103,7 @@ void FileAccessNetworkClient::_thread_func() {
put_32(block_requests.front()->get().size);
block_requests.pop_front();
}
blockrequest_mutex->unlock();
blockrequest_mutex.unlock();
DEBUG_PRINT("THREAD ITER");
@@ -140,7 +138,7 @@ void FileAccessNetworkClient::_thread_func() {
fa->_respond(len, Error(status));
}
fa->sem->post();
fa->sem.post();
} break;
case FileAccessNetwork::RESPONSE_DATA: {
@@ -160,14 +158,14 @@ void FileAccessNetworkClient::_thread_func() {
int status = get_32();
fa->exists_modtime = status != 0;
fa->sem->post();
fa->sem.post();
} break;
case FileAccessNetwork::RESPONSE_GET_MODTIME: {
uint64_t status = get_64();
fa->exists_modtime = status;
fa->sem->post();
fa->sem.post();
} break;
}
@@ -215,7 +213,7 @@ Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const S
return ERR_INVALID_PARAMETER;
}
thread = Thread::create(_thread_func, this);
thread.start(_thread_func, this);
return OK;
}
@@ -224,29 +222,20 @@ FileAccessNetworkClient *FileAccessNetworkClient::singleton = NULL;
FileAccessNetworkClient::FileAccessNetworkClient() {
thread = NULL;
mutex = Mutex::create();
blockrequest_mutex = Mutex::create();
quit = false;
singleton = this;
last_id = 0;
client.instance();
sem = Semaphore::create();
lockcount = 0;
}
FileAccessNetworkClient::~FileAccessNetworkClient() {
if (thread) {
if (thread.is_started()) {
quit = true;
sem->post();
Thread::wait_to_finish(thread);
memdelete(thread);
sem.post();
thread.wait_to_finish();
}
memdelete(blockrequest_mutex);
memdelete(mutex);
memdelete(sem);
}
void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) {
@@ -259,14 +248,14 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block)
ERR_FAIL_COND((p_block.size() != (int)(total_size % page_size)));
}
buffer_mutex->lock();
buffer_mutex.lock();
pages.write[page].buffer = p_block;
pages.write[page].queued = false;
buffer_mutex->unlock();
buffer_mutex.unlock();
if (waiting_on_page == page) {
waiting_on_page = -1;
page_sem->post();
page_sem.post();
}
}
@@ -308,9 +297,9 @@ Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) {
nc->unlock_mutex();
DEBUG_PRINT("OPEN POST");
DEBUG_TIME("open_post");
nc->sem->post(); //awaiting answer
nc->sem.post(); //awaiting answer
DEBUG_PRINT("WAIT...");
sem->wait();
sem.wait();
DEBUG_TIME("open_end");
DEBUG_PRINT("WAIT ENDED...");
@@ -385,16 +374,16 @@ void FileAccessNetwork::_queue_page(int p_page) const {
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->blockrequest_mutex->lock();
nc->blockrequest_mutex.lock();
FileAccessNetworkClient::BlockRequest br;
br.id = id;
br.offset = size_t(p_page) * page_size;
br.size = page_size;
nc->block_requests.push_back(br);
pages.write[p_page].queued = true;
nc->blockrequest_mutex->unlock();
nc->blockrequest_mutex.unlock();
DEBUG_PRINT("QUEUE PAGE POST");
nc->sem->post();
nc->sem.post();
DEBUG_PRINT("queued " + itos(p_page));
}
}
@@ -418,16 +407,16 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const {
int page = pos / page_size;
if (page != last_page) {
buffer_mutex->lock();
buffer_mutex.lock();
if (pages[page].buffer.empty()) {
waiting_on_page = page;
for (int j = 0; j < read_ahead; j++) {
_queue_page(page + j);
}
buffer_mutex->unlock();
buffer_mutex.unlock();
DEBUG_PRINT("wait");
page_sem->wait();
page_sem.wait();
DEBUG_PRINT("done");
} else {
@@ -436,7 +425,7 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const {
_queue_page(page + j);
}
//queue pages
buffer_mutex->unlock();
buffer_mutex.unlock();
}
buff = pages.write[page].buffer.ptrw();
@@ -476,8 +465,8 @@ bool FileAccessNetwork::file_exists(const String &p_path) {
nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
nc->unlock_mutex();
DEBUG_PRINT("FILE EXISTS POST");
nc->sem->post();
sem->wait();
nc->sem.post();
sem.wait();
return exists_modtime != 0;
}
@@ -493,8 +482,8 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) {
nc->client->put_data((const uint8_t *)cs.ptr(), cs.length());
nc->unlock_mutex();
DEBUG_PRINT("MODTIME POST");
nc->sem->post();
sem->wait();
nc->sem.post();
sem.wait();
return exists_modtime;
}
@@ -522,9 +511,6 @@ FileAccessNetwork::FileAccessNetwork() {
eof_flag = false;
opened = false;
pos = 0;
sem = Semaphore::create();
page_sem = Semaphore::create();
buffer_mutex = Mutex::create();
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
id = nc->last_id++;
@@ -540,9 +526,6 @@ FileAccessNetwork::FileAccessNetwork() {
FileAccessNetwork::~FileAccessNetwork() {
close();
memdelete(sem);
memdelete(page_sem);
memdelete(buffer_mutex);
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
+7 -7
View File
@@ -49,11 +49,11 @@ class FileAccessNetworkClient {
List<BlockRequest> block_requests;
Semaphore *sem;
Thread *thread;
Semaphore sem;
Thread thread;
bool quit;
Mutex *mutex;
Mutex *blockrequest_mutex;
Mutex mutex;
Mutex blockrequest_mutex;
Map<int, FileAccessNetwork *> accesses;
Ref<StreamPeerTCP> client;
int last_id;
@@ -85,9 +85,9 @@ public:
class FileAccessNetwork : public FileAccess {
Semaphore *sem;
Semaphore *page_sem;
Mutex *buffer_mutex;
Semaphore sem;
Semaphore page_sem;
Mutex buffer_mutex;
bool opened;
size_t total_size;
mutable size_t pos;
+41 -66
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,16 +64,16 @@ 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;
}
Mutex *mutex;
Semaphore *sem;
Mutex mutex;
Semaphore sem;
Thread *thread;
Thread thread;
//Semaphore* semaphore;
bool thread_abort;
@@ -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);
}
}
@@ -98,11 +98,11 @@ struct _IP_ResolverPrivate {
while (!ipr->thread_abort) {
ipr->sem->wait();
ipr->sem.wait();
ipr->mutex->lock();
ipr->mutex.lock();
ipr->resolve_queues();
ipr->mutex->unlock();
ipr->mutex.unlock();
}
}
@@ -115,30 +115,30 @@ struct _IP_ResolverPrivate {
IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
resolver->mutex->lock();
resolver->mutex.lock();
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
if (resolver->cache.has(key) && resolver->cache[key].is_valid()) {
IP_Address res = resolver->cache[key];
resolver->mutex->unlock();
resolver->mutex.unlock();
return res;
}
IP_Address res = _resolve_hostname(p_hostname, p_type);
resolver->cache[key] = res;
resolver->mutex->unlock();
resolver->mutex.unlock();
return res;
}
IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Type p_type) {
resolver->mutex->lock();
resolver->mutex.lock();
ResolverID id = resolver->find_empty_id();
if (id == RESOLVER_INVALID_ID) {
WARN_PRINT("Out of resolver queries");
resolver->mutex->unlock();
resolver->mutex.unlock();
return id;
}
@@ -147,17 +147,17 @@ 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;
if (resolver->thread)
resolver->sem->post();
resolver->queue[id].status.set(IP::RESOLVER_STATUS_WAITING);
if (resolver->thread.is_started())
resolver->sem.post();
else
resolver->resolve_queues();
}
resolver->mutex->unlock();
resolver->mutex.unlock();
return id;
}
@@ -165,15 +165,15 @@ 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) {
resolver->mutex.lock();
if (resolver->queue[p_id].status.get() == IP::RESOLVER_STATUS_NONE) {
ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE");
resolver->mutex->unlock();
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();
resolver->mutex.unlock();
return res;
}
@@ -181,17 +181,17 @@ IP_Address IP::get_resolve_item_address(ResolverID p_id) const {
ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP_Address());
resolver->mutex->lock();
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();
resolver->mutex.unlock();
return IP_Address();
}
IP_Address res = resolver->queue[p_id].response;
resolver->mutex->unlock();
resolver->mutex.unlock();
return res;
}
@@ -199,16 +199,16 @@ void IP::erase_resolve_item(ResolverID p_id) {
ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES);
resolver->mutex->lock();
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();
resolver->mutex.unlock();
}
void IP::clear_cache(const String &p_hostname) {
resolver->mutex->lock();
resolver->mutex.lock();
if (p_hostname.empty()) {
resolver->cache.clear();
@@ -219,7 +219,7 @@ void IP::clear_cache(const String &p_hostname) {
resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_ANY));
}
resolver->mutex->unlock();
resolver->mutex.unlock();
}
Array IP::_get_local_addresses() const {
@@ -314,41 +314,16 @@ IP::IP() {
singleton = this;
resolver = memnew(_IP_ResolverPrivate);
resolver->sem = NULL;
resolver->mutex = Mutex::create();
#ifndef NO_THREADS
resolver->sem = Semaphore::create();
if (resolver->sem) {
resolver->thread_abort = false;
resolver->thread = Thread::create(_IP_ResolverPrivate::_thread_function, resolver);
if (!resolver->thread)
memdelete(resolver->sem); //wtf
} else {
resolver->thread = NULL;
}
#else
resolver->sem = NULL;
resolver->thread = NULL;
#endif
resolver->thread_abort = false;
resolver->thread.start(_IP_ResolverPrivate::_thread_function, resolver);
}
IP::~IP() {
#ifndef NO_THREADS
if (resolver->thread) {
resolver->thread_abort = true;
resolver->sem->post();
Thread::wait_to_finish(resolver->thread);
memdelete(resolver->thread);
memdelete(resolver->sem);
}
resolver->thread_abort = true;
resolver->sem.post();
resolver->thread.wait_to_finish();
#endif
memdelete(resolver->mutex);
memdelete(resolver);
}
+12 -42
View File
@@ -289,9 +289,7 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c
bool ResourceLoader::_add_to_loading_map(const String &p_path) {
bool success;
if (loading_map_mutex) {
loading_map_mutex->lock();
}
loading_map_mutex.lock();
LoadingMapKey key;
key.path = p_path;
@@ -304,17 +302,13 @@ bool ResourceLoader::_add_to_loading_map(const String &p_path) {
success = true;
}
if (loading_map_mutex) {
loading_map_mutex->unlock();
}
loading_map_mutex.unlock();
return success;
}
void ResourceLoader::_remove_from_loading_map(const String &p_path) {
if (loading_map_mutex) {
loading_map_mutex->lock();
}
loading_map_mutex.lock();
LoadingMapKey key;
key.path = p_path;
@@ -322,15 +316,11 @@ void ResourceLoader::_remove_from_loading_map(const String &p_path) {
loading_map.erase(key);
if (loading_map_mutex) {
loading_map_mutex->unlock();
}
loading_map_mutex.unlock();
}
void ResourceLoader::_remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread) {
if (loading_map_mutex) {
loading_map_mutex->lock();
}
loading_map_mutex.lock();
LoadingMapKey key;
key.path = p_path;
@@ -338,9 +328,7 @@ void ResourceLoader::_remove_from_loading_map_and_thread(const String &p_path, T
loading_map.erase(key);
if (loading_map_mutex) {
loading_map_mutex->unlock();
}
loading_map_mutex.unlock();
}
RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) {
@@ -362,9 +350,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
}
//lock first if possible
if (ResourceCache::lock) {
ResourceCache::lock->read_lock();
}
ResourceCache::lock.read_lock();
//get ptr
Resource **rptr = ResourceCache::resources.getptr(local_path);
@@ -376,16 +362,12 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
//referencing is fine
if (r_error)
*r_error = OK;
if (ResourceCache::lock) {
ResourceCache::lock->read_unlock();
}
ResourceCache::lock.read_unlock();
_remove_from_loading_map(local_path);
return res;
}
}
if (ResourceCache::lock) {
ResourceCache::lock->read_unlock();
}
ResourceCache::lock.read_unlock();
}
bool xl_remapped = false;
@@ -851,9 +833,7 @@ String ResourceLoader::path_remap(const String &p_path) {
void ResourceLoader::reload_translation_remaps() {
if (ResourceCache::lock) {
ResourceCache::lock->read_lock();
}
ResourceCache::lock.read_lock();
List<Resource *> to_reload;
SelfList<Resource> *E = remapped_list.first();
@@ -863,9 +843,7 @@ void ResourceLoader::reload_translation_remaps() {
E = E->next();
}
if (ResourceCache::lock) {
ResourceCache::lock->read_unlock();
}
ResourceCache::lock.read_unlock();
//now just make sure to not delete any of these resources while changing locale..
while (to_reload.front()) {
@@ -1004,15 +982,9 @@ void ResourceLoader::remove_custom_loaders() {
}
}
Mutex *ResourceLoader::loading_map_mutex = NULL;
Mutex ResourceLoader::loading_map_mutex;
HashMap<ResourceLoader::LoadingMapKey, int, ResourceLoader::LoadingMapKeyHasher> ResourceLoader::loading_map;
void ResourceLoader::initialize() {
#ifndef NO_THREADS
loading_map_mutex = Mutex::create();
#endif
}
void ResourceLoader::finalize() {
#ifndef NO_THREADS
const LoadingMapKey *K = NULL;
@@ -1020,8 +992,6 @@ void ResourceLoader::finalize() {
ERR_PRINTS("Exited while resource is being loaded: " + K->path);
}
loading_map.clear();
memdelete(loading_map_mutex);
loading_map_mutex = NULL;
#endif
}
+1 -2
View File
@@ -120,7 +120,7 @@ class ResourceLoader {
static ResourceLoadedCallback _loaded_callback;
static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path);
static Mutex *loading_map_mutex;
static Mutex loading_map_mutex;
//used to track paths being loaded in a thread, avoids cyclic recursion
struct LoadingMapKey {
@@ -197,7 +197,6 @@ public:
static void add_custom_loaders();
static void remove_custom_loaders();
static void initialize();
static void finalize();
};
+14 -21
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
@@ -2068,30 +2067,30 @@ ObjectID ObjectDB::add_instance(Object *p_object) {
ERR_FAIL_COND_V(p_object->get_instance_id() != 0, 0);
rw_lock->write_lock();
rw_lock.write_lock();
ObjectID instance_id = ++instance_counter;
instances[instance_id] = p_object;
instance_checks[p_object] = instance_id;
rw_lock->write_unlock();
rw_lock.write_unlock();
return instance_id;
}
void ObjectDB::remove_instance(Object *p_object) {
rw_lock->write_lock();
rw_lock.write_lock();
instances.erase(p_object->get_instance_id());
instance_checks.erase(p_object);
rw_lock->write_unlock();
rw_lock.write_unlock();
}
Object *ObjectDB::get_instance(ObjectID p_instance_id) {
rw_lock->read_lock();
rw_lock.read_lock();
Object **obj = instances.getptr(p_instance_id);
rw_lock->read_unlock();
rw_lock.read_unlock();
if (!obj)
return NULL;
@@ -2100,7 +2099,7 @@ Object *ObjectDB::get_instance(ObjectID p_instance_id) {
void ObjectDB::debug_objects(DebugFunc p_func) {
rw_lock->read_lock();
rw_lock.read_lock();
const ObjectID *K = NULL;
while ((K = instances.next(K))) {
@@ -2108,7 +2107,7 @@ void ObjectDB::debug_objects(DebugFunc p_func) {
p_func(instances[*K]);
}
rw_lock->read_unlock();
rw_lock.read_unlock();
}
void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
@@ -2116,23 +2115,18 @@ void Object::get_argument_options(const StringName &p_function, int p_idx, List<
int ObjectDB::get_object_count() {
rw_lock->read_lock();
rw_lock.read_lock();
int count = instances.size();
rw_lock->read_unlock();
rw_lock.read_unlock();
return count;
}
RWLock *ObjectDB::rw_lock = NULL;
void ObjectDB::setup() {
rw_lock = RWLock::create();
}
RWLock ObjectDB::rw_lock;
void ObjectDB::cleanup() {
rw_lock->write_lock();
rw_lock.write_lock();
if (instances.size()) {
WARN_PRINT("ObjectDB instances leaked at exit (run with --verbose for details).");
@@ -2159,6 +2153,5 @@ void ObjectDB::cleanup() {
}
instances.clear();
instance_checks.clear();
rw_lock->write_unlock();
memdelete(rw_lock);
rw_lock.write_unlock();
}
+5 -5
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:
@@ -791,12 +792,11 @@ class ObjectDB {
friend class Object;
friend void unregister_core_types();
static RWLock *rw_lock;
static RWLock rw_lock;
static void cleanup();
static ObjectID add_instance(Object *p_object);
static void remove_instance(Object *p_object);
friend void register_core_types();
static void setup();
public:
typedef void (*DebugFunc)(Object *p_obj);
@@ -806,11 +806,11 @@ public:
static int get_object_count();
_FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
rw_lock->read_lock();
rw_lock.read_lock();
bool exists = instance_checks.has(p_ptr);
rw_lock->read_unlock();
rw_lock.read_unlock();
return exists;
}
+13 -13
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
+3 -3
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);
+11 -23
View File
@@ -30,31 +30,19 @@
#include "mutex.h"
#include "core/error_macros.h"
#include <stddef.h>
Mutex *(*Mutex::create_func)(bool) = 0;
Mutex *Mutex::create(bool p_recursive) {
ERR_FAIL_COND_V(!create_func, 0);
return create_func(p_recursive);
}
Mutex::~Mutex() {
}
Mutex *_global_mutex = NULL;
static Mutex _global_mutex;
void _global_lock() {
if (_global_mutex)
_global_mutex->lock();
_global_mutex.lock();
}
void _global_unlock() {
if (_global_mutex)
_global_mutex->unlock();
_global_mutex.unlock();
}
#ifndef NO_THREADS
template class MutexImpl<std::recursive_mutex>;
template class MutexImpl<std::mutex>;
#endif
+74 -27
View File
@@ -32,42 +32,89 @@
#define MUTEX_H
#include "core/error_list.h"
#include "core/typedefs.h"
/**
* @class Mutex
* @author Juan Linietsky
* Portable Mutex (thread-safe locking) implementation.
* Mutexes are always recursive ( they don't self-lock in a single thread ).
* Mutexes can be used with a Lockp object like this, to avoid having to worry about unlocking:
* Lockp( mutex );
*/
#if !defined(NO_THREADS)
class Mutex {
protected:
static Mutex *(*create_func)(bool);
#include <mutex>
template <class StdMutexT>
class MutexImpl {
mutable StdMutexT mutex;
friend class MutexLock;
public:
virtual void lock() = 0; ///< Lock the mutex, block if locked by someone else
virtual void unlock() = 0; ///< Unlock the mutex, let other threads continue
virtual Error try_lock() = 0; ///< Attempt to lock the mutex, OK on success, ERROR means it can't lock.
_ALWAYS_INLINE_ void lock() const {
mutex.lock();
}
static Mutex *create(bool p_recursive = true); ///< Create a mutex
_ALWAYS_INLINE_ void unlock() const {
mutex.unlock();
}
virtual ~Mutex();
_ALWAYS_INLINE_ Error try_lock() const {
return mutex.try_lock() ? OK : ERR_BUSY;
}
};
// This is written this way instead of being a template to overcome a limitation of C++ pre-17
// that would require MutexLock to be used like this: MutexLock<Mutex> lock;
class MutexLock {
union {
std::recursive_mutex *recursive_mutex;
std::mutex *mutex;
};
bool recursive;
public:
_ALWAYS_INLINE_ explicit MutexLock(const MutexImpl<std::recursive_mutex> &p_mutex) :
recursive_mutex(&p_mutex.mutex),
recursive(true) {
recursive_mutex->lock();
}
_ALWAYS_INLINE_ explicit MutexLock(const MutexImpl<std::mutex> &p_mutex) :
mutex(&p_mutex.mutex),
recursive(false) {
mutex->lock();
}
_ALWAYS_INLINE_ ~MutexLock() {
if (recursive) {
recursive_mutex->unlock();
} else {
mutex->unlock();
}
}
};
using Mutex = MutexImpl<std::recursive_mutex>; // Recursive, for general use
using BinaryMutex = MutexImpl<std::mutex>; // Non-recursive, handle with care
extern template class MutexImpl<std::recursive_mutex>;
extern template class MutexImpl<std::mutex>;
#else
class FakeMutex {
FakeMutex() {}
};
template <class MutexT>
class MutexImpl {
public:
_ALWAYS_INLINE_ void lock() const {}
_ALWAYS_INLINE_ void unlock() const {}
_ALWAYS_INLINE_ Error try_lock() const { return OK; }
};
class MutexLock {
Mutex *mutex;
public:
MutexLock(Mutex *p_mutex) {
mutex = p_mutex;
if (mutex) mutex->lock();
}
~MutexLock() {
if (mutex) mutex->unlock();
}
explicit MutexLock(const MutexImpl<FakeMutex> &p_mutex) {}
};
#endif
using Mutex = MutexImpl<FakeMutex>;
using BinaryMutex = MutexImpl<FakeMutex>; // Non-recursive, handle with care
#endif // !NO_THREADS
#endif // MUTEX_H
-2
View File
@@ -41,8 +41,6 @@
#include <stdarg.h>
class Mutex;
class OS {
static OS *singleton;
-47
View File
@@ -1,47 +0,0 @@
/*************************************************************************/
/* rw_lock.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 "rw_lock.h"
#include "core/error_macros.h"
#include <stddef.h>
RWLock *(*RWLock::create_func)() = 0;
RWLock *RWLock::create() {
ERR_FAIL_COND_V(!create_func, 0);
return create_func();
}
RWLock::~RWLock() {
}
+56 -20
View File
@@ -33,49 +33,85 @@
#include "core/error_list.h"
#if !defined(NO_THREADS)
#include <shared_mutex>
class RWLock {
protected:
static RWLock *(*create_func)();
mutable std::shared_timed_mutex mutex;
public:
virtual void read_lock() = 0; ///< Lock the rwlock, block if locked by someone else
virtual void read_unlock() = 0; ///< Unlock the rwlock, let other threads continue
virtual Error read_try_lock() = 0; ///< Attempt to lock the rwlock, OK on success, ERROR means it can't lock.
// Lock the rwlock, block if locked by someone else
void read_lock() const {
mutex.lock_shared();
}
virtual void write_lock() = 0; ///< Lock the rwlock, block if locked by someone else
virtual void write_unlock() = 0; ///< Unlock the rwlock, let other thwrites continue
virtual Error write_try_lock() = 0; ///< Attempt to lock the rwlock, OK on success, ERROR means it can't lock.
// Unlock the rwlock, let other threads continue
void read_unlock() const {
mutex.unlock_shared();
}
static RWLock *create(); ///< Create a rwlock
// Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock.
Error read_try_lock() const {
return mutex.try_lock_shared() ? OK : ERR_BUSY;
}
virtual ~RWLock();
// Lock the rwlock, block if locked by someone else
void write_lock() {
mutex.lock();
}
// Unlock the rwlock, let other thwrites continue
void write_unlock() {
mutex.unlock();
}
// Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock.
Error write_try_lock() {
return mutex.try_lock() ? OK : ERR_BUSY;
}
};
#else
class RWLock {
public:
void read_lock() const {}
void read_unlock() const {}
Error read_try_lock() const { return OK; }
void write_lock() {}
void write_unlock() {}
Error write_try_lock() { return OK; }
};
#endif
class RWLockRead {
RWLock *lock;
const RWLock &lock;
public:
RWLockRead(const RWLock *p_lock) {
lock = const_cast<RWLock *>(p_lock);
if (lock) lock->read_lock();
RWLockRead(const RWLock &p_lock) :
lock(p_lock) {
lock.read_lock();
}
~RWLockRead() {
if (lock) lock->read_unlock();
lock.read_unlock();
}
};
class RWLockWrite {
RWLock *lock;
RWLock &lock;
public:
RWLockWrite(RWLock *p_lock) {
lock = p_lock;
if (lock) lock->write_lock();
RWLockWrite(RWLock &p_lock) :
lock(p_lock) {
lock.write_lock();
}
~RWLockWrite() {
if (lock) lock->write_unlock();
lock.write_unlock();
}
};
-45
View File
@@ -1,45 +0,0 @@
/*************************************************************************/
/* semaphore.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 "semaphore.h"
#include "core/error_macros.h"
Semaphore *(*Semaphore::create_func)() = 0;
Semaphore *Semaphore::create() {
ERR_FAIL_COND_V(!create_func, 0);
return create_func();
}
Semaphore::~Semaphore() {
}
+47 -7
View File
@@ -32,19 +32,59 @@
#define SEMAPHORE_H
#include "core/error_list.h"
#include "core/typedefs.h"
#if !defined(NO_THREADS)
#include <condition_variable>
#include <mutex>
class Semaphore {
protected:
static Semaphore *(*create_func)();
private:
mutable std::mutex mutex_;
mutable std::condition_variable condition_;
mutable unsigned long count_ = 0; // Initialized as locked.
public:
virtual Error wait() = 0; ///< wait until semaphore has positive value, then decrement and pass
virtual Error post() = 0; ///< unlock the semaphore, incrementing the value
virtual int get() const = 0; ///< get semaphore value
_ALWAYS_INLINE_ void post() const {
std::lock_guard<decltype(mutex_)> lock(mutex_);
++count_;
condition_.notify_one();
}
static Semaphore *create(); ///< Create a mutex
_ALWAYS_INLINE_ void wait() const {
std::unique_lock<decltype(mutex_)> lock(mutex_);
while (!count_) { // Handle spurious wake-ups.
condition_.wait(lock);
}
--count_;
}
virtual ~Semaphore();
_ALWAYS_INLINE_ bool try_wait() const {
std::lock_guard<decltype(mutex_)> lock(mutex_);
if (count_) {
--count_;
return true;
}
return false;
}
_ALWAYS_INLINE_ int get() const {
std::lock_guard<decltype(mutex_)> lock(mutex_);
return count_;
}
};
#else
class Semaphore {
public:
_ALWAYS_INLINE_ void post() const {}
_ALWAYS_INLINE_ void wait() const {}
_ALWAYS_INLINE_ bool try_wait() const { return true; }
_ALWAYS_INLINE_ int get() const { return 1; }
};
#endif
#endif // SEMAPHORE_H
+71 -23
View File
@@ -30,45 +30,93 @@
#include "thread.h"
Thread *(*Thread::create_func)(ThreadCreateCallback, void *, const Settings &) = NULL;
Thread::ID (*Thread::get_thread_id_func)() = NULL;
void (*Thread::wait_to_finish_func)(Thread *) = NULL;
Error (*Thread::set_name_func)(const String &) = NULL;
#include "core/script_language.h"
Thread::ID Thread::_main_thread_id = 0;
#if !defined(NO_THREADS)
Thread::ID Thread::get_caller_id() {
#include "core/safe_refcount.h"
if (get_thread_id_func)
return get_thread_id_func();
return 0;
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;
SafeNumeric<Thread::ID> Thread::last_thread_id{ 1 };
thread_local Thread::ID Thread::caller_id = 1;
void Thread::_set_platform_funcs(
Error (*p_set_name_func)(const String &),
void (*p_set_priority_func)(Thread::Priority),
void (*p_init_func)(),
void (*p_term_func)()) {
Thread::set_name_func = p_set_name_func;
Thread::set_priority_func = p_set_priority_func;
Thread::init_func = p_init_func;
Thread::term_func = p_term_func;
}
Thread *Thread::create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings) {
if (create_func) {
return create_func(p_callback, p_user, p_settings);
void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
Thread::caller_id = p_self->id;
if (set_priority_func) {
set_priority_func(p_settings.priority);
}
if (init_func) {
init_func();
}
ScriptServer::thread_enter(); //scripts may need to attach a stack
p_callback(p_userdata);
ScriptServer::thread_exit();
if (term_func) {
term_func();
}
return NULL;
}
void Thread::wait_to_finish(Thread *p_thread) {
void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
if (id != 0) {
#ifdef DEBUG_ENABLED
WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
#endif
thread.detach();
std::thread empty_thread;
thread.swap(empty_thread);
}
id = last_thread_id.increment();
std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
thread.swap(new_thread);
}
if (wait_to_finish_func)
wait_to_finish_func(p_thread);
bool Thread::is_started() const {
return id != 0;
}
void Thread::wait_to_finish() {
if (id != 0) {
thread.join();
std::thread empty_thread;
thread.swap(empty_thread);
id = 0;
}
}
Error Thread::set_name(const String &p_name) {
if (set_name_func)
if (set_name_func) {
return set_name_func(p_name);
}
return ERR_UNAVAILABLE;
};
Thread::Thread() {
}
Thread::Thread() :
id(0) {}
Thread::~Thread() {
if (id != 0) {
#ifdef DEBUG_ENABLED
WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
#endif
thread.detach();
}
}
#endif
+58 -21
View File
@@ -32,49 +32,86 @@
#define THREAD_H
#include "core/typedefs.h"
#include "core/ustring.h"
typedef void (*ThreadCreateCallback)(void *p_userdata);
#if !defined(NO_THREADS)
#include "core/safe_refcount.h"
#include <thread>
#endif
class String;
class Thread {
public:
enum Priority {
typedef void (*Callback)(void *p_userdata);
typedef uint64_t ID;
enum Priority {
PRIORITY_LOW,
PRIORITY_NORMAL,
PRIORITY_HIGH
};
struct Settings {
Priority priority;
Settings() { priority = PRIORITY_NORMAL; }
};
typedef uint64_t ID;
protected:
static Thread *(*create_func)(ThreadCreateCallback p_callback, void *, const Settings &);
static ID (*get_thread_id_func)();
static void (*wait_to_finish_func)(Thread *);
static Error (*set_name_func)(const String &);
private:
#if !defined(NO_THREADS)
friend class Main;
static ID _main_thread_id;
static ID main_thread_id;
static SafeNumeric<ID> last_thread_id;
Thread();
ID id;
static thread_local ID caller_id;
std::thread thread;
static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
static Error (*set_name_func)(const String &);
static void (*set_priority_func)(Thread::Priority);
static void (*init_func)();
static void (*term_func)();
#endif
public:
virtual ID get_id() const = 0;
static void _set_platform_funcs(
Error (*p_set_name_func)(const String &),
void (*p_set_priority_func)(Thread::Priority),
void (*p_init_func)() = nullptr,
void (*p_term_func)() = nullptr);
#if !defined(NO_THREADS)
_FORCE_INLINE_ ID get_id() const { return id; }
// get the ID of the caller thread
_FORCE_INLINE_ static ID get_caller_id() { return caller_id; }
// get the ID of the main thread
_FORCE_INLINE_ static ID get_main_id() { return main_thread_id; }
static Error set_name(const String &p_name);
_FORCE_INLINE_ static ID get_main_id() { return _main_thread_id; } ///< get the ID of the main thread
static ID get_caller_id(); ///< get the ID of the caller function ID
static void wait_to_finish(Thread *p_thread); ///< waits until thread is finished
static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings()); ///< Static function to create a thread, will call p_callback
virtual ~Thread();
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
bool is_started() const;
///< waits until thread is finished, and deallocates it.
void wait_to_finish();
Thread();
~Thread();
#else
_FORCE_INLINE_ ID get_id() const { return 0; }
// get the ID of the caller thread
_FORCE_INLINE_ static ID get_caller_id() { return 0; }
// get the ID of the main thread
_FORCE_INLINE_ static ID get_main_id() { return 0; }
static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; }
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {}
bool is_started() const { return false; }
void wait_to_finish() {}
#endif
};
#endif
#endif // THREAD_H
-65
View File
@@ -1,65 +0,0 @@
/*************************************************************************/
/* thread_dummy.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 "thread_dummy.h"
#include "core/os/memory.h"
Thread *ThreadDummy::create(ThreadCreateCallback p_callback, void *p_user, const Thread::Settings &p_settings) {
return memnew(ThreadDummy);
};
void ThreadDummy::make_default() {
Thread::create_func = &ThreadDummy::create;
};
Mutex *MutexDummy::create(bool p_recursive) {
return memnew(MutexDummy);
};
void MutexDummy::make_default() {
Mutex::create_func = &MutexDummy::create;
};
Semaphore *SemaphoreDummy::create() {
return memnew(SemaphoreDummy);
};
void SemaphoreDummy::make_default() {
Semaphore::create_func = &SemaphoreDummy::create;
};
RWLock *RWLockDummy::create() {
return memnew(RWLockDummy);
};
void RWLockDummy::make_default() {
RWLock::create_func = &RWLockDummy::create;
};
-89
View File
@@ -1,89 +0,0 @@
/*************************************************************************/
/* thread_dummy.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef THREAD_DUMMY_H
#define THREAD_DUMMY_H
#include "core/os/mutex.h"
#include "core/os/rw_lock.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
class ThreadDummy : public Thread {
static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings());
public:
virtual ID get_id() const { return 0; };
static void make_default();
};
class MutexDummy : public Mutex {
static Mutex *create(bool p_recursive);
public:
virtual void lock(){};
virtual void unlock(){};
virtual Error try_lock() { return OK; };
static void make_default();
};
class SemaphoreDummy : public Semaphore {
static Semaphore *create();
public:
virtual Error wait() { return OK; };
virtual Error post() { return OK; };
virtual int get() const { return 0; }; ///< get semaphore value
static void make_default();
};
class RWLockDummy : public RWLock {
static RWLock *create();
public:
virtual void read_lock() {}
virtual void read_unlock() {}
virtual Error read_try_lock() { return OK; }
virtual void write_lock() {}
virtual void write_unlock() {}
virtual Error write_try_lock() { return OK; }
static void make_default();
};
#endif
-49
View File
@@ -1,49 +0,0 @@
/*************************************************************************/
/* thread_safe.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 "thread_safe.h"
#include "core/error_macros.h"
#include "core/os/memory.h"
ThreadSafe::ThreadSafe() {
mutex = Mutex::create();
if (!mutex) {
WARN_PRINT("THREAD_SAFE defined, but no default mutex type");
}
}
ThreadSafe::~ThreadSafe() {
if (mutex)
memdelete(mutex);
}
+4 -45
View File
@@ -33,50 +33,9 @@
#include "core/os/mutex.h"
class ThreadSafe {
Mutex *mutex;
public:
inline void lock() const {
if (mutex) mutex->lock();
}
inline void unlock() const {
if (mutex) mutex->unlock();
}
ThreadSafe();
~ThreadSafe();
};
class ThreadSafeMethod {
const ThreadSafe *_ts;
public:
ThreadSafeMethod(const ThreadSafe *p_ts) {
_ts = p_ts;
_ts->lock();
}
~ThreadSafeMethod() { _ts->unlock(); }
};
#ifndef NO_THREADS
#define _THREAD_SAFE_CLASS_ ThreadSafe __thread__safe__;
#define _THREAD_SAFE_METHOD_ ThreadSafeMethod __thread_safe_method__(&__thread__safe__);
#define _THREAD_SAFE_LOCK_ __thread__safe__.lock();
#define _THREAD_SAFE_UNLOCK_ __thread__safe__.unlock();
#else
#define _THREAD_SAFE_CLASS_
#define _THREAD_SAFE_METHOD_
#define _THREAD_SAFE_LOCK_
#define _THREAD_SAFE_UNLOCK_
#endif
#define _THREAD_SAFE_CLASS_ mutable Mutex _thread_safe_;
#define _THREAD_SAFE_METHOD_ MutexLock _thread_safe_method_(_thread_safe_);
#define _THREAD_SAFE_LOCK_ _thread_safe_.lock();
#define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock();
#endif
+11 -12
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,22 +71,21 @@ 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
Vector<Thread *> threads;
int thread_count = OS::get_singleton()->get_processor_count();
Thread *threads = memnew_arr(Thread, thread_count);
threads.resize(OS::get_singleton()->get_processor_count());
for (int i = 0; i < threads.size(); i++) {
threads.write[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data);
for (int i = 0; i < thread_count; i++) {
threads[i].start(process_array_thread<ThreadArrayProcessData<C, U> >, &data);
}
for (int i = 0; i < threads.size(); i++) {
Thread::wait_to_finish(threads[i]);
memdelete(threads[i]);
for (int i = 0; i < thread_count; i++) {
threads[i].wait_to_finish();
}
memdelete_arr(threads);
}
#else
+2 -5
View File
@@ -30,7 +30,7 @@
#include "pool_vector.h"
Mutex *pool_vector_lock = NULL;
Mutex pool_vector_lock;
PoolAllocator *MemoryPool::memory_pool = NULL;
uint8_t *MemoryPool::pool_memory = NULL;
@@ -40,7 +40,7 @@ MemoryPool::Alloc *MemoryPool::allocs = NULL;
MemoryPool::Alloc *MemoryPool::free_list = NULL;
uint32_t MemoryPool::alloc_count = 0;
uint32_t MemoryPool::allocs_used = 0;
Mutex *MemoryPool::alloc_mutex = NULL;
Mutex MemoryPool::alloc_mutex;
size_t MemoryPool::total_memory = 0;
size_t MemoryPool::max_memory = 0;
@@ -57,14 +57,11 @@ void MemoryPool::setup(uint32_t p_max_allocs) {
}
free_list = &allocs[0];
alloc_mutex = Mutex::create();
}
void MemoryPool::cleanup() {
memdelete_arr(allocs);
memdelete(alloc_mutex);
ERR_FAIL_COND_MSG(allocs_used > 0, "There are still MemoryPool allocs in use at exit!");
}
+26 -25
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;
@@ -69,7 +70,7 @@ struct MemoryPool {
static Alloc *free_list;
static uint32_t alloc_count;
static uint32_t allocs_used;
static Mutex *alloc_mutex;
static Mutex alloc_mutex;
static size_t total_memory;
static size_t max_memory;
@@ -95,9 +96,9 @@ class PoolVector {
//must allocate something
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
if (MemoryPool::allocs_used == MemoryPool::alloc_count) {
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
ERR_FAIL_MSG("All memory pool allocations are in use, can't COW.");
}
@@ -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;
@@ -122,7 +123,7 @@ class PoolVector {
}
#endif
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
if (MemoryPool::memory_pool) {
@@ -148,9 +149,9 @@ class PoolVector {
//this should never happen but..
#ifdef DEBUG_ENABLED
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
MemoryPool::total_memory -= old_alloc->size;
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
#endif
{
@@ -174,11 +175,11 @@ class PoolVector {
old_alloc->mem = NULL;
old_alloc->size = 0;
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
old_alloc->free_list = MemoryPool::free_list;
MemoryPool::free_list = old_alloc;
MemoryPool::allocs_used--;
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
}
}
}
@@ -227,9 +228,9 @@ class PoolVector {
}
#ifdef DEBUG_ENABLED
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
MemoryPool::total_memory -= alloc->size;
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
#endif
if (MemoryPool::memory_pool) {
@@ -242,11 +243,11 @@ class PoolVector {
alloc->mem = NULL;
alloc->size = 0;
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
alloc->free_list = MemoryPool::free_list;
MemoryPool::free_list = alloc;
MemoryPool::allocs_used--;
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
}
alloc = NULL;
@@ -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;
@@ -523,9 +524,9 @@ Error PoolVector<T>::resize(int p_size) {
return OK; //nothing to do here
//must allocate something
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
if (MemoryPool::allocs_used == MemoryPool::alloc_count) {
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "All memory pool allocations are in use.");
}
@@ -539,11 +540,11 @@ Error PoolVector<T>::resize(int p_size) {
alloc->size = 0;
alloc->refcount.init();
alloc->pool_id = POOL_ALLOCATOR_INVALID_ID;
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
} 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;
@@ -559,13 +560,13 @@ Error PoolVector<T>::resize(int p_size) {
_copy_on_write(); // make it unique
#ifdef DEBUG_ENABLED
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
MemoryPool::total_memory -= alloc->size;
MemoryPool::total_memory += new_size;
if (MemoryPool::total_memory > MemoryPool::max_memory) {
MemoryPool::max_memory = MemoryPool::total_memory;
}
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
#endif
int cur_elements = alloc->size / sizeof(T);
@@ -615,11 +616,11 @@ Error PoolVector<T>::resize(int p_size) {
alloc->mem = NULL;
alloc->size = 0;
MemoryPool::alloc_mutex->lock();
MemoryPool::alloc_mutex.lock();
alloc->free_list = MemoryPool::free_list;
MemoryPool::free_list = alloc;
MemoryPool::allocs_used--;
MemoryPool::alloc_mutex->unlock();
MemoryPool::alloc_mutex.unlock();
} else {
alloc->mem = memrealloc(alloc->mem, new_size);
+2 -2
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);
+1 -11
View File
@@ -90,7 +90,7 @@ static IP *ip = NULL;
static _Geometry *_geometry = NULL;
extern Mutex *_global_mutex;
extern Mutex _global_mutex;
extern void register_global_constants();
extern void unregister_global_constants();
@@ -99,14 +99,9 @@ extern void unregister_variant_methods();
void register_core_types() {
ObjectDB::setup();
ResourceCache::setup();
MemoryPool::setup();
_global_mutex = Mutex::create();
StringName::setup();
ResourceLoader::initialize();
register_global_constants();
register_variant_methods();
@@ -318,10 +313,5 @@ void unregister_core_types() {
CoreStringNames::free();
StringName::cleanup();
if (_global_mutex) {
memdelete(_global_mutex);
_global_mutex = NULL; //still needed at a few places
};
MemoryPool::cleanup();
}
+33 -60
View File
@@ -54,30 +54,30 @@ void Resource::set_path(const String &p_path, bool p_take_over) {
if (path_cache != "") {
ResourceCache::lock->write_lock();
ResourceCache::lock.write_lock();
ResourceCache::resources.erase(path_cache);
ResourceCache::lock->write_unlock();
ResourceCache::lock.write_unlock();
}
path_cache = "";
ResourceCache::lock->read_lock();
ResourceCache::lock.read_lock();
bool has_path = ResourceCache::resources.has(p_path);
ResourceCache::lock->read_unlock();
ResourceCache::lock.read_unlock();
if (has_path) {
if (p_take_over) {
ResourceCache::lock->write_lock();
ResourceCache::lock.write_lock();
Resource **res = ResourceCache::resources.getptr(p_path);
if (res) {
(*res)->set_name("");
}
ResourceCache::lock->write_unlock();
ResourceCache::lock.write_unlock();
} else {
ResourceCache::lock->read_lock();
ResourceCache::lock.read_lock();
bool exists = ResourceCache::resources.has(p_path);
ResourceCache::lock->read_unlock();
ResourceCache::lock.read_unlock();
ERR_FAIL_COND_MSG(exists, "Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion).");
}
@@ -86,9 +86,9 @@ void Resource::set_path(const String &p_path, bool p_take_over) {
if (path_cache != "") {
ResourceCache::lock->write_lock();
ResourceCache::lock.write_lock();
ResourceCache::resources[path_cache] = this;
ResourceCache::lock->write_unlock();
ResourceCache::lock.write_unlock();
}
_change_notify("resource_path");
@@ -343,9 +343,7 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
if (remapped_list.in_list() == p_remapped)
return;
if (ResourceCache::lock) {
ResourceCache::lock->write_lock();
}
ResourceCache::lock.write_lock();
if (p_remapped) {
ResourceLoader::remapped_list.add(&remapped_list);
@@ -353,9 +351,7 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
ResourceLoader::remapped_list.remove(&remapped_list);
}
if (ResourceCache::lock) {
ResourceCache::lock->write_unlock();
}
ResourceCache::lock.write_unlock();
}
bool Resource::is_translation_remapped() const {
@@ -367,38 +363,24 @@ bool Resource::is_translation_remapped() const {
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
void Resource::set_id_for_path(const String &p_path, int p_id) {
if (p_id == -1) {
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->write_lock();
}
ResourceCache::path_cache_lock.write_lock();
ResourceCache::resource_path_cache[p_path].erase(get_path());
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->write_unlock();
}
ResourceCache::path_cache_lock.write_unlock();
} else {
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->write_lock();
}
ResourceCache::path_cache_lock.write_lock();
ResourceCache::resource_path_cache[p_path][get_path()] = p_id;
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->write_unlock();
}
ResourceCache::path_cache_lock.write_unlock();
}
}
int Resource::get_id_for_path(const String &p_path) const {
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->read_lock();
}
ResourceCache::path_cache_lock.read_lock();
if (ResourceCache::resource_path_cache[p_path].has(get_path())) {
int result = ResourceCache::resource_path_cache[p_path][get_path()];
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->read_unlock();
}
ResourceCache::path_cache_lock.read_unlock();
return result;
} else {
if (ResourceCache::path_cache_lock) {
ResourceCache::path_cache_lock->read_unlock();
}
ResourceCache::path_cache_lock.read_unlock();
return -1;
}
}
@@ -444,9 +426,9 @@ Resource::Resource() :
Resource::~Resource() {
if (path_cache != "") {
ResourceCache::lock->write_lock();
ResourceCache::lock.write_lock();
ResourceCache::resources.erase(path_cache);
ResourceCache::lock->write_unlock();
ResourceCache::lock.write_unlock();
}
if (owners.size()) {
WARN_PRINT("Resource is still owned.");
@@ -458,19 +440,11 @@ HashMap<String, Resource *> ResourceCache::resources;
HashMap<String, HashMap<String, int> > ResourceCache::resource_path_cache;
#endif
RWLock *ResourceCache::lock = NULL;
RWLock ResourceCache::lock;
#ifdef TOOLS_ENABLED
RWLock *ResourceCache::path_cache_lock = NULL;
RWLock ResourceCache::path_cache_lock;
#endif
void ResourceCache::setup() {
lock = RWLock::create();
#ifdef TOOLS_ENABLED
path_cache_lock = RWLock::create();
#endif
}
void ResourceCache::clear() {
if (resources.size()) {
ERR_PRINT("Resources still in use at exit (run with --verbose for details).");
@@ -484,7 +458,6 @@ void ResourceCache::clear() {
}
resources.clear();
memdelete(lock);
}
void ResourceCache::reload_externals() {
@@ -492,19 +465,19 @@ void ResourceCache::reload_externals() {
bool ResourceCache::has(const String &p_path) {
lock->read_lock();
lock.read_lock();
bool b = resources.has(p_path);
lock->read_unlock();
lock.read_unlock();
return b;
}
Resource *ResourceCache::get(const String &p_path) {
lock->read_lock();
lock.read_lock();
Resource **res = resources.getptr(p_path);
lock->read_unlock();
lock.read_unlock();
if (!res) {
return NULL;
@@ -515,28 +488,28 @@ Resource *ResourceCache::get(const String &p_path) {
void ResourceCache::get_cached_resources(List<Ref<Resource> > *p_resources) {
lock->read_lock();
lock.read_lock();
const String *K = NULL;
while ((K = resources.next(K))) {
Resource *r = resources[*K];
p_resources->push_back(Ref<Resource>(r));
}
lock->read_unlock();
lock.read_unlock();
}
int ResourceCache::get_cached_resource_count() {
lock->read_lock();
lock.read_lock();
int rc = resources.size();
lock->read_unlock();
lock.read_unlock();
return rc;
}
void ResourceCache::dump(const char *p_file, bool p_short) {
#ifdef DEBUG_ENABLED
lock->read_lock();
lock.read_lock();
Map<String, int> type_count;
@@ -573,6 +546,6 @@ void ResourceCache::dump(const char *p_file, bool p_short) {
memdelete(f);
}
lock->read_unlock();
lock.read_unlock();
#endif
}
+2 -3
View File
@@ -148,16 +148,15 @@ typedef Ref<Resource> RES;
class ResourceCache {
friend class Resource;
friend class ResourceLoader; //need the lock
static RWLock *lock;
static RWLock lock;
static HashMap<String, Resource *> resources;
#ifdef TOOLS_ENABLED
static HashMap<String, HashMap<String, int> > resource_path_cache; // each tscn has a set of resource paths and IDs
static RWLock *path_cache_lock;
static RWLock path_cache_lock;
#endif // TOOLS_ENABLED
friend void unregister_core_types();
static void clear();
friend void register_core_types();
static void setup();
public:
static void reload_externals();
-169
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
+258 -147
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
+23 -27
View File
@@ -47,12 +47,10 @@ StringName _scs_create(const char *p_chr) {
}
bool StringName::configured = false;
Mutex *StringName::lock = NULL;
Mutex StringName::lock;
void StringName::setup() {
lock = Mutex::create();
ERR_FAIL_COND(configured);
for (int i = 0; i < STRING_TABLE_LEN; i++) {
@@ -63,7 +61,7 @@ void StringName::setup() {
void StringName::cleanup() {
lock->lock();
lock.lock();
int lost_strings = 0;
for (int i = 0; i < STRING_TABLE_LEN; i++) {
@@ -87,9 +85,7 @@ void StringName::cleanup() {
if (lost_strings) {
print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit.");
}
lock->unlock();
memdelete(lock);
lock.unlock();
}
void StringName::unref() {
@@ -98,7 +94,7 @@ void StringName::unref() {
if (_data && _data->refcount.unref()) {
lock->lock();
lock.lock();
if (_data->prev) {
_data->prev->next = _data->next;
@@ -113,7 +109,7 @@ void StringName::unref() {
_data->next->prev = _data->prev;
}
memdelete(_data);
lock->unlock();
lock.unlock();
}
_data = NULL;
@@ -184,7 +180,7 @@ StringName::StringName(const char *p_name) {
if (!p_name || p_name[0] == 0)
return; //empty, ignore
lock->lock();
lock.lock();
uint32_t hash = String::hash(p_name);
@@ -203,7 +199,7 @@ StringName::StringName(const char *p_name) {
if (_data) {
if (_data->refcount.ref()) {
// exists
lock->unlock();
lock.unlock();
return;
}
}
@@ -220,7 +216,7 @@ StringName::StringName(const char *p_name) {
_table[idx]->prev = _data;
_table[idx] = _data;
lock->unlock();
lock.unlock();
}
StringName::StringName(const StaticCString &p_static_string) {
@@ -231,7 +227,7 @@ StringName::StringName(const StaticCString &p_static_string) {
ERR_FAIL_COND(!p_static_string.ptr || !p_static_string.ptr[0]);
lock->lock();
lock.lock();
uint32_t hash = String::hash(p_static_string.ptr);
@@ -250,7 +246,7 @@ StringName::StringName(const StaticCString &p_static_string) {
if (_data) {
if (_data->refcount.ref()) {
// exists
lock->unlock();
lock.unlock();
return;
}
}
@@ -267,7 +263,7 @@ StringName::StringName(const StaticCString &p_static_string) {
_table[idx]->prev = _data;
_table[idx] = _data;
lock->unlock();
lock.unlock();
}
StringName::StringName(const String &p_name) {
@@ -279,7 +275,7 @@ StringName::StringName(const String &p_name) {
if (p_name == String())
return;
lock->lock();
lock.lock();
uint32_t hash = p_name.hash();
@@ -297,7 +293,7 @@ StringName::StringName(const String &p_name) {
if (_data) {
if (_data->refcount.ref()) {
// exists
lock->unlock();
lock.unlock();
return;
}
}
@@ -314,7 +310,7 @@ StringName::StringName(const String &p_name) {
_table[idx]->prev = _data;
_table[idx] = _data;
lock->unlock();
lock.unlock();
}
StringName StringName::search(const char *p_name) {
@@ -325,7 +321,7 @@ StringName StringName::search(const char *p_name) {
if (!p_name[0])
return StringName();
lock->lock();
lock.lock();
uint32_t hash = String::hash(p_name);
@@ -342,12 +338,12 @@ StringName StringName::search(const char *p_name) {
}
if (_data && _data->refcount.ref()) {
lock->unlock();
lock.unlock();
return StringName(_data);
}
lock->unlock();
lock.unlock();
return StringName(); //does not exist
}
@@ -359,7 +355,7 @@ StringName StringName::search(const CharType *p_name) {
if (!p_name[0])
return StringName();
lock->lock();
lock.lock();
uint32_t hash = String::hash(p_name);
@@ -376,18 +372,18 @@ StringName StringName::search(const CharType *p_name) {
}
if (_data && _data->refcount.ref()) {
lock->unlock();
lock.unlock();
return StringName(_data);
}
lock->unlock();
lock.unlock();
return StringName(); //does not exist
}
StringName StringName::search(const String &p_name) {
ERR_FAIL_COND_V(p_name == "", StringName());
lock->lock();
lock.lock();
uint32_t hash = p_name.hash();
@@ -404,11 +400,11 @@ StringName StringName::search(const String &p_name) {
}
if (_data && _data->refcount.ref()) {
lock->unlock();
lock.unlock();
return StringName(_data);
}
lock->unlock();
lock.unlock();
return StringName(); //does not exist
}
+1 -1
View File
@@ -82,7 +82,7 @@ class StringName {
friend void register_core_types();
friend void unregister_core_types();
static Mutex *lock;
static Mutex lock;
static void setup();
static void cleanup();
static bool configured;