Skip to content

Commit 1f546a5

Browse files
Ivorforcedsnopek
andcommitted
Add Mutex from upstream Godot, and put the upstream Mutex into CoreBind.
Co-authored-by: David Snopek <dsnopek@gmail.com>
1 parent 357ad86 commit 1f546a5

6 files changed

Lines changed: 142 additions & 11 deletions

File tree

binding_generator.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,7 +1792,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
17921792
result.append("#include <godot_cpp/core/binder_common.hpp>")
17931793
result.append("")
17941794

1795-
result.append("namespace godot {")
1795+
if class_name == "Mutex":
1796+
result.append("namespace godot::CoreBind {")
1797+
else:
1798+
result.append("namespace godot {")
17961799
result.append("")
17971800

17981801
for type_name in used_classes:
@@ -2099,7 +2102,10 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
20992102

21002103
result.append("")
21012104

2102-
result.append("namespace godot {")
2105+
if class_name == "Mutex":
2106+
result.append("namespace godot::CoreBind {")
2107+
else:
2108+
result.append("namespace godot {")
21032109
result.append("")
21042110

21052111
if is_singleton:
Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**************************************************************************/
2-
/* mutex_lock.hpp */
2+
/* mutex.hpp */
33
/**************************************************************************/
44
/* This file is part of: */
55
/* GODOT ENGINE */
@@ -30,27 +30,108 @@
3030

3131
#pragma once
3232

33-
#include <godot_cpp/classes/mutex.hpp>
33+
#include <godot_cpp/core/defs.hpp>
34+
35+
#ifdef MINGW_ENABLED
36+
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
37+
#include <thirdparty/mingw-std-threads/mingw.mutex.h>
38+
#define THREADING_NAMESPACE mingw_stdthread
39+
#else
40+
#include <mutex>
41+
#define THREADING_NAMESPACE std
42+
#endif
3443

3544
namespace godot {
3645

37-
class MutexLock {
38-
const Mutex &mutex;
46+
#ifdef THREADS_ENABLED
47+
48+
template <typename MutexT>
49+
class MutexLock;
50+
51+
template <typename StdMutexT>
52+
class MutexImpl {
53+
friend class MutexLock<MutexImpl<StdMutexT>>;
54+
55+
using StdMutexType = StdMutexT;
56+
57+
mutable StdMutexT mutex;
3958

4059
public:
41-
_ALWAYS_INLINE_ explicit MutexLock(const Mutex &p_mutex) :
42-
mutex(p_mutex) {
43-
const_cast<Mutex *>(&mutex)->lock();
60+
_ALWAYS_INLINE_ void lock() const {
61+
mutex.lock();
62+
}
63+
64+
_ALWAYS_INLINE_ void unlock() const {
65+
mutex.unlock();
4466
}
4567

46-
_ALWAYS_INLINE_ ~MutexLock() {
47-
const_cast<Mutex *>(&mutex)->unlock();
68+
_ALWAYS_INLINE_ bool try_lock() const {
69+
return mutex.try_lock();
4870
}
4971
};
5072

73+
template <typename MutexT>
74+
class [[nodiscard]] MutexLock {
75+
mutable THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
76+
77+
public:
78+
explicit MutexLock(const MutexT &p_mutex) :
79+
lock(p_mutex.mutex) {}
80+
81+
// Clarification: all the funny syntax is needed so this function exists only for binary mutexes.
82+
template <typename T = MutexT>
83+
_ALWAYS_INLINE_ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &_get_lock(
84+
typename std::enable_if<std::is_same<T, THREADING_NAMESPACE::mutex>::value> * = nullptr) const {
85+
return lock;
86+
}
87+
88+
_ALWAYS_INLINE_ void temp_relock() const {
89+
lock.lock();
90+
}
91+
92+
_ALWAYS_INLINE_ void temp_unlock() const {
93+
lock.unlock();
94+
}
95+
96+
// TODO: Implement a `try_temp_relock` if needed (will also need a dummy method below).
97+
};
98+
99+
using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use
100+
using BinaryMutex = MutexImpl<THREADING_NAMESPACE::mutex>; // Non-recursive, handle with care
101+
51102
#define _THREAD_SAFE_CLASS_ mutable Mutex _thread_safe_;
52103
#define _THREAD_SAFE_METHOD_ MutexLock _thread_safe_method_(_thread_safe_);
53104
#define _THREAD_SAFE_LOCK_ _thread_safe_.lock();
54105
#define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock();
55106

107+
#else // No threads.
108+
109+
class MutexImpl {
110+
mutable THREADING_NAMESPACE::mutex mutex;
111+
112+
public:
113+
void lock() const {}
114+
void unlock() const {}
115+
bool try_lock() const { return true; }
116+
};
117+
118+
template <typename MutexT>
119+
class [[nodiscard]] MutexLock {
120+
public:
121+
MutexLock(const MutexT &p_mutex) {}
122+
123+
void temp_relock() const {}
124+
void temp_unlock() const {}
125+
};
126+
127+
using Mutex = MutexImpl;
128+
using BinaryMutex = MutexImpl;
129+
130+
#define _THREAD_SAFE_CLASS_
131+
#define _THREAD_SAFE_METHOD_
132+
#define _THREAD_SAFE_LOCK_
133+
#define _THREAD_SAFE_UNLOCK_
134+
135+
#endif // THREADS_ENABLED
136+
56137
} // namespace godot

test/project/main.gd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,12 @@ func _ready():
310310
var przykład = ClassDB.instantiate("ExamplePrzykład")
311311
assert_equal(przykład.get_the_word(), "słowo to przykład")
312312

313+
# Test call some methods on a thread safe class.
314+
var example_thread_safe = ExampleThreadSafeClass.new()
315+
assert_equal(example_thread_safe.test(), 123)
316+
assert_equal(example_thread_safe.test_const(), 456)
317+
assert_equal(example_thread_safe.test_manual(), 789)
318+
313319
exit_with_status()
314320

315321
func _on_Example_custom_signal(signal_name, value):

test/src/example.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,3 +816,25 @@ void ExampleInternal::_bind_methods() {
816816
int ExampleInternal::get_the_answer() const {
817817
return 42;
818818
}
819+
820+
void ExampleThreadSafeClass::_bind_methods() {
821+
ClassDB::bind_method(D_METHOD("test"), &ExampleThreadSafeClass::test);
822+
ClassDB::bind_method(D_METHOD("test_const"), &ExampleThreadSafeClass::test_const);
823+
ClassDB::bind_method(D_METHOD("test_manual"), &ExampleThreadSafeClass::test_manual);
824+
}
825+
826+
int ExampleThreadSafeClass::test() {
827+
_THREAD_SAFE_METHOD_
828+
return 123;
829+
}
830+
831+
int ExampleThreadSafeClass::test_const() const {
832+
_THREAD_SAFE_METHOD_
833+
return 456;
834+
}
835+
836+
int ExampleThreadSafeClass::test_manual() {
837+
_THREAD_SAFE_LOCK_
838+
_THREAD_SAFE_UNLOCK_
839+
return 789;
840+
}

test/src/example.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <godot_cpp/classes/tile_set.hpp>
2222
#include <godot_cpp/classes/tween.hpp>
2323
#include <godot_cpp/classes/viewport.hpp>
24+
#include <godot_cpp/templates/mutex.hpp>
2425
#include <godot_cpp/variant/variant.hpp>
2526
#include <godot_cpp/variant/variant_internal.hpp>
2627

@@ -313,3 +314,17 @@ class ExampleInternal : public RefCounted {
313314
public:
314315
int get_the_answer() const;
315316
};
317+
318+
class ExampleThreadSafeClass : public RefCounted {
319+
GDCLASS(ExampleThreadSafeClass, RefCounted);
320+
321+
_THREAD_SAFE_CLASS_
322+
323+
protected:
324+
static void _bind_methods();
325+
326+
public:
327+
int test();
328+
int test_const() const;
329+
int test_manual();
330+
};

test/src/register_types.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
3232
GDREGISTER_RUNTIME_CLASS(ExampleRuntime);
3333
GDREGISTER_CLASS(ExamplePrzykład);
3434
GDREGISTER_INTERNAL_CLASS(ExampleInternal);
35+
GDREGISTER_CLASS(ExampleThreadSafeClass);
3536
}
3637

3738
void uninitialize_example_module(ModuleInitializationLevel p_level) {

0 commit comments

Comments
 (0)