Implemented a read-write lock using only mutex_t and condvar_t (in case the pthread_r...
[strongswan.git] / src / libstrongswan / threading / rwlock.c
1 /*
2 * Copyright (C) 2008-2009 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <pthread.h>
19
20 #include <threading.h>
21 #include <library.h>
22 #include <debug.h>
23
24 #include "rwlock.h"
25 #include "lock_profiler.h"
26
27 #ifdef HAVE_PTHREAD_RWLOCK_INIT
28
29 /**
30 * Implementation of rwlock_t.read_lock
31 */
32 static void read_lock(private_rwlock_t *this)
33 {
34 int err;
35
36 profiler_start(&this->profile);
37 err = pthread_rwlock_rdlock(&this->rwlock);
38 if (err != 0)
39 {
40 DBG1("!!! RWLOCK READ LOCK ERROR: %s !!!", strerror(err));
41 }
42 profiler_end(&this->profile);
43 }
44
45 /**
46 * Implementation of rwlock_t.write_lock
47 */
48 static void write_lock(private_rwlock_t *this)
49 {
50 int err;
51
52 profiler_start(&this->profile);
53 err = pthread_rwlock_wrlock(&this->rwlock);
54 if (err != 0)
55 {
56 DBG1("!!! RWLOCK WRITE LOCK ERROR: %s !!!", strerror(err));
57 }
58 profiler_end(&this->profile);
59 }
60
61 /**
62 * Implementation of rwlock_t.try_write_lock
63 */
64 static bool try_write_lock(private_rwlock_t *this)
65 {
66 return pthread_rwlock_trywrlock(&this->rwlock) == 0;
67 }
68
69 /**
70 * Implementation of rwlock_t.unlock
71 */
72 static void rw_unlock(private_rwlock_t *this)
73 {
74 int err;
75
76 err = pthread_rwlock_unlock(&this->rwlock);
77 if (err != 0)
78 {
79 DBG1("!!! RWLOCK UNLOCK ERROR: %s !!!", strerror(err));
80 }
81 }
82
83 /**
84 * Implementation of rwlock_t.destroy
85 */
86 static void rw_destroy(private_rwlock_t *this)
87 {
88 pthread_rwlock_destroy(&this->rwlock);
89 profiler_cleanup(&this->profile);
90 free(this);
91 }
92
93 /*
94 * see header file
95 */
96 rwlock_t *rwlock_create(rwlock_type_t type)
97 {
98 switch (type)
99 {
100 case RWLOCK_TYPE_DEFAULT:
101 default:
102 {
103 private_rwlock_t *this = malloc_thing(private_rwlock_t);
104
105 this->public.read_lock = (void(*)(rwlock_t*))read_lock;
106 this->public.write_lock = (void(*)(rwlock_t*))write_lock;
107 this->public.try_write_lock = (bool(*)(rwlock_t*))try_write_lock;
108 this->public.unlock = (void(*)(rwlock_t*))rw_unlock;
109 this->public.destroy = (void(*)(rwlock_t*))rw_destroy;
110
111 pthread_rwlock_init(&this->rwlock, NULL);
112 profiler_init(&this->profile);
113
114 return &this->public;
115 }
116 }
117 }
118
119 #else /* HAVE_PTHREAD_RWLOCK_INIT */
120
121 /**
122 * This implementation of the rwlock_t interface uses mutex_t and condvar_t
123 * primitives, if the pthread_rwlock_* group of functions is not available.
124 *
125 * The following constraints are enforced:
126 * - Multiple readers can hold the lock at the same time.
127 * - Only a single writer can hold the lock at any given time.
128 * - A writer must block until all readers have released the lock before
129 * obtaining the lock exclusively.
130 * - Readers that arrive while a writer is waiting to acquire the lock will
131 * block until after the writer has obtained and released the lock.
132 * These constraints allow for read sharing, prevent write sharing, prevent
133 * read-write sharing and prevent starvation of writers by a steady stream
134 * of incoming readers. Reader starvation is not prevented (this could happen
135 * if there are more writers than readers).
136 *
137 * The implementation does not support recursive locking and readers must not
138 * acquire the lock exclusively at the same time and vice-versa (this is not
139 * checked or enforced so behave yourself to prevent deadlocks).
140 */
141
142 /**
143 * Implementation of rwlock_t.read_lock
144 */
145 static void read_lock(private_rwlock_t *this)
146 {
147 profiler_start(&this->profile);
148 this->mutex->lock(this->mutex);
149 while (this->writer || this->waiting_writers)
150 {
151 this->readers->wait(this->readers, this->mutex);
152 }
153 this->reader_count++;
154 profiler_end(&this->profile);
155 this->mutex->unlock(this->mutex);
156 }
157
158 /**
159 * Implementation of rwlock_t.write_lock
160 */
161 static void write_lock(private_rwlock_t *this)
162 {
163 profiler_start(&this->profile);
164 this->mutex->lock(this->mutex);
165 this->waiting_writers++;
166 while (this->writer || this->reader_count)
167 {
168 this->writers->wait(this->writers, this->mutex);
169 }
170 this->waiting_writers--;
171 this->writer = pthread_self();
172 profiler_end(&this->profile);
173 this->mutex->unlock(this->mutex);
174 }
175
176 /**
177 * Implementation of rwlock_t.try_write_lock
178 */
179 static bool try_write_lock(private_rwlock_t *this)
180 {
181 bool res = FALSE;
182 this->mutex->lock(this->mutex);
183 if (!this->writer && !this->reader_count)
184 {
185 res = TRUE;
186 this->writer = pthread_self();
187 }
188 this->mutex->unlock(this->mutex);
189 return res;
190 }
191
192 /**
193 * Implementation of rwlock_t.unlock
194 */
195 static void rw_unlock(private_rwlock_t *this)
196 {
197 this->mutex->lock(this->mutex);
198 if (this->writer == pthread_self())
199 {
200 this->writer = 0;
201 if (this->waiting_writers)
202 {
203 this->writers->signal(this->writers);
204 }
205 else
206 {
207 this->readers->broadcast(this->readers);
208 }
209 }
210 else
211 {
212 this->reader_count--;
213 if (!this->reader_count)
214 {
215 this->writers->signal(this->writers);
216 }
217 }
218 this->mutex->unlock(this->mutex);
219 }
220
221 /**
222 * Implementation of rwlock_t.destroy
223 */
224 static void rw_destroy(private_rwlock_t *this)
225 {
226 this->mutex->destroy(this->mutex);
227 this->writers->destroy(this->writers);
228 this->readers->destroy(this->readers);
229 profiler_cleanup(&this->profile);
230 free(this);
231 }
232
233 /*
234 * see header file
235 */
236 rwlock_t *rwlock_create(rwlock_type_t type)
237 {
238 switch (type)
239 {
240 case RWLOCK_TYPE_DEFAULT:
241 default:
242 {
243 private_rwlock_t *this = malloc_thing(private_rwlock_t);
244
245 this->public.read_lock = (void(*)(rwlock_t*))read_lock;
246 this->public.write_lock = (void(*)(rwlock_t*))write_lock;
247 this->public.try_write_lock = (bool(*)(rwlock_t*))try_write_lock;
248 this->public.unlock = (void(*)(rwlock_t*))rw_unlock;
249 this->public.destroy = (void(*)(rwlock_t*))rw_destroy;
250
251 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
252 this->writers = condvar_create(CONDVAR_TYPE_DEFAULT);
253 this->readers = condvar_create(CONDVAR_TYPE_DEFAULT);
254 this->waiting_writers = 0;
255 this->reader_count = 0;
256 this->writer = 0;
257
258 profiler_init(&this->profile);
259
260 return &this->public;
261 }
262 }
263 }
264
265 #endif /* HAVE_PTHREAD_RWLOCK_INIT */
266