1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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_thread.h" |
27 | |
28 | #if !SDL_THREAD_PTHREAD_RECURSIVE_MUTEX && \ |
29 | !SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP |
30 | #define FAKE_RECURSIVE_MUTEX 1 |
31 | #endif |
32 | |
33 | struct SDL_mutex |
34 | { |
35 | pthread_mutex_t id; |
36 | #if FAKE_RECURSIVE_MUTEX |
37 | int recursive; |
38 | pthread_t owner; |
39 | #endif |
40 | }; |
41 | |
42 | SDL_mutex * |
43 | SDL_CreateMutex(void) |
44 | { |
45 | SDL_mutex *mutex; |
46 | pthread_mutexattr_t attr; |
47 | |
48 | /* Allocate the structure */ |
49 | mutex = (SDL_mutex *) SDL_calloc(1, sizeof(*mutex)); |
50 | if (mutex) { |
51 | pthread_mutexattr_init(&attr); |
52 | #if SDL_THREAD_PTHREAD_RECURSIVE_MUTEX |
53 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); |
54 | #elif SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP |
55 | pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP); |
56 | #else |
57 | /* No extra attributes necessary */ |
58 | #endif |
59 | if (pthread_mutex_init(&mutex->id, &attr) != 0) { |
60 | SDL_SetError("pthread_mutex_init() failed" ); |
61 | SDL_free(mutex); |
62 | mutex = NULL; |
63 | } |
64 | } else { |
65 | SDL_OutOfMemory(); |
66 | } |
67 | return (mutex); |
68 | } |
69 | |
70 | void |
71 | SDL_DestroyMutex(SDL_mutex * mutex) |
72 | { |
73 | if (mutex) { |
74 | pthread_mutex_destroy(&mutex->id); |
75 | SDL_free(mutex); |
76 | } |
77 | } |
78 | |
79 | /* Lock the mutex */ |
80 | int |
81 | SDL_LockMutex(SDL_mutex * mutex) |
82 | { |
83 | #if FAKE_RECURSIVE_MUTEX |
84 | pthread_t this_thread; |
85 | #endif |
86 | |
87 | if (mutex == NULL) { |
88 | return SDL_SetError("Passed a NULL mutex" ); |
89 | } |
90 | |
91 | #if FAKE_RECURSIVE_MUTEX |
92 | this_thread = pthread_self(); |
93 | if (mutex->owner == this_thread) { |
94 | ++mutex->recursive; |
95 | } else { |
96 | /* The order of operations is important. |
97 | We set the locking thread id after we obtain the lock |
98 | so unlocks from other threads will fail. |
99 | */ |
100 | if (pthread_mutex_lock(&mutex->id) == 0) { |
101 | mutex->owner = this_thread; |
102 | mutex->recursive = 0; |
103 | } else { |
104 | return SDL_SetError("pthread_mutex_lock() failed" ); |
105 | } |
106 | } |
107 | #else |
108 | if (pthread_mutex_lock(&mutex->id) != 0) { |
109 | return SDL_SetError("pthread_mutex_lock() failed" ); |
110 | } |
111 | #endif |
112 | return 0; |
113 | } |
114 | |
115 | int |
116 | SDL_TryLockMutex(SDL_mutex * mutex) |
117 | { |
118 | int retval; |
119 | int result; |
120 | #if FAKE_RECURSIVE_MUTEX |
121 | pthread_t this_thread; |
122 | #endif |
123 | |
124 | if (mutex == NULL) { |
125 | return SDL_SetError("Passed a NULL mutex" ); |
126 | } |
127 | |
128 | retval = 0; |
129 | #if FAKE_RECURSIVE_MUTEX |
130 | this_thread = pthread_self(); |
131 | if (mutex->owner == this_thread) { |
132 | ++mutex->recursive; |
133 | } else { |
134 | /* The order of operations is important. |
135 | We set the locking thread id after we obtain the lock |
136 | so unlocks from other threads will fail. |
137 | */ |
138 | result = pthread_mutex_trylock(&mutex->id); |
139 | if (result == 0) { |
140 | mutex->owner = this_thread; |
141 | mutex->recursive = 0; |
142 | } else if (result == EBUSY) { |
143 | retval = SDL_MUTEX_TIMEDOUT; |
144 | } else { |
145 | retval = SDL_SetError("pthread_mutex_trylock() failed" ); |
146 | } |
147 | } |
148 | #else |
149 | result = pthread_mutex_trylock(&mutex->id); |
150 | if (result != 0) { |
151 | if (result == EBUSY) { |
152 | retval = SDL_MUTEX_TIMEDOUT; |
153 | } else { |
154 | retval = SDL_SetError("pthread_mutex_trylock() failed" ); |
155 | } |
156 | } |
157 | #endif |
158 | return retval; |
159 | } |
160 | |
161 | int |
162 | SDL_UnlockMutex(SDL_mutex * mutex) |
163 | { |
164 | if (mutex == NULL) { |
165 | return SDL_SetError("Passed a NULL mutex" ); |
166 | } |
167 | |
168 | #if FAKE_RECURSIVE_MUTEX |
169 | /* We can only unlock the mutex if we own it */ |
170 | if (pthread_self() == mutex->owner) { |
171 | if (mutex->recursive) { |
172 | --mutex->recursive; |
173 | } else { |
174 | /* The order of operations is important. |
175 | First reset the owner so another thread doesn't lock |
176 | the mutex and set the ownership before we reset it, |
177 | then release the lock semaphore. |
178 | */ |
179 | mutex->owner = 0; |
180 | pthread_mutex_unlock(&mutex->id); |
181 | } |
182 | } else { |
183 | return SDL_SetError("mutex not owned by this thread" ); |
184 | } |
185 | |
186 | #else |
187 | if (pthread_mutex_unlock(&mutex->id) != 0) { |
188 | return SDL_SetError("pthread_mutex_unlock() failed" ); |
189 | } |
190 | #endif /* FAKE_RECURSIVE_MUTEX */ |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | /* vi: set ts=4 sw=4 expandtab: */ |
196 | |