/*
* Python 3 bindings for libfiu
* Alberto Bertogli (albertito@blitiri.com.ar)
*
* This is the low-level module, used by the python one to construct
* friendlier objects.
*/
#include <Python.h>
/* Unconditionally enable fiu, otherwise we get fake headers */
#define FIU_ENABLE 1
#include <fiu-control.h>
#include <fiu.h>
static PyObject *fail(PyObject *self, PyObject *args)
{
char *name;
PyObject *rv, *err;
if (!PyArg_ParseTuple(args, "s:fail", &name))
return NULL;
rv = PyLong_FromLong(fiu_fail(name));
err = PyErr_Occurred();
if (rv == NULL || err != NULL) {
Py_XDECREF(rv);
return NULL;
}
return rv;
}
static PyObject *failinfo(PyObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":failinfo"))
return NULL;
/* We assume failinfo is a python object; but the caller must be
* careful because if it's not, it can get into trouble. */
PyObject *rv = fiu_failinfo();
Py_XINCREF(rv);
return rv;
}
static PyObject *enable(PyObject *self, PyObject *args)
{
char *name;
int failnum;
PyObject *failinfo;
unsigned int flags;
if (!PyArg_ParseTuple(args, "siOI:enable", &name, &failnum, &failinfo,
&flags))
return NULL;
/* The caller will guarantee that failinfo doesn't dissapear from under
* our feet. */
return PyLong_FromLong(fiu_enable(name, failnum, failinfo, flags));
}
static PyObject *enable_random(PyObject *self, PyObject *args)
{
char *name;
int failnum;
PyObject *failinfo;
unsigned int flags;
double probability;
if (!PyArg_ParseTuple(args, "siOId:enable_random", &name, &failnum,
&failinfo, &flags, &probability))
return NULL;
/* The caller will guarantee that failinfo doesn't dissapear from under
* our feet. */
return PyLong_FromLong(
fiu_enable_random(name, failnum, failinfo, flags, probability));
}
static int external_callback(const char *name, int *failnum, void **failinfo,
unsigned int *flags)
{
int rv;
PyObject *cbrv;
PyObject *args;
PyGILState_STATE gil_state;
/* We need to protect ourselves from the following case:
* - fiu.enable_callback('x', cb) (where cb is obviously a Python
* function)
* - Later on, call a function p1() inside a python C module, that
* runs a C function c1() inside
* Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS
* - c1() calls fiu_fail("x")
* - fiu_fail("x") calls external_callback(), and it should run cb()
* - BUT! It can't run cb(), because it's inside
* Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS so it's not safe to
* execute any Python code!
*
* The solution is to ensure we're safe to run Python code using
* PyGILState_Ensure()/PyGILState_Release().
*/
gil_state = PyGILState_Ensure();
args = Py_BuildValue("(siI)", name, *failnum, *flags);
if (args == NULL) {
PyGILState_Release(gil_state);
return 0;
}
cbrv = PyObject_CallObject(*failinfo, args);
Py_DECREF(args);
if (cbrv == NULL) {
PyGILState_Release(gil_state);
return 0;
}
/* If PyLong_AsLong() causes an error, it will be handled by the
* PyErr_Occurred() check in fail(), so we don't need to worry about
* it now. */
rv = PyLong_AsLong(cbrv);
Py_DECREF(cbrv);
PyGILState_Release(gil_state);
return rv;
}
static PyObject *enable_external(PyObject *self, PyObject *args)
{
char *name;
int failnum;
unsigned int flags;
PyObject *py_external_cb;
if (!PyArg_ParseTuple(args, "siIO:enable_external", &name, &failnum,
&flags, &py_external_cb))
return NULL;
if (!PyCallable_Check(py_external_cb)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
/* We use failinfo to store Python's callback function. It'd be nice
* if we could keep both, but it's not easy without keeping state
* inside the C module.
*
* Similar to the way failinfo is handled, we DO NOT TOUCH THE RC OF
* THE EXTERNAL CALLBACK, assuming the caller will take care of making
* sure it doesn't dissapear from beneath us. */
return PyLong_FromLong(fiu_enable_external(
name, failnum, py_external_cb, flags, external_callback));
}
static PyObject *enable_stack_by_name(PyObject *self, PyObject *args)
{
char *name;
int failnum;
PyObject *failinfo;
unsigned int flags;
char *func_name;
int pos_in_stack = -1;
if (!PyArg_ParseTuple(args, "siOIs|i:enable_stack_by_name", &name,
&failnum, &failinfo, &flags, &func_name,
&pos_in_stack))
return NULL;
return PyLong_FromLong(fiu_enable_stack_by_name(
name, failnum, failinfo, flags, func_name, pos_in_stack));
}
static PyObject *disable(PyObject *self, PyObject *args)
{
char *name;
if (!PyArg_ParseTuple(args, "s:disable", &name))
return NULL;
return PyLong_FromLong(fiu_disable(name));
}
static PyObject *set_prng_seed(PyObject *self, PyObject *args)
{
int seed;
if (!PyArg_ParseTuple(args, "i:set_prng_seed", &seed))
return NULL;
fiu_set_prng_seed(seed);
Py_RETURN_NONE;
}
static PyObject *rc_fifo(PyObject *self, PyObject *args)
{
char *basename;
if (!PyArg_ParseTuple(args, "s:rc_fifo", &basename))
return NULL;
return PyLong_FromLong(fiu_rc_fifo(basename));
}
static PyMethodDef fiu_methods[] = {
{"fail", (PyCFunction)fail, METH_VARARGS, NULL},
{"failinfo", (PyCFunction)failinfo, METH_VARARGS, NULL},
{"enable", (PyCFunction)enable, METH_VARARGS, NULL},
{"enable_random", (PyCFunction)enable_random, METH_VARARGS, NULL},
{"enable_external", (PyCFunction)enable_external, METH_VARARGS, NULL},
{"enable_stack_by_name", (PyCFunction)enable_stack_by_name, METH_VARARGS,
NULL},
{"disable", (PyCFunction)disable, METH_VARARGS, NULL},
{"set_prng_seed", (PyCFunction)set_prng_seed, METH_VARARGS, NULL},
{"rc_fifo", (PyCFunction)rc_fifo, METH_VARARGS, NULL},
{NULL}};
static PyModuleDef fiu_module = {
PyModuleDef_HEAD_INIT,
.m_name = "libfiu",
.m_size = -1,
.m_methods = fiu_methods,
};
PyMODINIT_FUNC PyInit_fiu_ll(void)
{
PyObject *m;
m = PyModule_Create(&fiu_module);
PyModule_AddIntConstant(m, "FIU_ONETIME", FIU_ONETIME);
fiu_init(0);
return m;
}