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_recmutex common.h Inventor/C/threads/common.h
37   \ingroup threads
38   \brief The structure for the recursive mutex.
39 */
40 
41 /*!
42   \typedef struct cc_recmutex cc_recmutex
43   \ingroup threads
44   \brief The type definition for the recursive mutex structure.
45 */
46 
47 /*! \file recmutex.h */
48 #include <Inventor/C/threads/recmutex.h>
49 
50 #include <cstdlib>
51 #include <cassert>
52 
53 #include <Inventor/C/errors/debugerror.h>
54 #include <Inventor/C/threads/mutex.h>
55 #include <Inventor/C/threads/thread.h>
56 #include <Inventor/C/threads/condvar.h>
57 
58 #include "tidbitsp.h"
59 #include "threads/recmutexp.h"
60 
61 /* ********************************************************************** */
62 
63 /* debugging. for instance useful for checking that there's not
64    excessive mutex construction. */
65 
66 /* these are declared in mutex.cpp */
67 extern unsigned int cc_debug_mtxcount;
68 extern const char * COIN_DEBUG_MUTEX_COUNT;
69 
70 /* ********************************************************************** */
71 
72 /*!
73   \internal
74 */
75 void
cc_recmutex_struct_init(cc_recmutex * recmutex)76 cc_recmutex_struct_init(cc_recmutex * recmutex)
77 {
78   cc_mutex_struct_init(&recmutex->mutex);
79   cc_condvar_struct_init(&recmutex->condvar);
80 
81   recmutex->threadid = 0;
82   recmutex->level = 0;
83   recmutex->waiters = 0;
84 }
85 
86 /*!
87   \internal
88 */
89 void
cc_recmutex_struct_clean(cc_recmutex * recmutex)90 cc_recmutex_struct_clean(cc_recmutex * recmutex)
91 {
92   cc_mutex_struct_clean(&recmutex->mutex);
93   cc_condvar_struct_clean(&recmutex->condvar);
94 }
95 
96 /*! Constructs a recursive mutex. */
97 
98 cc_recmutex *
cc_recmutex_construct(void)99 cc_recmutex_construct(void)
100 {
101   cc_recmutex * recmutex;
102   recmutex = (cc_recmutex *) malloc(sizeof(cc_recmutex));
103   assert(recmutex != NULL);
104   cc_recmutex_struct_init(recmutex);
105 
106   { /* debugging */
107     const char * env = coin_getenv(COIN_DEBUG_MUTEX_COUNT);
108     if (env && (atoi(env) > 0)) {
109       cc_debug_mtxcount += 1;
110       (void)fprintf(stderr, "DEBUG: live mutexes +1 => %u (recmutex++)\n",
111                     cc_debug_mtxcount);
112     }
113   }
114 
115   return recmutex;
116 }
117 
118 
119 /*! Destroys the recursive mutex \a recmutex. */
120 
121 void
cc_recmutex_destruct(cc_recmutex * recmutex)122 cc_recmutex_destruct(cc_recmutex * recmutex)
123 {
124   { /* debugging */
125     const char * env = coin_getenv(COIN_DEBUG_MUTEX_COUNT);
126     if (env && (atoi(env) > 0)) {
127       assert((cc_debug_mtxcount > 0) && "skewed mutex construct/destruct pairing");
128       cc_debug_mtxcount -= 1;
129       (void)fprintf(stderr, "DEBUG: live mutexes -1 => %u (recmutex--)\n",
130                     cc_debug_mtxcount);
131     }
132   }
133 
134   assert(recmutex != NULL);
135   cc_recmutex_struct_clean(recmutex);
136   free(recmutex);
137 }
138 
139 /*
140   Internal function used by cc_recmutex_lock() and cc_recmutex_try_lock().
141 */
recmutex_lock_internal(cc_recmutex * recmutex,int wait)142 static int recmutex_lock_internal(cc_recmutex * recmutex, int wait)
143 {
144   int level = -1; /* return -1 for recmutex_try_lock() if we couldn't get the mutex */
145   unsigned long id = cc_thread_id();
146 
147   assert(recmutex != NULL);
148   cc_mutex_lock(&recmutex->mutex);
149   if (recmutex->level == 0) {
150     recmutex->level++;
151     recmutex->threadid = id;
152     level = recmutex->level;
153   }
154   else if (id == recmutex->threadid) {
155     recmutex->level++;
156     level = recmutex->level;
157   }
158   else if (wait) {
159     recmutex->waiters++;
160     /* wait in loop, since some thread might snatch the mutex before
161        us when we receive a signal */
162     do {
163       cc_condvar_wait(&recmutex->condvar, &recmutex->mutex);
164     } while (recmutex->level > 0);
165 
166     assert(recmutex->level == 0);
167     recmutex->waiters--;
168     recmutex->threadid = id;
169     recmutex->level++;
170     level = recmutex->level;
171   }
172   cc_mutex_unlock(&recmutex->mutex);
173   return level;
174 }
175 
176 /*! Locks the recursive mutex \a recmutex. Returns the nesting level. */
177 
178 int
cc_recmutex_lock(cc_recmutex * recmutex)179 cc_recmutex_lock(cc_recmutex * recmutex)
180 {
181   return recmutex_lock_internal(recmutex, TRUE);
182 }
183 
184 /*! Attempts to lock the recursive mutex \a recmutex. Returns TRUE if
185     thread got the lock or already had the lock. */
186 int
cc_recmutex_try_lock(cc_recmutex * recmutex)187 cc_recmutex_try_lock(cc_recmutex * recmutex)
188 {
189   return recmutex_lock_internal(recmutex, FALSE) >= 0;
190 }
191 
192 /*! Unlocks the recursive mutex \a recmutex. Returns the nesting level
193     after unlocking. */
194 
195 int
cc_recmutex_unlock(cc_recmutex * recmutex)196 cc_recmutex_unlock(cc_recmutex * recmutex)
197 {
198   int level;
199   assert(recmutex != NULL);
200   assert(recmutex->threadid == cc_thread_id());
201   assert(recmutex->level > 0);
202   cc_mutex_lock(&recmutex->mutex);
203   recmutex->level--;
204   if (recmutex->level == 0 && recmutex->waiters) {
205     cc_condvar_wake_one(&recmutex->condvar);
206   }
207   level = recmutex->level;
208   cc_mutex_unlock(&recmutex->mutex);
209   return level;
210 }
211 
212 /*
213   internal functions
214 */
215 
216 static cc_recmutex * recmutex_field_lock;
217 static cc_recmutex * recmutex_notify_lock;
218 
219 static void
recmutex_cleanup(void)220 recmutex_cleanup(void)
221 {
222   cc_recmutex_destruct(recmutex_field_lock);
223   cc_recmutex_destruct(recmutex_notify_lock);
224 }
225 
226 void
cc_recmutex_init(void)227 cc_recmutex_init(void)
228 {
229   recmutex_field_lock = cc_recmutex_construct();
230   recmutex_notify_lock = cc_recmutex_construct();
231   /* atexit priority makes this callback trigger after normal cleanup
232      functions which might still use a recmutex instance */
233   coin_atexit((coin_atexit_f*) recmutex_cleanup, CC_ATEXIT_THREADING_SUBSYSTEM);
234 }
235 
236 int
cc_recmutex_internal_field_lock(void)237 cc_recmutex_internal_field_lock(void)
238 {
239   return cc_recmutex_lock(recmutex_field_lock);
240 }
241 
242 int
cc_recmutex_internal_field_unlock(void)243 cc_recmutex_internal_field_unlock(void)
244 {
245   return cc_recmutex_unlock(recmutex_field_lock);
246 }
247 
248 int
cc_recmutex_internal_notify_lock(void)249 cc_recmutex_internal_notify_lock(void)
250 {
251   return cc_recmutex_lock(recmutex_notify_lock);
252 }
253 
254 int
cc_recmutex_internal_notify_unlock(void)255 cc_recmutex_internal_notify_unlock(void)
256 {
257   return cc_recmutex_unlock(recmutex_notify_lock);
258 }
259