Skip to content

Commit 82c6c44

Browse files
authored
Merge pull request #2032 from Ivorforce/native-mutex
Add `Mutex` from upstream Godot, and put the upstream `Mutex` into `CoreBind`
2 parents eb006b6 + 1f546a5 commit 82c6c44

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:
@@ -2102,7 +2105,10 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
21022105

21032106
result.append("")
21042107

2105-
result.append("namespace godot {")
2108+
if class_name == "Mutex":
2109+
result.append("namespace godot::CoreBind {")
2110+
else:
2111+
result.append("namespace godot {")
21062112
result.append("")
21072113

21082114
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
@@ -313,6 +313,12 @@ func _ready():
313313
var przykład = ClassDB.instantiate("ExamplePrzykład")
314314
assert_equal(przykład.get_the_word(), "słowo to przykład")
315315

316+
# Test call some methods on a thread safe class.
317+
var example_thread_safe = ExampleThreadSafeClass.new()
318+
assert_equal(example_thread_safe.test(), 123)
319+
assert_equal(example_thread_safe.test_const(), 456)
320+
assert_equal(example_thread_safe.test_manual(), 789)
321+
316322
exit_with_status()
317323

318324
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
@@ -823,3 +823,25 @@ void ExampleInternal::_bind_methods() {
823823
int ExampleInternal::get_the_answer() const {
824824
return 42;
825825
}
826+
827+
void ExampleThreadSafeClass::_bind_methods() {
828+
ClassDB::bind_method(D_METHOD("test"), &ExampleThreadSafeClass::test);
829+
ClassDB::bind_method(D_METHOD("test_const"), &ExampleThreadSafeClass::test_const);
830+
ClassDB::bind_method(D_METHOD("test_manual"), &ExampleThreadSafeClass::test_manual);
831+
}
832+
833+
int ExampleThreadSafeClass::test() {
834+
_THREAD_SAFE_METHOD_
835+
return 123;
836+
}
837+
838+
int ExampleThreadSafeClass::test_const() const {
839+
_THREAD_SAFE_METHOD_
840+
return 456;
841+
}
842+
843+
int ExampleThreadSafeClass::test_manual() {
844+
_THREAD_SAFE_LOCK_
845+
_THREAD_SAFE_UNLOCK_
846+
return 789;
847+
}

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

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

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)