1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
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
31#endif
32
33struct SDL_mutex {
34	pthread_mutex_t id;
35#if FAKE_RECURSIVE_MUTEX
36	int recursive;
37	pthread_t owner;
38#endif
39};
40
41SDL_mutex *SDL_CreateMutex (void)
42{
43	SDL_mutex *mutex;
44	pthread_mutexattr_t attr;
45
46	/* Allocate the structure */
47	mutex = (SDL_mutex *)SDL_calloc(1, sizeof(*mutex));
48	if ( mutex ) {
49		pthread_mutexattr_init(&attr);
50#if SDL_THREAD_PTHREAD_RECURSIVE_MUTEX
51		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
52#elif SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP
53		pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
54#else
55		/* No extra attributes necessary */
56#endif
57		if ( pthread_mutex_init(&mutex->id, &attr) != 0 ) {
58			SDL_SetError("pthread_mutex_init() failed");
59			SDL_free(mutex);
60			mutex = NULL;
61		}
62	} else {
63		SDL_OutOfMemory();
64	}
65	return(mutex);
66}
67
68void SDL_DestroyMutex(SDL_mutex *mutex)
69{
70	if ( mutex ) {
71		pthread_mutex_destroy(&mutex->id);
72		SDL_free(mutex);
73	}
74}
75
76/* Lock the mutex */
77int SDL_mutexP(SDL_mutex *mutex)
78{
79	int retval;
80#if FAKE_RECURSIVE_MUTEX
81	pthread_t this_thread;
82#endif
83
84	if ( mutex == NULL ) {
85		SDL_SetError("Passed a NULL mutex");
86		return -1;
87	}
88
89	retval = 0;
90#if FAKE_RECURSIVE_MUTEX
91	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		if ( pthread_mutex_lock(&mutex->id) == 0 ) {
100			mutex->owner = this_thread;
101			mutex->recursive = 0;
102		} else {
103			SDL_SetError("pthread_mutex_lock() failed");
104			retval = -1;
105		}
106	}
107#else
108	if ( pthread_mutex_lock(&mutex->id) < 0 ) {
109		SDL_SetError("pthread_mutex_lock() failed");
110		retval = -1;
111	}
112#endif
113	return retval;
114}
115
116int SDL_mutexV(SDL_mutex *mutex)
117{
118	int retval;
119
120	if ( mutex == NULL ) {
121		SDL_SetError("Passed a NULL mutex");
122		return -1;
123	}
124
125	retval = 0;
126#if FAKE_RECURSIVE_MUTEX
127	/* We can only unlock the mutex if we own it */
128	if ( pthread_self() == mutex->owner ) {
129		if ( mutex->recursive ) {
130			--mutex->recursive;
131		} else {
132			/* The order of operations is important.
133			   First reset the owner so another thread doesn't lock
134			   the mutex and set the ownership before we reset it,
135			   then release the lock semaphore.
136			 */
137			mutex->owner = 0;
138			pthread_mutex_unlock(&mutex->id);
139		}
140	} else {
141		SDL_SetError("mutex not owned by this thread");
142		retval = -1;
143	}
144
145#else
146	if ( pthread_mutex_unlock(&mutex->id) < 0 ) {
147		SDL_SetError("pthread_mutex_unlock() failed");
148		retval = -1;
149	}
150#endif /* FAKE_RECURSIVE_MUTEX */
151
152	return retval;
153}
154