1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include <errno.h>
24#include <pthread.h>
25
26#include "SDL_sysmutex_c.h"
27
28SDL_Mutex *SDL_CreateMutex(void)
29{
30 SDL_Mutex *mutex;
31 pthread_mutexattr_t attr;
32
33 // Allocate the structure
34 mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex));
35 if (mutex) {
36 pthread_mutexattr_init(&attr);
37#ifdef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX
38 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
39#elif defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP)
40 pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
41#else
42 // No extra attributes necessary
43#endif
44 if (pthread_mutex_init(&mutex->id, &attr) != 0) {
45 SDL_SetError("pthread_mutex_init() failed");
46 SDL_free(mutex);
47 mutex = NULL;
48 }
49 }
50 return mutex;
51}
52
53void SDL_DestroyMutex(SDL_Mutex *mutex)
54{
55 if (mutex) {
56 pthread_mutex_destroy(&mutex->id);
57 SDL_free(mutex);
58 }
59}
60
61void SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
62{
63 if (mutex) {
64#ifdef FAKE_RECURSIVE_MUTEX
65 pthread_t this_thread = pthread_self();
66 if (mutex->owner == this_thread) {
67 ++mutex->recursive;
68 } else {
69 /* The order of operations is important.
70 We set the locking thread id after we obtain the lock
71 so unlocks from other threads will fail.
72 */
73 const int rc = pthread_mutex_lock(&mutex->id);
74 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
75 mutex->owner = this_thread;
76 mutex->recursive = 0;
77 }
78#else
79 const int rc = pthread_mutex_lock(&mutex->id);
80 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
81#endif
82 }
83}
84
85bool SDL_TryLockMutex(SDL_Mutex *mutex)
86{
87 bool result = true;
88
89 if (mutex) {
90#ifdef FAKE_RECURSIVE_MUTEX
91 pthread_t this_thread = pthread_self();
92 if (mutex->owner == this_thread) {
93 ++mutex->recursive;
94 } else {
95 /* The order of operations is important.
96 We set the locking thread id after we obtain the lock
97 so unlocks from other threads will fail.
98 */
99 const int rc = pthread_mutex_trylock(&mutex->id);
100 if (rc == 0) {
101 mutex->owner = this_thread;
102 mutex->recursive = 0;
103 } else if (rc == EBUSY) {
104 result = false;
105 } else {
106 SDL_assert(!"Error trying to lock mutex"); // assume we're in a lot of trouble if this assert fails.
107 result = false;
108 }
109 }
110#else
111 const int rc = pthread_mutex_trylock(&mutex->id);
112 if (rc != 0) {
113 if (rc == EBUSY) {
114 result = false;
115 } else {
116 SDL_assert(!"Error trying to lock mutex"); // assume we're in a lot of trouble if this assert fails.
117 result = false;
118 }
119 }
120#endif
121 }
122
123 return result;
124}
125
126void SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes
127{
128 if (mutex) {
129#ifdef FAKE_RECURSIVE_MUTEX
130 // We can only unlock the mutex if we own it
131 if (pthread_self() == mutex->owner) {
132 if (mutex->recursive) {
133 --mutex->recursive;
134 } else {
135 /* The order of operations is important.
136 First reset the owner so another thread doesn't lock
137 the mutex and set the ownership before we reset it,
138 then release the lock semaphore.
139 */
140 mutex->owner = 0;
141 pthread_mutex_unlock(&mutex->id);
142 }
143 } else {
144 SDL_SetError("mutex not owned by this thread");
145 return;
146 }
147
148#else
149 const int rc = pthread_mutex_unlock(&mutex->id);
150 SDL_assert(rc == 0); // assume we're in a lot of trouble if this assert fails.
151#endif // FAKE_RECURSIVE_MUTEX
152 }
153}
154
155