1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*! \file Inventor/C/threads/common.h */
34
35 /*!
36 \struct cc_rwmutex common.h Inventor/C/threads/common.h
37 \ingroup threads
38 \brief The structure for a read-write mutex.
39 */
40
41 /*!
42 \typedef struct cc_rwmutex cc_rwmutex
43 \ingroup threads
44 \brief The type definition for the read-write mutex structure.
45 */
46
47 /*!
48 \enum cc_precedence {CC_READ_PRECEDENCE, CC_WRITE_PRECEDENCE}
49 \ingroup threads
50 \brief The precedence values for the read-write mutex.
51 \details
52 CC_READ_PRECEDENCE is the default.
53 */
54
55 /*!
56 \typedef enum cc_precedence cc_precedence
57 \ingroup threads
58 \brief The type definition of the cc_precedence enumerator.
59 */
60
61 /*! \file rwmutex.h */
62 #include <Inventor/C/threads/rwmutex.h>
63
64 #include <cstdlib>
65 #include <cassert>
66
67 #include <Inventor/C/errors/debugerror.h>
68 #include <Inventor/C/threads/mutex.h>
69 #include <Inventor/C/threads/condvar.h>
70
71 #include "threads/rwmutexp.h"
72 #include "tidbitsp.h"
73
74 /* ********************************************************************** */
75
76 /* debugging. for instance useful for checking that there's not
77 excessive mutex construction. */
78
79 /* these are declared in mutex.cpp */
80 extern unsigned int cc_debug_mtxcount;
81 extern const char * COIN_DEBUG_MUTEX_COUNT;
82
83 /**************************************************************************/
84
85 /*!
86 \internal
87 */
88 void
cc_rwmutex_struct_init(cc_rwmutex * rwmutex)89 cc_rwmutex_struct_init(cc_rwmutex * rwmutex)
90 {
91 cc_mutex_struct_init(&rwmutex->mutex);
92 cc_condvar_struct_init(&rwmutex->read);
93 cc_condvar_struct_init(&rwmutex->write);
94
95 rwmutex->readers = 0;
96 rwmutex->readwaiters = 0;
97 rwmutex->writers = 0;
98 rwmutex->writewaiters = 0;
99 rwmutex->policy = CC_READ_PRECEDENCE;
100 }
101
102 /*!
103 \internal
104 */
105 void
cc_rwmutex_struct_clean(cc_rwmutex * rwmutex)106 cc_rwmutex_struct_clean(cc_rwmutex * rwmutex)
107 {
108 cc_mutex_struct_clean(&rwmutex->mutex);
109 cc_condvar_struct_clean(&rwmutex->read);
110 cc_condvar_struct_clean(&rwmutex->write);
111 }
112
113 /*!
114 Construct a read-write mutex, with read precedence.
115 */
116
117 cc_rwmutex *
cc_rwmutex_construct(void)118 cc_rwmutex_construct(void)
119 {
120 cc_rwmutex * rwmutex;
121 rwmutex = (cc_rwmutex *) malloc(sizeof(cc_rwmutex));
122 assert(rwmutex != NULL);
123 cc_rwmutex_struct_init(rwmutex);
124
125 { /* debugging */
126 const char * env = coin_getenv(COIN_DEBUG_MUTEX_COUNT);
127 if (env && (atoi(env) > 0)) {
128 cc_debug_mtxcount += 1;
129 (void)fprintf(stderr, "DEBUG: live mutexes +1 => %u (rwmutex++)\n",
130 cc_debug_mtxcount);
131 }
132 }
133
134 return rwmutex;
135 }
136
137 /*!
138 Construct a read-write mutex, with read precedence or write precedence.
139 */
140
141 cc_rwmutex *
cc_rwmutex_construct_etc(enum cc_precedence policy)142 cc_rwmutex_construct_etc(enum cc_precedence policy)
143 {
144 cc_rwmutex * rwmutex;
145 assert((policy == CC_READ_PRECEDENCE) || (policy == CC_WRITE_PRECEDENCE));
146 rwmutex = cc_rwmutex_construct();
147 assert(rwmutex != NULL);
148 rwmutex->policy = policy;
149 return rwmutex;
150 }
151
152 /*!
153 Destruct a read-write mutex.
154 */
155
156 void
cc_rwmutex_destruct(cc_rwmutex * rwmutex)157 cc_rwmutex_destruct(cc_rwmutex * rwmutex)
158 {
159 { /* debugging */
160 const char * env = coin_getenv(COIN_DEBUG_MUTEX_COUNT);
161 if (env && (atoi(env) > 0)) {
162 assert((cc_debug_mtxcount > 0) && "skewed mutex construct/destruct pairing");
163 cc_debug_mtxcount -= 1;
164 (void)fprintf(stderr, "DEBUG: live mutexes -1 => %u (rwmutex--)\n",
165 cc_debug_mtxcount);
166 }
167 }
168
169 assert(rwmutex != NULL);
170 cc_rwmutex_struct_clean(rwmutex);
171 free(rwmutex);
172 }
173
174 /* ********************************************************************** */
175
176 /*! Locks the specified \a rwmutex for writing. */
177 int
cc_rwmutex_write_lock(cc_rwmutex * rwmutex)178 cc_rwmutex_write_lock(cc_rwmutex * rwmutex)
179 {
180 (void) cc_mutex_lock(&rwmutex->mutex);
181 if (rwmutex->readers == 0 &&
182 rwmutex->writers == 0 &&
183 rwmutex->readwaiters == 0 &&
184 rwmutex->writewaiters == 0) {
185 rwmutex->writers++;
186 (void) cc_mutex_unlock(&rwmutex->mutex);
187 return CC_OK;
188 }
189 rwmutex->writewaiters++;
190
191 /* loop in case some other thread acquires the lock while we wait
192 for the signal */
193 do {
194 (void) cc_condvar_wait(&rwmutex->write, &rwmutex->mutex);
195 } while (rwmutex->readers != 0 || rwmutex->writers != 0);
196 rwmutex->writers++;
197 rwmutex->writewaiters--;
198 assert(rwmutex->writewaiters >= 0);
199 (void) cc_mutex_unlock(&rwmutex->mutex);
200 return CC_OK;
201 } /* cc_rwmutex_write_lock() */
202
203 /*! Check whether the given \a rwmutex* is available for write locking. */
204
205 int
cc_rwmutex_write_try_lock(cc_rwmutex * rwmutex)206 cc_rwmutex_write_try_lock(cc_rwmutex * rwmutex)
207 {
208 (void) cc_mutex_lock(&rwmutex->mutex);
209 if (rwmutex->readers == 0 &&
210 rwmutex->writers == 0 &&
211 rwmutex->readwaiters == 0 &&
212 rwmutex->writewaiters == 0) {
213 rwmutex->writers++;
214 (void) cc_mutex_unlock(&rwmutex->mutex);
215 return CC_OK;
216 }
217 (void) cc_mutex_unlock(&rwmutex->mutex);
218 return CC_BUSY;
219 } /* cc_rwmutex_write_try_lock() */
220
221 /*! Unlock the write lock on the given \a rwmutex. */
222
223 int
cc_rwmutex_write_unlock(cc_rwmutex * rwmutex)224 cc_rwmutex_write_unlock(cc_rwmutex * rwmutex)
225 {
226 int rwait;
227 int wwait;
228 (void) cc_mutex_lock(&rwmutex->mutex);
229 rwmutex->writers--;
230 assert(rwmutex->writers >= 0);
231 rwait = rwmutex->readwaiters;
232 wwait = rwmutex->writewaiters;
233
234 if (rwmutex->policy == CC_READ_PRECEDENCE) {
235 if (rwait) cc_condvar_wake_all(&rwmutex->read);
236 else cc_condvar_wake_one(&rwmutex->write);
237 }
238 else {
239 if (wwait) cc_condvar_wake_one(&rwmutex->write);
240 else cc_condvar_wake_all(&rwmutex->read);
241 }
242 (void) cc_mutex_unlock(&rwmutex->mutex);
243 return CC_OK;
244 }
245
246 /*! Locks the specified \a rwmutex for reading. */
247 int
cc_rwmutex_read_lock(cc_rwmutex * rwmutex)248 cc_rwmutex_read_lock(cc_rwmutex * rwmutex)
249 {
250 assert(rwmutex != NULL);
251 (void) cc_mutex_lock(&rwmutex->mutex);
252 if (rwmutex->writers == 0) {
253 rwmutex->readers++;
254 (void) cc_mutex_unlock(&rwmutex->mutex);
255 return CC_OK;
256 }
257 rwmutex->readwaiters++;
258
259 /* loop in case some other thread acquires the lock while we wait
260 for the signal */
261 do {
262 (void) cc_condvar_wait(&rwmutex->read, &rwmutex->mutex);
263 } while (rwmutex->writers != 0);
264
265 rwmutex->readers++;
266 rwmutex->readwaiters--;
267 assert(rwmutex->readwaiters >= 0);
268 (void) cc_mutex_unlock(&rwmutex->mutex);
269 return CC_OK;
270 }
271
272 /*! Check whether the given \a rwmutex* is available for read locking. */
273
274 int
cc_rwmutex_read_try_lock(cc_rwmutex * rwmutex)275 cc_rwmutex_read_try_lock(cc_rwmutex * rwmutex)
276 {
277 assert(rwmutex != NULL);
278
279 (void) cc_mutex_lock(&rwmutex->mutex);
280 if (rwmutex->writers == 0 &&
281 rwmutex->writewaiters == 0) {
282 rwmutex->readers++;
283 (void) cc_mutex_unlock(&rwmutex->mutex);
284 return CC_OK;
285 }
286 (void) cc_mutex_unlock(&rwmutex->mutex);
287 return CC_BUSY;
288 }
289
290 /*! Unlock the read lock on the given \a rwmutex. */
291
292 int
cc_rwmutex_read_unlock(cc_rwmutex * rwmutex)293 cc_rwmutex_read_unlock(cc_rwmutex * rwmutex)
294 {
295 int rwait;
296 int wwait;
297 int readers;
298 (void) cc_mutex_lock(&rwmutex->mutex);
299 rwmutex->readers--;
300 readers = rwmutex->readers;
301 assert(readers >= 0);
302 rwait = rwmutex->readwaiters;
303 wwait = rwmutex->writewaiters;
304
305 if (rwmutex->policy == CC_READ_PRECEDENCE || readers) {
306 if (rwait) cc_condvar_wake_all(&rwmutex->read);
307 else if (!readers) cc_condvar_wake_one(&rwmutex->write);
308 }
309 else {
310 if (wwait) cc_condvar_wake_one(&rwmutex->write);
311 else cc_condvar_wake_all(&rwmutex->read);
312 }
313 (void) cc_mutex_unlock(&rwmutex->mutex);
314 return CC_OK;
315 } /* cc_rwmutex_read_unlock() */
316
317