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