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 error.h */
34 #include <Inventor/C/errors/error.h>
35 
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif /* HAVE_CONFIG_H */
39 
40 #include <cstdio>
41 #include <cassert>
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h> /* STDERR_FILENO */
44 #endif /* HAVE_UNISTD_H */
45 
46 #ifdef COIN_THREADSAFE
47 #include <Inventor/C/threads/mutex.h>
48 #include "threads/mutexp.h"
49 #endif /* COIN_THREADSAFE */
50 
51 #include "coindefs.h"
52 #include "tidbitsp.h"
53 
54 /* ********************************************************************** */
55 
56 /*!
57   \struct cc_error error.h Inventor/C/errors/error.h
58   \typedef struct cc_error cc_error
59   \brief The cc_error type is an internal Coin structure for error management.
60 
61   \ingroup errors
62 
63   This is a Coin extension.
64 */
65 
66 /*!
67   \var cc_error::debugstring
68 
69   The error message.
70 */
71 
72 /*!
73   \typedef void cc_error_cb(const cc_error * err, void * data)
74 
75   The definition for an error callback handler.
76 */
77 
78 #ifdef __cplusplus
79 extern "C" {
80 #endif /* __cplusplus */
81 
82 #ifdef COIN_THREADSAFE
83 static cc_mutex * cc_error_mutex = NULL;
cc_error_mutex_cleanup(void)84 static void cc_error_mutex_cleanup(void) {
85   if (cc_error_mutex) {
86     cc_mutex_destruct(cc_error_mutex);
87     cc_error_mutex = NULL;
88   }
89 }
90 #endif /* COIN_THREADSAFE */
91 
92 /* FIXME: should be hidden from public API, and only visible to
93    subclasses. 20020526 mortene. */
94 /*!
95   \relates cc_error
96 */
97 
98 void
cc_error_default_handler_cb(const cc_error * err,void * COIN_UNUSED_ARG (data))99 cc_error_default_handler_cb(const cc_error * err, void * COIN_UNUSED_ARG(data))
100 {
101   /* It is not possible to "pass" C library data from the application
102      to a MSWin .DLL, so this is necessary to get hold of the stderr
103      FILE*. Just using fprintf(stderr, ...) or fprintf(stdout, ...)
104      directly will result in a crash when Coin has been compiled as a
105      .DLL. */
106   FILE * coin_stderr = coin_get_stderr();
107 
108   if (coin_stderr) {
109     const cc_string * str = cc_error_get_debug_string(err);
110     (void)fprintf(coin_stderr, "%s\n", cc_string_get_text(str));
111     (void)fflush(coin_stderr);
112   }
113 }
114 
115 static cc_error_cb * cc_error_callback = cc_error_default_handler_cb;
116 static void * cc_error_callback_data = NULL;
117 static SbBool cc_error_cleanup_function_set = FALSE;
118 
119 static void
cc_error_cleanup(void)120 cc_error_cleanup(void)
121 {
122   cc_error_callback = cc_error_default_handler_cb;
123   cc_error_callback_data = NULL;
124   cc_error_cleanup_function_set = FALSE;
125 }
126 
127 /*!
128   \relates cc_error
129 */
130 
131 void
cc_error_init(cc_error * me)132 cc_error_init(cc_error * me)
133 {
134   cc_string_construct(&(me->debugstring));
135 }
136 
137 /*!
138   \relates cc_error
139 */
140 
141 void
cc_error_clean(cc_error * me)142 cc_error_clean(cc_error * me)
143 {
144   cc_string_clean(&(me->debugstring));
145 }
146 
147 /*!
148   \relates cc_error
149 */
150 
151 void
cc_error_copy(const cc_error * src,cc_error * dst)152 cc_error_copy(const cc_error * src, cc_error * dst)
153 {
154   cc_string_set_string(&dst->debugstring, &src->debugstring);
155 }
156 
157 /*!
158   \relates cc_error
159 */
160 
161 void
cc_error_set_debug_string(cc_error * me,const char * str)162 cc_error_set_debug_string(cc_error * me, const char * str)
163 {
164   cc_string_set_text(&(me->debugstring), str);
165 }
166 
167 /*!
168   \relates cc_error
169 */
170 
171 void
cc_error_append_to_debug_string(cc_error * me,const char * str)172 cc_error_append_to_debug_string(cc_error * me, const char * str)
173 {
174   cc_string_append_text(&(me->debugstring), str);
175 }
176 
177 /*!
178   \relates cc_error
179 */
180 
181 void
cc_error_handle(cc_error * me)182 cc_error_handle(cc_error * me)
183 {
184   void * arg = NULL;
185 
186   cc_error_cb * function = cc_error_get_handler(&arg);
187   assert(function != NULL);
188 
189 #ifdef COIN_THREADSAFE
190   if (!cc_error_mutex) {
191     /* extra locking to avoid that two threads create the mutex */
192     /* FIXME: this is not smart, as it means that if the first call to
193        e.g. SoDebugError::post*() will hang if it happens within a
194        region where the global lock is already taken. 20050708 mortene.*/
195     cc_mutex_global_lock();
196     if (cc_error_mutex == NULL) {
197       cc_error_mutex = cc_mutex_construct();
198       coin_atexit(cc_error_mutex_cleanup, CC_ATEXIT_MSG_SUBSYSTEM);
199     }
200     cc_mutex_global_unlock();
201   }
202   cc_mutex_lock(cc_error_mutex);
203 #endif /* COIN_THREADSAFE */
204 
205   (*function)(me, arg);
206 
207 #ifdef COIN_THREADSAFE
208   cc_mutex_unlock(cc_error_mutex);
209 #endif /* COIN_THREADSAFE */
210 }
211 
212 /*!
213   \relates cc_error
214 */
215 
216 void
cc_error_set_handler_callback(cc_error_cb * func,void * data)217 cc_error_set_handler_callback(cc_error_cb * func, void * data)
218 {
219   cc_error_callback = func;
220   cc_error_callback_data = data;
221 
222   if (!cc_error_cleanup_function_set) {
223     coin_atexit((coin_atexit_f*) cc_error_cleanup, CC_ATEXIT_MSG_SUBSYSTEM);
224     cc_error_cleanup_function_set = TRUE;
225   }
226 }
227 
228 /*!
229   \relates cc_error
230 */
231 
232 cc_error_cb *
cc_error_get_handler_callback(void)233 cc_error_get_handler_callback(void)
234 {
235   return cc_error_callback;
236 }
237 
238 /*!
239   \relates cc_error
240 */
241 
242 void *
cc_error_get_handler_data(void)243 cc_error_get_handler_data(void)
244 {
245   return cc_error_callback_data;
246 }
247 
248 /*!
249   \relates cc_error
250 */
251 
252 cc_error_cb *
cc_error_get_handler(void ** data)253 cc_error_get_handler(void ** data)
254 {
255   *data = cc_error_callback_data;
256   return cc_error_callback;
257 }
258 
259 /*!
260   \relates cc_error
261 */
262 
263 const cc_string *
cc_error_get_debug_string(const cc_error * me)264 cc_error_get_debug_string(const cc_error * me)
265 {
266   return &(me->debugstring);
267 }
268 
269 /*!
270   \relates cc_error
271 */
272 
273 void
cc_error_post_arglist(const char * format,va_list args)274 cc_error_post_arglist(const char * format, va_list args)
275 {
276   cc_string s;
277   cc_error err;
278 
279   cc_string_construct(&s);
280 
281   cc_string_vsprintf(&s, format, args);
282 
283   cc_error_init(&err);
284   cc_error_set_debug_string(&err, cc_string_get_text(&s));
285   cc_error_handle(&err);
286   cc_error_clean(&err);
287 
288   cc_string_clean(&s);
289 }
290 
291 /*!
292   \relates cc_error
293 */
294 
295 void
cc_error_post(const char * format,...)296 cc_error_post(const char * format, ...)
297 {
298   va_list argptr;
299   va_start(argptr, format);
300   cc_error_post_arglist(format, argptr);
301   va_end(argptr);
302 }
303 
304 #ifdef __cplusplus
305 } /* extern "C" */
306 #endif /* __cplusplus */
307