1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 2006-2011 The OpenLDAP Foundation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
11 * A copy of this license is available in file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
14 */
15/* This work was initially developed by Howard Chu for inclusion
16 * in OpenLDAP Software.
17 */
18
19/*
20 * This is an implementation of recursive mutexes.
21 */
22
23#include "portable.h"
24
25#include <ac/stdlib.h>
26
27#include <ac/errno.h>
28#include <ac/string.h>
29#include <ac/time.h>
30
31#include "ldap-int.h"
32#include "ldap_pvt_thread.h" /* Get the thread interface */
33
34struct ldap_int_thread_rmutex_s {
35	ldap_pvt_thread_mutex_t ltrm_mutex;
36	ldap_pvt_thread_cond_t ltrm_cond;
37	ldap_pvt_thread_t ltrm_owner;
38	int ltrm_valid;
39#define LDAP_PVT_THREAD_RMUTEX_VALID	0x0cdb
40	int ltrm_depth;
41	int ltrm_waits;
42};
43
44static const ldap_pvt_thread_t tid_zero;
45
46int
47ldap_pvt_thread_rmutex_init( ldap_pvt_thread_rmutex_t *rmutex )
48{
49	struct ldap_int_thread_rmutex_s *rm;
50
51	assert( rmutex != NULL );
52
53	rm = (struct ldap_int_thread_rmutex_s *) LDAP_CALLOC( 1,
54		sizeof( struct ldap_int_thread_rmutex_s ) );
55	if ( !rm )
56		return LDAP_NO_MEMORY;
57
58	/* we should check return results */
59	ldap_pvt_thread_mutex_init( &rm->ltrm_mutex );
60	ldap_pvt_thread_cond_init( &rm->ltrm_cond );
61
62	rm->ltrm_valid = LDAP_PVT_THREAD_RMUTEX_VALID;
63
64	*rmutex = rm;
65	return 0;
66}
67
68int
69ldap_pvt_thread_rmutex_destroy( ldap_pvt_thread_rmutex_t *rmutex )
70{
71	struct ldap_int_thread_rmutex_s *rm;
72
73	assert( rmutex != NULL );
74	rm = *rmutex;
75
76	assert( rm != NULL );
77	assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
78
79	if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
80		return LDAP_PVT_THREAD_EINVAL;
81
82	ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
83
84	assert( rm->ltrm_depth >= 0 );
85	assert( rm->ltrm_waits >= 0 );
86
87	/* in use? */
88	if( rm->ltrm_depth > 0 || rm->ltrm_waits > 0 ) {
89		ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
90		return LDAP_PVT_THREAD_EBUSY;
91	}
92
93	rm->ltrm_valid = 0;
94
95	ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
96
97	ldap_pvt_thread_mutex_destroy( &rm->ltrm_mutex );
98	ldap_pvt_thread_cond_destroy( &rm->ltrm_cond );
99
100	LDAP_FREE(rm);
101	*rmutex = NULL;
102	return 0;
103}
104
105int ldap_pvt_thread_rmutex_lock( ldap_pvt_thread_rmutex_t *rmutex,
106	ldap_pvt_thread_t owner )
107{
108	struct ldap_int_thread_rmutex_s *rm;
109
110	assert( rmutex != NULL );
111	rm = *rmutex;
112
113	assert( rm != NULL );
114	assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
115
116	if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
117		return LDAP_PVT_THREAD_EINVAL;
118
119	ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
120
121	assert( rm->ltrm_depth >= 0 );
122	assert( rm->ltrm_waits >= 0 );
123
124	if( rm->ltrm_depth > 0 ) {
125		/* already locked */
126		if ( !ldap_pvt_thread_equal( rm->ltrm_owner, owner )) {
127			rm->ltrm_waits++;
128			do {
129				ldap_pvt_thread_cond_wait( &rm->ltrm_cond,
130					&rm->ltrm_mutex );
131			} while( rm->ltrm_depth > 0 );
132
133			rm->ltrm_waits--;
134			assert( rm->ltrm_waits >= 0 );
135			rm->ltrm_owner = owner;
136		}
137	} else {
138		rm->ltrm_owner = owner;
139	}
140
141	rm->ltrm_depth++;
142
143	ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
144
145	return 0;
146}
147
148int ldap_pvt_thread_rmutex_trylock( ldap_pvt_thread_rmutex_t *rmutex,
149	ldap_pvt_thread_t owner )
150{
151	struct ldap_int_thread_rmutex_s *rm;
152
153	assert( rmutex != NULL );
154	rm = *rmutex;
155
156	assert( rm != NULL );
157	assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
158
159	if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
160		return LDAP_PVT_THREAD_EINVAL;
161
162	ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
163
164	assert( rm->ltrm_depth >= 0 );
165	assert( rm->ltrm_waits >= 0 );
166
167	if( rm->ltrm_depth > 0 ) {
168		if ( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) {
169			ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
170			return LDAP_PVT_THREAD_EBUSY;
171		}
172	} else {
173		rm->ltrm_owner = owner;
174	}
175
176	rm->ltrm_depth++;
177
178	ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
179
180	return 0;
181}
182
183int ldap_pvt_thread_rmutex_unlock( ldap_pvt_thread_rmutex_t *rmutex,
184	ldap_pvt_thread_t owner )
185{
186	struct ldap_int_thread_rmutex_s *rm;
187
188	assert( rmutex != NULL );
189	rm = *rmutex;
190
191	assert( rm != NULL );
192	assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
193
194	if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
195		return LDAP_PVT_THREAD_EINVAL;
196
197	ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
198
199	if( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) {
200		ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
201		return LDAP_PVT_THREAD_EINVAL;
202	}
203
204	rm->ltrm_depth--;
205	if ( !rm->ltrm_depth )
206		rm->ltrm_owner = tid_zero;
207
208	assert( rm->ltrm_depth >= 0 );
209	assert( rm->ltrm_waits >= 0 );
210
211	if ( !rm->ltrm_depth && rm->ltrm_waits ) {
212		ldap_pvt_thread_cond_signal( &rm->ltrm_cond );
213	}
214
215	ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
216
217	return 0;
218}
219
220