--- ex.pod 2003/01/06 15:31:24 1.26
+++ ex.pod 2003/01/30 10:23:12 1.27
@@ -79,8 +79,8 @@
class of the exception thrower, I<object> identifies the particular
class instance of the exception thrower, and I<value> is the exceptional
return code value the thrower wants to communicate. All three parts are
-of type "C<void *>" internally, but every value is useable which can
-be lossless casted this type. Exceptions are created on-the-fly by the
+of type "C<void *>" internally, but every value is useable which can be
+lossless "casted" to this type. Exceptions are created on-the-fly by the
B<ex_throw> command.
=head2 APPLICATION PROGRAMMER INTERFACE (API)
@@ -101,19 +101,20 @@
=item C<void *>I<ex_class>
This is the I<class> argument of the B<ex_throw> call which created
-the exception. This should globally and uniquely identify the class of
-I<ex_value>. Usually this is a pointer to a static object (variable,
-structure or function) which identifies the class of the thrower and
-allows the catcher to correctly handle I<ex_value>.
+the exception. This can globally and uniquely identify the class to
+which I<ex_value> belongs to . Usually this is a pointer to a static
+object (variable, structure or function) which identifies the class of
+the thrower and allows the catcher to correctly handle I<ex_value>. It
+is usually just an additional (optional) information to I<ex_value>.
=item C<void *>I<ex_object>
-This is the I<object> argument of the B<ex_throw> call which created
-the exception. This should globally and uniquely identify the class
-instance of I<ex_value> (in case multiple instances exists at all).
+This is the I<object> argument of the B<ex_throw> call which created the
+exception. This can globally and uniquely identify the class instance
+I<ex_value> belongs to (in case multiple instances exists at all).
Usually this a pointer to a dynamic object (structure) which identifiers
the particular instance of the thrower. It is usually just an additional
-information to I<ex_value>.
+(optional) information to I<ex_value>.
=item C<void *>I<ex_value>
@@ -127,22 +128,23 @@
=item C<char *>I<ex_file>
This is the file name of the B<ISO-C> source where the B<ex_throw> call
-was performed. It is provided as an additional information about the
-throw point and is intended mainly for tracing and debugging purposes.
+was performed. It is automatically provided as an additional information
+about the throw point and is intended mainly for tracing and debugging
+purposes.
=item C<int> I<ex_line>
-This is the line number inside the B<ISO-C> source file name where
-the B<ex_throw> call was performed. It is provided as an additional
-information about the throw point and is intended mainly for tracing and
-debugging purposes.
+This is the line number inside the B<ISO-C> source file name where the
+B<ex_throw> call was performed. It is automatically provided as an
+additional information about the throw point and is intended mainly for
+tracing and debugging purposes.
=item C<char *>I<ex_func>
This is the function name (if determinable, else "C<#NA#>") inside the
B<ISO-C> source file name where the B<ex_throw> call was performed. It
-is provided as an additional information about the throw point and is
-intended mainly for tracing and debugging purposes.
+is automatically provided as an additional information about the throw
+point and is intended mainly for tracing and debugging purposes.
=back
@@ -174,8 +176,8 @@
B<ex_catch> clauses, of course.
The B<ex_try> block is a regular B<ISO-C> language statement block, but
-it is not allowed to jump into it via C<goto> or C<longjmp>(3) or out
-of it via C<break>, C<return>, C<goto> or C<longjmp>(3) because there
+it is not allowed to jump into it via "C<goto>" or C<longjmp>(3) or out
+of it via "C<break>", "C<return>", "C<goto>" or C<longjmp>(3) because there
is some hidden setup and cleanup that needs to be done by B<OSSP ex>
regardless of whether an exception is caught. Jumping into an B<ex_try>
clause would avoid doing the setup, and jumping out of it would avoid
@@ -191,7 +193,7 @@
B<ex_try> blocks: all accessible B<ISO-C> objects have the (expected)
values as of the time B<ex_throw> was called, except that the values
of objects of automatic storage invocation duration that do not have
-the C<volatile> storage class I<and> have been changed between the
+the "C<volatile>" storage class I<and> have been changed between the
B<ex_try> invocation and B<ex_throw> are indeterminate. This is because
both you usually do not know which commands in the B<ex_try> were
already successful before the exception was thrown (logically speaking)
@@ -202,11 +204,12 @@
This builds an exception from the supplied arguments and throws it.
If an B<ex_try>/B<ex_catch> clause formed the dynamic scope of the
-B<ex_throw> call, this exception is copied into the I<variable> of its
-B<ex_catch> clause and the program control flow is continued in the
-B<ex_catch> block. If no B<ex_try>/B<ex_catch> clause exists in the
-dynamic scope of the B<ex_throw> call, the program C<abort>(3)s. The
-B<ex_throw> can be performed everywhere, including inside B<ex_try> and
+B<ex_throw> call, this exception is copied into the I<variable> of
+its B<ex_catch> clause and the program control flow is continued in
+the (optional B<ex_cleanup> and then in the) B<ex_catch> block. If
+no B<ex_try>/B<ex_catch> clause exists in the dynamic scope of the
+B<ex_throw> call, the program calls C<abort>(3). The B<ex_throw> can
+be performed everywhere, including inside B<ex_try>, B<ex_cleanup> and
B<ex_catch> blocks.
=item B<ex_rethrow>;
@@ -222,50 +225,51 @@
This directive executes I<BLOCK> while deferring the throwing of
exceptions, i.e., inside the dynamic scope of B<ex_defer> all
-B<ex_throw> operations are silently ignored and on leaving the I<BLOCK>
-the first occurred exception is thrown.
-
-The B<ex_defer> block is a regular B<ISO-C> language statement block,
-but it is not allowed to jump into it via C<goto> or C<longjmp>(3) or
-out of it via C<break>, C<return>, C<goto> or C<longjmp>(3) because this
-would cause the deferral level to become out of sync. Jumping into
-an B<ex_defer> clause would avoid increasing the exception deferral
-level, and jumping out of it would avoid decreasing it. In both cases
-the result is an incorrect exception deferral level. Nevertheless you
-are allowed to nest B<ex_defer> clauses.
+B<ex_throw> operations are remembered but deferred and on leaving the
+I<BLOCK> the I<first> occurred exception is thrown. The second and
+subsequent exceptions are ignored.
+
+The B<ex_defer> block I<BLOCK> is a regular B<ISO-C> language statement
+block, but it is not allowed to jump into it via "C<goto>" or
+C<longjmp>(3) or out of it via "C<break>", "C<return>", "C<goto>" or
+C<longjmp>(3) because this would cause the deferral level to become out
+of sync. Jumping into an B<ex_defer> clause would avoid increasing the
+exception deferral level, and jumping out of it would avoid decreasing
+it. In both cases the result is an incorrect exception deferral level.
+Nevertheless you are allowed to nest B<ex_defer> clauses.
=item B<ex_shield> I<BLOCK>
This directive executes I<BLOCK> while shielding it against the throwing
of exceptions, i.e., inside the dynamic scope of B<ex_shield> all
-B<ex_throw> operations are silently ignored.
+B<ex_throw> operations are just silently ignored.
The B<ex_shield> block is a regular B<ISO-C> language statement block,
-but it is not allowed to jump into it via C<goto> or C<longjmp>(3) or
-out of it via C<break>, C<return>, C<goto> or C<longjmp>(3) because this
-would cause the shielding level to become out of sync. Jumping into
-an B<ex_shield> clause would avoid increasing the exception shielding
-level, and jumping out of it would avoid decreasing it. In both cases
-the result is an incorrect exception shielding level. Nevertheless you
-are allowed to nest B<ex_shield> clauses.
+but it is not allowed to jump into it via "C<goto>" or C<longjmp>(3)
+or out of it via "C<break>", "C<return>", "C<goto>" or C<longjmp>(3)
+because this would cause the shielding level to become out of sync.
+Jumping into an B<ex_shield> clause would avoid increasing the exception
+shielding level, and jumping out of it would avoid decreasing it.
+In both cases the result is an incorrect exception shielding level.
+Nevertheless you are allowed to nest B<ex_shield> clauses.
=item B<ex_catching>
This is a boolean flag which can be checked inside the dynamic scope
of an B<ex_try> clause to test whether the current scope is exception
-catching.
+catching (see B<ex_try>/B<ex_catch> clause).
=item B<ex_deferred>
This is a boolean flag which can be checked inside the dynamic scope of
an B<ex_defer> clause to test whether the current scope is exception
-deferring.
+deferring (see B<ex_defer> clause).
=item B<ex_shielding>
This is a boolean flag which can be checked inside the dynamic scope of
an B<ex_shield> clause to test whether the current scope is exception
-shielding.
+shielding (see B<ex_shield> clause).
=back
@@ -311,7 +315,7 @@
=back
-The default implementation (define C<__EX_MCTX_SJLJ__> or as long
+The default implementation (define C<__EX_MCTX_SJLJ__> or as long as
C<__EX_MCTX_CUSTOM__> is not defined) uses the B<ISO-C> jmp_buf(3)
facility:
@@ -334,8 +338,9 @@
By default, B<__ex_ctx> (which is B<__ex_ctx_default> as provided by
F<libex>) returns a pointer to a static C<ex_ctx_t> context. For use in
-multi-threading environments, this should be overwritten with a callback
-function returning a per-thread context structure.
+multi-threading environments, this should be overwritten with a
+callback function returning a per-thread context structure (see section
+B<MULTITHREADING ENVIRONMENTS> below).
To initialize an exception context structure there are two macros
defined: "B<EX_CTX_INITIALIZER>" for static initialization and
@@ -347,15 +352,17 @@
In case there is an exception thrown which is not caught by any
B<ex_try>/B<ex_catch> clauses, B<OSSP ex> calls the callback "C<void
(*>B<__ex_terminate>C<)(ex_t *)>". It receives a pointer to the
-exception object which should have been thrown.
+exception object which was thrown.
By default, B<__ex_terminate> (which is B<__ex_terminate_default>
as provided by F<libex>) prints a message of the form "C<**EX:
UNCAUGHT EXCEPTION: class=0xXXXXXXXX object=0xXXXXXXXX value=0xXXXXXXX
[xxxx:NNN:xxxx]>" to F<stderr> and then calls abort(3) in order to
terminate the application. For use in multi-threading environments, this
-should be overwritten with a callback function which terminates only the
-current thread.
+should be overwritten with a callback function which terminates only
+the current thread. Even better, a real application always should have
+a top-level B<ex_try>/B<ex_catch> clause in its "C<main()>" in order to
+more gracefully terminate the application.
=head2 Namespace Mapping
@@ -395,7 +402,7 @@
strcpy(cp1, "foo");
strcpy(cp2, "bar");
}
- ex_clean {
+ ex_cleanup {
if (cp3 != NULL) free(cp3);
if (cp2 != NULL) free(cp2);
if (cp1 != NULL) free(cp1);
@@ -411,59 +418,59 @@
=item B<01: variable scope>
-Variables which are used in the B<ex_clean> or B<ex_catch> clauses
+Variables which are used in the B<ex_cleanup> or B<ex_catch> clauses
must be declared before the B<ex_try> clause, otherwise they only
exist inside the B<ex_try> block. In the example above, C<cp1>, C<cp2>
and C<cp3> are automatic variables and only exist in the block of the
-B<ex_try> clause, the code in the B<ex_clean> and B<ex_catch> clauses
+B<ex_try> clause, the code in the B<ex_cleanup> and B<ex_catch> clauses
does not know anything about them.
=item B<02: variable initialization>
-Variables which are used in the B<ex_clean> or B<ex_catch> clauses must
+Variables which are used in the B<ex_cleanup> or B<ex_catch> clauses must
be initialized before the point of the first possible B<ex_throw> is
-reached. In the example above, B<ex_clean> would have trouble using
+reached. In the example above, B<ex_cleanup> would have trouble using
C<cp3> if mallocex() throws a exception when allocating a C<TOOBIG>
buffer.
=item B<03: volatile variables>
-Variables which are used in the B<ex_clean> or B<ex_catch> clauses
-must be declared with the storage class C<volatile>, otherwise they
+Variables which are used in the B<ex_cleanup> or B<ex_catch> clauses
+must be declared with the storage class "C<volatile>", otherwise they
might contain outdated information if B<ex_throw> throws an exception.
If using a "free if unset" approach like the example does in the
-B<ex_clean> clause, the variables must be initialized (see B<02>) I<and>
+B<ex_cleanup> clause, the variables must be initialized (see B<02>) I<and>
remain valid upon use.
=item B<04: clean before catch>
-The B<ex_clean> clause is not only written down before the B<ex_catch>
+The B<ex_cleanup> clause is not only written down before the B<ex_catch>
clause, it is also evaluated before the B<ex_catch> clause. So,
resources being cleaned up must no longer be used in the B<ex_catch>
block. The example above would have trouble referencing the character
-strings in the printf(3) statement because these have been free(3)'d
+strings in the printf(3) statement because these have been freed
before.
=item B<05: variable uninitialization>
If resources are passed away and out of the scope of the
-B<ex_try>/B<ex_clean>/B<ex_catch> construct and the variables were
+B<ex_try>/B<ex_cleanup>/B<ex_catch> construct and the variables were
initialized for using a "free if unset" approach then they must be
uninitialized after being passed away. The example above would free(3)
-C<cp1> in the B<ex_clean> clause if mallocex() throws an exception if
+C<cp1> in the B<ex_cleanup> clause if mallocex() throws an exception if
allocating a C<TOOBIG> buffer. The C<globalcontext->first> pointer
hence becomes invalid.
=back
-As following is fixed version of the code (annotated with the pitfall
+The following is fixed version of the code (annotated with the pitfall
items for reference):
/* GOOD EXAMPLE */
{ /*01*/
- volatile /*03*/ char *cp1 = NULL /*02*/;
- volatile /*03*/ char *cp2 = NULL /*02*/;
- volatile /*03*/ char *cp3 = NULL /*02*/;
+ char * volatile /*03*/ cp1 = NULL /*02*/;
+ char * volatile /*03*/ cp2 = NULL /*02*/;
+ char * volatile /*03*/ cp3 = NULL /*02*/;
try {
cp1 = mallocex(SMALLAMOUNT);
globalcontext->first = cp1;
@@ -473,8 +480,8 @@
strcpy(cp1, "foo");
strcpy(cp2, "bar");
}
- clean {
- /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
+ clean { /*04*/
+ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
if (cp3 != NULL)
free(cp3);
if (cp2 != NULL)
@@ -487,13 +494,13 @@
}
}
-An alternative fixed version could also be:
+Alternatively, this could also be used:
- /* ALTERNATE *05* GOOD EXAMPLE */
+ /* ALTERNATIVE GOOD EXAMPLE */
{ /*01*/
- volatile /*03*/ char *cp1 = NULL /*02*/;
- volatile /*03*/ char *cp2 = NULL /*02*/;
- volatile /*03*/ char *cp3 = NULL /*02*/;
+ char * volatile /*03*/ cp1 = NULL /*02*/;
+ char * volatile /*03*/ cp2 = NULL /*02*/;
+ char * volatile /*03*/ cp3 = NULL /*02*/;
try {
cp1 = mallocex(SMALLAMOUNT);
globalcontext->first = cp1;
@@ -503,8 +510,8 @@
strcpy(cp1, "foo");
strcpy(cp2, "bar");
}
- clean {
- /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
+ clean { /*04*/
+ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
if (cp3 != NULL)
free(cp3);
if (cp2 != NULL)
@@ -518,7 +525,6 @@
}
}
-
=head1 MULTITHREADING ENVIRONMENTS
B<OSSP ex> is designed to work both in single-threading and
@@ -534,12 +540,12 @@
=head2 GNU pth
Using B<OSSP ex> together with B<GNU pth> is straight-forward, because
-B<GNU pth> 1.5 (and higher) already has support for B<OSSP ex> built-in.
+B<GNU pth> 2.0 (and higher) already has support for B<OSSP ex> built-in.
All which is needed is that B<GNU pth> is configured with the B<GNU
Autoconf> option C<--with-ex>. Then each B<GNU pth> user-space thread
-has its own B<OSSP ex> exception context automatically. The default
-of using B<ISO-C> jmp_buf(3) does not conflict with the thread
-dispatching mechanisms used by B<GNU pth>.
+has its own B<OSSP ex> exception context automatically. The default of
+using B<ISO-C> jmp_buf(3) does not conflict with the thread dispatching
+mechanisms used by B<GNU pth>.
=head2 POSIX pthreads
@@ -550,7 +556,7 @@
terminates only the current thread. To get an impression, a small
utility library for this follows:
-=over 4
+=over 2
=item F<pthread_ex.h>
@@ -560,7 +566,8 @@
#include <pthread.h>
int pthread_init_ex (void);
- int pthread_create_ex (pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);
+ int pthread_create_ex (pthread_t *, const pthread_attr_t *,
+ void *(*)(void *), void *);
#ifndef PTHREAD_EX_INTERNAL
#define pthread_init pthread_init_ex
@@ -592,7 +599,8 @@
/* callback: context fetching */
static ex_ctx_t *pthread_ex_ctx(void)
{
- return (ex_ctx_t *)pthread_getspecific(pthread_ex_ctx_key);
+ return (ex_ctx_t *)
+ pthread_getspecific(pthread_ex_ctx_key);
}
/* callback: termination */
@@ -606,8 +614,10 @@
{
int rc;
- /* additionally create thread data key and override OSSP ex callbacks */
- pthread_key_create(&pthread_ex_ctx_key, pthread_ex_ctx_destroy);
+ /* additionally create thread data key
+ and override OSSP ex callbacks */
+ pthread_key_create(&pthread_ex_ctx_key,
+ pthread_ex_ctx_destroy);
__ex_ctx = pthread_ex_ctx;
__ex_terminate = pthread_ex_terminate;
@@ -637,14 +647,18 @@
}
/* pthread_create() wrapper */
- int pthread_create_ex(pthread_t *thread, const pthread_attr_t *attr, void *(*entry)(void *), void *arg)
+ int pthread_create_ex(pthread_t *thread,
+ const pthread_attr_t *attr,
+ void *(*entry)(void *), void *arg)
{
pthread_create_ex_t wrapper;
- /* spawn thread but execute start function through wrapper */
+ /* spawn thread but execute start
+ function through wrapper */
wrapper.entry = entry;
wrapper.arg = arg;
- return pthread_create(thread, attr, pthread_create_wrapper, &wrapper);
+ return pthread_create(thread, attr,
+ pthread_create_wrapper, &wrapper);
}
=back
@@ -748,7 +762,7 @@
You can achieve this very easily by changing the library as following:
-=over 4
+=over 2
=item F<foo.h>
@@ -832,7 +846,7 @@
and Cosmin Truta E<lt>cosmin@cs.toronto.eduE<gt>.
The B<cleanup> clause was inspired by the B<Java> C<finally> clause.
-The B<shield> feature was inspired by an C<errno> shielding facility
+The B<shield> feature was inspired by an "C<errno>" shielding facility
used in the B<GNU pth> implementation. The B<defer> feature was invented
to simplify an application's cleanup handling if multiple independent
resources are allocated and have to be freed on error.
|