1/**************************************************************************/
2/* mutex.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef MUTEX_H
32#define MUTEX_H
33
34#include "core/error/error_macros.h"
35#include "core/typedefs.h"
36
37#include <mutex>
38
39template <class MutexT>
40class MutexLock;
41
42template <class StdMutexT>
43class MutexImpl {
44 friend class MutexLock<MutexImpl<StdMutexT>>;
45
46 using StdMutexType = StdMutexT;
47
48 mutable StdMutexT mutex;
49
50public:
51 _ALWAYS_INLINE_ void lock() const {
52 mutex.lock();
53 }
54
55 _ALWAYS_INLINE_ void unlock() const {
56 mutex.unlock();
57 }
58
59 _ALWAYS_INLINE_ bool try_lock() const {
60 return mutex.try_lock();
61 }
62};
63
64// A very special kind of mutex, used in scenarios where these
65// requirements hold at the same time:
66// - Must be used with a condition variable (only binary mutexes are suitable).
67// - Must have recursive semnantics (or simulate, as this one does).
68// The implementation keeps the lock count in TS. Therefore, only
69// one object of each version of the template can exists; hence the Tag argument.
70// Tags must be unique across the Godot codebase.
71// Also, don't forget to declare the thread_local variable on each use.
72template <int Tag>
73class SafeBinaryMutex {
74 friend class MutexLock<SafeBinaryMutex>;
75
76 using StdMutexType = std::mutex;
77
78 mutable std::mutex mutex;
79 static thread_local uint32_t count;
80
81public:
82 _ALWAYS_INLINE_ void lock() const {
83 if (++count == 1) {
84 mutex.lock();
85 }
86 }
87
88 _ALWAYS_INLINE_ void unlock() const {
89 DEV_ASSERT(count);
90 if (--count == 0) {
91 mutex.unlock();
92 }
93 }
94
95 _ALWAYS_INLINE_ bool try_lock() const {
96 if (count) {
97 count++;
98 return true;
99 } else {
100 if (mutex.try_lock()) {
101 count++;
102 return true;
103 } else {
104 return false;
105 }
106 }
107 }
108
109 ~SafeBinaryMutex() {
110 DEV_ASSERT(!count);
111 }
112};
113
114template <class MutexT>
115class MutexLock {
116 friend class ConditionVariable;
117
118 std::unique_lock<typename MutexT::StdMutexType> lock;
119
120public:
121 _ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) :
122 lock(p_mutex.mutex){};
123};
124
125// This specialization is needed so manual locking and MutexLock can be used
126// at the same time on a SafeBinaryMutex.
127template <int Tag>
128class MutexLock<SafeBinaryMutex<Tag>> {
129 friend class ConditionVariable;
130
131 std::unique_lock<std::mutex> lock;
132
133public:
134 _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
135 lock(p_mutex.mutex) {
136 SafeBinaryMutex<Tag>::count++;
137 };
138 _ALWAYS_INLINE_ ~MutexLock() {
139 SafeBinaryMutex<Tag>::count--;
140 };
141};
142
143using Mutex = MutexImpl<std::recursive_mutex>; // Recursive, for general use
144using BinaryMutex = MutexImpl<std::mutex>; // Non-recursive, handle with care
145
146extern template class MutexImpl<std::recursive_mutex>;
147extern template class MutexImpl<std::mutex>;
148extern template class MutexLock<MutexImpl<std::recursive_mutex>>;
149extern template class MutexLock<MutexImpl<std::mutex>>;
150
151#endif // MUTEX_H
152