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