[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
=================