[PYTHON MATRIX-SIG] Ruminations on SIGFPE
Guido van Rossum
guido@CNRI.Reston.Va.US
Wed, 18 Sep 1996 22:32:31 -0400
This sounds like a good start.
Python's current signal handling is really intended for async signal
handling only. Since SIGFPE is an exception to most rules about
signals anyway, I don't mind making an exception for it, provided it
can be done fairly portably or disabled easily.
I would think that in order to catch most FP exceptions in *Python*
code, modifications to a small number of files would suffice; you
could start with floatobject.c and complexobject.c, and then add
mathmodule.c and cmathmodule.c and be done with most of it.
I'd expect that the modification could look roughly like this:
- The interface is based on controlled use of setjmp/longjmp.
- Somewhere globally, set a signal handler for SIGFPE.
- Before invoking some code that could raise SIGFPE, you declare a
setjmp buffer and call setjmp(). You then set a global variable
that points to the setjmp buffer. (All this could be done in a macro
so it's easily disabled.) When the code returns safely, you zap the
global pointer variable.
- The SIGFPE handler, when invokes, sees if this global variable is
set. If so, it longjmps there. If not, it should emulate the current
behavior (NaN or core dump -- perhaps a call to
PyErr_Fatal("unhandled FPE") is the thing to do).
- If the longjmp is done, the code invoking the unsafe code should zap
the global pointer variable as before, clean up, and raise a Python
exception (usually this done through a call to PyErr_Set*() and
returning NULL).
Unfortunately this looks like a lot of work. I expect that a few
macros could be developed so that e.g. fload_add() could changed from
static object *
float_add(v, w)
floatobject *v;
floatobject *w;
{
return newfloatobject(v->ob_fval + w->ob_fval);
}
to
static object *
float_add(v, w)
floatobject *v;
floatobject *w;
{
double result;
PyFPE_MAGIC_PREP(return NULL)
result = v->ob_fval + w->ob_fval;
PyFPE_MAGIC_DONE
return newfloatobject(result);
}
Roughly, PyFPE_MAGIC_PREP(leave_stmt) should expand to
{
jmp_buf b;
void *save = PyFPE_JumpBufPtr;
if (setjmp(b)) {
PyFPE_JumpBufPtr = save;
PyErr_SetString(PyExc_FloatingPointException, "...");
leave_stmt;
}
PyFPE_JumpBufPtr = &b;
(note unmatched brace -- see next macro) and PyFPE_MAGIC_DONE should
expand to
PyFPE_JumpBufPtr = save;
}
Note that this takes care of nested invocations through the 'save'
variable.
Some problems with this:
- the type of PyFPE_JumpBufPtr is problematic
- it's rather a lot of overhead for a single f.p. add, therefore it
probably should be an option (on the other hand, there's already a lot
of overhead in any single float operation in Python, e.g. the
newfloatobject() call does a malloc(), etc.)
- it's a lot of changes to get this right.
- I don't have the time to write this code (though I will accept it as
a patch for 1.5).
Hope this helps,
--Guido van Rossum (home page: http://www.python.org/~guido/)
=================
MATRIX-SIG - SIG on Matrix Math for Python
send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================