2 * Portable Exception Handling for ANSI C.
3 * Copyright (C) 1999 Kaz Kylheku <kaz@ashi.footprints.net>
5 * Free Software License:
7 * All rights are reserved by the author, with the following exceptions:
8 * Permission is granted to freely reproduce and distribute this software,
9 * possibly in exchange for a fee, provided that this copyright notice appears
10 * intact. Permission is also granted to adapt this software to produce
11 * derivative works, as long as the modified versions carry this copyright
12 * notice and additional notices stating that the work has been modified.
13 * This source code may be translated into executable form and incorporated
14 * into proprietary software; there is no requirement for such software to
15 * contain a copyright notice related to this source.
19 * Modified to support throwing an exception with a null message pointer,
20 * and to have the message not be const (as we generate messages with
21 * "ws_strdup_printf()", which means they need to be freed; using
22 * a null message means that we don't have to use a special string
23 * for exceptions with no message, and don't have to worry about
41 #include "exceptions.h"
44 #define XCEPT_BUFFER_SIZE 1024
46 #ifdef KAZLIB_POSIX_THREADS
50 static pthread_mutex_t init_mtx
= PTHREAD_MUTEX_INITIALIZER
;
51 static int init_counter
;
52 static pthread_key_t top_key
;
53 static pthread_key_t uh_key
;
54 static pthread_key_t alloc_key
;
55 static pthread_key_t dealloc_key
;
56 static void unhandled_catcher(except_t
*);
58 #define get_top() ((struct except_stacknode *) pthread_getspecific(top_key))
59 #define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0))
60 #define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0))
61 #define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0))
62 #define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0))
64 static void (*get_catcher(void))(except_t
*)
66 void (*catcher
)(except_t
*) = (void (*)(except_t
*)) pthread_getspecific(uh_key
);
67 return (catcher
== 0) ? unhandled_catcher
: catcher
;
70 static void *(*get_alloc(void))(size_t)
72 void *(*alloc
)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key
);
73 return (alloc
== 0) ? malloc
: alloc
;
76 static void (*get_dealloc(void))(void *)
78 void (*dealloc
)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key
);
79 return (dealloc
== 0) ? free
: dealloc
;
86 pthread_mutex_lock(&init_mtx
);
88 assert (init_counter
< INT_MAX
);
90 if (init_counter
++ == 0) {
91 int top_ok
= (pthread_key_create(&top_key
, 0) == 0);
92 int uh_ok
= (pthread_key_create(&uh_key
, 0) == 0);
93 int alloc_ok
= (pthread_key_create(&alloc_key
, 0) == 0);
94 int dealloc_ok
= (pthread_key_create(&dealloc_key
, 0) == 0);
96 if (!top_ok
|| !uh_ok
|| !alloc_ok
|| !dealloc_ok
) {
100 pthread_key_delete(top_key
);
102 pthread_key_delete(uh_key
);
104 pthread_key_delete(alloc_key
);
106 pthread_key_delete(dealloc_key
);
110 pthread_mutex_unlock(&init_mtx
);
115 void except_deinit(void)
117 pthread_mutex_lock(&init_mtx
);
119 assert (init_counter
> 0);
121 if (--init_counter
== 0) {
122 pthread_key_delete(top_key
);
123 pthread_key_delete(uh_key
);
124 pthread_key_delete(alloc_key
);
125 pthread_key_delete(dealloc_key
);
128 pthread_mutex_unlock(&init_mtx
);
131 #else /* not using POSIX thread support */
134 * We make the catcher stack per-thread, because we must.
136 * We don't make the unhandled-exception-catcher, the allocator, or the
137 * deallocator thread-specific, as we don't need to.
139 * We don't protext the init level with a mutex, as we only initialize
140 * it and de-initialize it once.
142 static int init_counter
;
143 static void unhandled_catcher(except_t
*);
144 static void (*uh_catcher_ptr
)(except_t
*) = unhandled_catcher
;
145 /* We need this 'size_t' cast due to a glitch in GLib where g_malloc was prototyped
146 * as 'void *g_malloc (unsigned long n_bytes)'. This was later fixed to the correct prototype
147 * 'void *g_malloc (size_t n_bytes)'. In Wireshark we use the latter prototype
148 * throughout the code. We can get away with this even with older versions of GLib by
149 * adding a '(void *(*)(size_t))' cast whenever we refer to g_malloc. The only platform
150 * supported by Wireshark where this isn't safe (sizeof size_t != sizeof unsigned long) is Win64.
151 * However, we _always_ bundle the newest version of GLib on this platform so
152 * the size_t issue doesn't exists here. Pheew.. */
153 static void *(*allocator
)(size_t) = (void *(*)(size_t)) g_malloc
;
154 static void (*deallocator
)(void *) = g_free
;
155 static WS_THREAD_LOCAL
struct except_stacknode
*stack_top
;
157 #define get_top() (stack_top)
158 #define set_top(T) (stack_top = (T))
159 #define get_catcher() (uh_catcher_ptr)
160 #define set_catcher(C) (uh_catcher_ptr = (C))
161 #define get_alloc() (allocator)
162 #define set_alloc(A) (allocator = (A))
163 #define get_dealloc() (deallocator)
164 #define set_dealloc(D) (deallocator = (D))
166 int except_init(void)
168 assert (init_counter
< INT_MAX
);
173 void except_deinit(void)
175 assert (init_counter
> 0);
182 static int match(const volatile except_id_t
*thrown
, const except_id_t
*caught
)
184 int group_match
= (caught
->except_group
== XCEPT_GROUP_ANY
||
185 caught
->except_group
== thrown
->except_group
);
186 int code_match
= (caught
->except_code
== XCEPT_CODE_ANY
||
187 caught
->except_code
== thrown
->except_code
);
189 return group_match
&& code_match
;
192 WS_NORETURN
static void do_throw(except_t
*except
)
194 struct except_stacknode
*top
;
196 assert (except
->except_id
.except_group
!= 0 &&
197 except
->except_id
.except_code
!= 0);
199 for (top
= get_top(); top
!= 0; top
= top
->except_down
) {
200 if (top
->except_type
== XCEPT_CLEANUP
) {
201 top
->except_info
.except_cleanup
->except_func(top
->except_info
.except_cleanup
->except_context
);
203 struct except_catch
*catcher
= top
->except_info
.except_catcher
;
204 const except_id_t
*pi
= catcher
->except_id
;
207 assert (top
->except_type
== XCEPT_CATCHER
);
208 except_free(catcher
->except_obj
.except_dyndata
);
210 for (i
= 0; i
< catcher
->except_size
; pi
++, i
++) {
211 if (match(&except
->except_id
, pi
)) {
212 catcher
->except_obj
= *except
;
214 longjmp(catcher
->except_jmp
, 1);
221 get_catcher()(except
); /* unhandled exception */
225 static void unhandled_catcher(except_t
*except
)
227 if (except
->except_message
== NULL
) {
228 fprintf(stderr
, "Unhandled exception (group=%lu, code=%lu)\n",
229 except
->except_id
.except_group
,
230 except
->except_id
.except_code
);
232 fprintf(stderr
, "Unhandled exception (\"%s\", group=%lu, code=%lu)\n",
233 except
->except_message
, except
->except_id
.except_group
,
234 except
->except_id
.except_code
);
239 static void stack_push(struct except_stacknode
*node
)
241 node
->except_down
= get_top();
245 void except_setup_clean(struct except_stacknode
*esn
,
246 struct except_cleanup
*ecl
, void (*cleanf
)(void *), void *context
)
248 esn
->except_type
= XCEPT_CLEANUP
;
249 ecl
->except_func
= cleanf
;
250 ecl
->except_context
= context
;
251 esn
->except_info
.except_cleanup
= ecl
;
255 void except_setup_try(struct except_stacknode
*esn
,
256 struct except_catch
*ech
, const except_id_t id
[], size_t size
)
259 ech
->except_size
= size
;
260 ech
->except_obj
.except_dyndata
= 0;
261 esn
->except_type
= XCEPT_CATCHER
;
262 esn
->except_info
.except_catcher
= ech
;
266 struct except_stacknode
*except_pop(void)
268 struct except_stacknode
*top
= get_top();
269 assert (top
->except_type
== XCEPT_CLEANUP
|| top
->except_type
== XCEPT_CATCHER
);
270 set_top(top
->except_down
);
274 WS_NORETURN
void except_rethrow(except_t
*except
)
276 struct except_stacknode
*top
= get_top();
278 assert (top
->except_type
== XCEPT_CATCHER
);
279 assert (&top
->except_info
.except_catcher
->except_obj
== except
);
280 set_top(top
->except_down
);
284 WS_NORETURN
void except_throw(long group
, long code
, const char *msg
)
288 except
.except_id
.except_group
= group
;
289 except
.except_id
.except_code
= code
;
290 except
.except_message
= msg
;
291 except
.except_dyndata
= 0;
294 if (code
== DissectorError
&& IsDebuggerPresent()) {
302 WS_NORETURN
void except_throwd(long group
, long code
, const char *msg
, void *data
)
306 except
.except_id
.except_group
= group
;
307 except
.except_id
.except_code
= code
;
308 except
.except_message
= msg
;
309 except
.except_dyndata
= data
;
315 * XXX - should we use ws_strdup_printf() here, so we're not limited by
316 * XCEPT_BUFFER_SIZE? We could then just use this to generate formatted
319 WS_NORETURN
void except_vthrowf(long group
, long code
, const char *fmt
,
322 char *buf
= (char *)except_alloc(XCEPT_BUFFER_SIZE
);
324 vsnprintf(buf
, XCEPT_BUFFER_SIZE
, fmt
, vl
);
325 except_throwd(group
, code
, buf
, buf
);
328 WS_NORETURN
void except_throwf(long group
, long code
, const char *fmt
, ...)
333 except_vthrowf(group
, code
, fmt
, vl
);
337 void (*except_unhandled_catcher(void (*new_catcher
)(except_t
*)))(except_t
*)
339 void (*old_catcher
)(except_t
*) = get_catcher();
340 set_catcher(new_catcher
);
346 #undef except_message
349 unsigned long except_code(except_t
*ex
)
351 return ex
->except_id
.except_code
;
354 unsigned long except_group(except_t
*ex
)
356 return ex
->except_id
.except_group
;
359 const char *except_message(except_t
*ex
)
361 return ex
->except_message
;
364 void *except_data(except_t
*ex
)
366 return ex
->except_dyndata
;
369 void *except_take_data(except_t
*ex
)
371 void *data
= ex
->except_dyndata
;
372 ex
->except_dyndata
= 0;
376 void except_set_allocator(void *(*alloc
)(size_t), void (*dealloc
)(void *))
379 set_dealloc(dealloc
);
382 void *except_alloc(size_t size
)
384 void *ptr
= get_alloc()(size
);
387 except_throw(XCEPT_BAD_ALLOC
, 0, "out of memory");
391 void except_free(void *ptr
)
396 #ifdef KAZLIB_TEST_MAIN
399 static void cleanup(void *arg
)
401 printf("cleanup(\"%s\") called\n", (char *) arg
);
404 static void bottom_level(void)
407 printf("throw exception? "); fflush(stdout
);
408 fgets(buf
, sizeof buf
, stdin
);
410 if (buf
[0] >= 0 && (buf
[0] == 'Y' || buf
[0] == 'y'))
411 except_throw(1, 1, "nasty exception");
414 static void top_level(void)
416 except_cleanup_push(cleanup
, "argument");
418 except_cleanup_pop(0);
421 int main(int argc
, char **argv
)
423 static const except_id_t
catch[] = { { 1, 1 }, { 1, 2 } };
428 * Nested exception ``try blocks''
432 except_try_push(catch, 2, &ex
);
435 except_try_push(catch, 2, &ex
);
440 msg
= except_message(ex
);
442 printf("caught exception (inner): s=%lu, c=%lu\n",
443 except_group(ex
), except_code(ex
));
445 printf("caught exception (inner): \"%s\", s=%lu, c=%lu\n",
446 msg
, except_group(ex
), except_code(ex
));
453 msg
= except_message(ex
);
455 printf("caught exception (outer): s=%lu, c=%lu\n",
456 except_group(ex
), except_code(ex
));
458 printf("caught exception (outer): \"%s\", s=%lu, c=%lu\n",
459 except_message(ex
), except_group(ex
), except_code(ex
));
463 except_throw(99, 99, "exception in main");
468 #endif /* KAZLIB_TEST_MAIN */
471 * Editor modelines - https://www.wireshark.org/tools/modelines.html
476 * indent-tabs-mode: nil
479 * vi: set shiftwidth=4 tabstop=8 expandtab:
480 * :indentSize=4:tabSize=8:noTabs=true: