@@ -66,21 +66,22 @@ extern "C" {
6666 * 1) Add the *_PROTECT macros to your C code as required to protect
6767 * dangerous floating point sections.
6868 *
69- * 2) Turn on the inclusion of the code by #defining WANT_SIGFPE_HANDLER in
70- * config.h.in before you configure, compile, and install Python, and the
71- * fpectl module, and any other modules which may have conditional code.
69+ * 2) Turn on the inclusion of the code by adding the ``--with-fpectl''
70+ * flag at the time you run configure. If the fpectl or other modules
71+ * which use the *_PROTECT macros are to be dynamically loaded, be
72+ * sure they are compiled with WANT_SIGFPE_HANDLER defined.
7273 *
7374 * 3) When python is built and running, import fpectl, and execute
7475 * fpectl.turnon_sigfpe(). This sets up the signal handler and enables
7576 * generation of SIGFPE whenever an exception occurs. From this point
7677 * on, any properly trapped SIGFPE should result in the Python
7778 * FloatingPointError exception.
7879 *
79- * Step 1 has been done already for the Python kernel code, and will be
80- * done soon for Hugunin's NumPy array package and my Gist graphics module.
81- * Step 2 is usually done once at python install time. Python's behavior
82- * with respect to SIGFPE is not changed unless you also do step 3. Thus
83- * you can control this new facility at compile time, or run time, or both.
80+ * Step 1 has been done already for the Python kernel code, and should be
81+ * done soon for the NumPy array package. Step 2 is usually done once at
82+ * python install time. Python's behavior with respect to SIGFPE is not
83+ * changed unless you also do step 3. Thus you can control this new
84+ * facility at compile time, or run time, or both.
8485 *
8586 ********************************
8687 * Using the macros in your code:
@@ -89,17 +90,16 @@ extern "C" {
8990 * {
9091 * ....
9192 * PyFPE_START_PROTECT("Error in foobar", return 0)
92- * dangerous_op(somearg1, somearg2, ...);
93- * PyFPE_END_PROTECT
93+ * result = dangerous_op(somearg1, somearg2, ...);
94+ * PyFPE_END_PROTECT(result)
9495 * ....
9596 * }
9697 *
97- * If a floating point error occurs in dangerous_op, foobar returns 0
98- * (NULL), after setting the associated value of the FloatingPointError
99- * exception to "Error in foobar". ``Dangerous_op'' can be a single
100- * operation, or a block, or function calls, or any combination, so long as
101- * no alternate return is possible before the PyFPE_END_PROTECT macro is
102- * reached.
98+ * If a floating point error occurs in dangerous_op, foobar returns 0 (NULL),
99+ * after setting the associated value of the FloatingPointError exception to
100+ * "Error in foobar". ``Dangerous_op'' can be a single operation, or a block
101+ * of code, function calls, or any combination, so long as no alternate
102+ * return is possible before the PyFPE_END_PROTECT macro is reached.
103103 *
104104 * The macros can only be used in a function context where an error return
105105 * can be recognized as signaling a Python exception. (Generally, most
@@ -121,7 +121,7 @@ extern "C" {
121121 * I therefore decided on a more limited form of nesting, using a counter
122122 * variable (PyFPE_counter) to keep track of any recursion. If an exception
123123 * occurs in an ``inner'' pair of macros, the return will apparently
124- * come from the top level.
124+ * come from the outermost level.
125125 *
126126 */
127127
@@ -131,23 +131,35 @@ extern "C" {
131131#include <math.h>
132132extern jmp_buf PyFPE_jbuf ;
133133extern int PyFPE_counter ;
134- extern double PyFPE_dummy ();
134+ extern double PyFPE_dummy (void * );
135135
136136#define PyFPE_START_PROTECT (err_string , leave_stmt ) \
137137if (!PyFPE_counter++ && setjmp(PyFPE_jbuf)) { \
138- PyFPE_counter = 0; \
139138 PyErr_SetString(PyExc_FloatingPointError, err_string); \
139+ PyFPE_counter = 0; \
140140 leave_stmt; \
141141}
142142
143143/*
144144 * This (following) is a heck of a way to decrement a counter. However,
145- * code optimizers will sometimes move this statement so that it gets
146- * executed *before* the unsafe expression which we're trying to protect.
147- * This pretty well messes things up, of course. So the best I've been able
148- * to do is to put a (hopefully fast) function call into the expression
149- * which counts down PyFPE_counter, and thereby monkey wrench the overeager
150- * optimizer. Better solutions are welcomed....
145+ * unless the macro argument is provided, code optimizers will sometimes move
146+ * this statement so that it gets executed *before* the unsafe expression
147+ * which we're trying to protect. That pretty well messes things up,
148+ * of course.
149+ *
150+ * If the expression(s) you're trying to protect don't happen to return a
151+ * value, you will need to manufacture a dummy result just to preserve the
152+ * correct ordering of statements. Note that the macro passes the address
153+ * of its argument (so you need to give it something which is addressable).
154+ * If your expression returns multiple results, pass the last such result
155+ * to PyFPE_END_PROTECT.
156+ *
157+ * Note that PyFPE_dummy returns a double, which is cast to int.
158+ * This seeming insanity is to tickle the Floating Point Unit (FPU).
159+ * If an exception has occurred in a preceding floating point operation,
160+ * some architectures (notably Intel 80x86) will not deliver the interrupt
161+ * until the *next* floating point operation. This is painful if you've
162+ * already decremented PyFPE_counter.
151163 */
152164#define PyFPE_END_PROTECT (v ) PyFPE_counter -= (int)PyFPE_dummy(&(v));
153165
0 commit comments