Skip to content

Commit

Permalink
Stackless issue python#265: Fix (invalid) function cast warnings with…
Browse files Browse the repository at this point in the history
… gcc 8

* fix PyChannel_Send_M

The implementation was buggy. This affects only users of the C-API,
that call PyChannel_Send() and PyChannel_Send_nr() for calls from
outside of the interpreter (with no main-tasklet).

* fix PyChannel_Receive_M and tasklet_new

If called from outside of the interpreter the behavior was undefined.
(Calling a C-function with wrong number of arguments).

* fix __reduce__ / __reduce_ex__

Avoid undefined behavior, if Stackless classes implement __reduce__()
and __reduce_ex__() using a single C-function.
__reduce_ex__ now checks the type of its argument.

* fix the signature of tp_clear functions

Return type is 'int', not 'void'.

* add missing arguments to C-function definitions

Most (getter)-functions, (setter)-functions and (PyCFunction)-functions
with flag METH_NOARGS lack an unused last argument, "void *" for
(getter) and (setter), "PyObject *" for (PyCFunction)-functions.
This commit adds these arguments.

* Fix invalid function cast warnings

Fix invalid function cast warnings with gcc 8 for method conventions
different from METH_NOARGS, METH_O and METH_VARARGS excluding Argument
Clinic generated code.
  • Loading branch information
akruis authored Jun 11, 2021
1 parent 88271d3 commit b73166e
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 114 deletions.
2 changes: 1 addition & 1 deletion Include/internal/pycore_stackless.h
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ PyObject* slp_context_run_callback(PyFrameObject *f, int exc, PyObject *result);
(ts->st.runflags & PY_WATCHDOG_IGNORE_NESTING))

/* Interpreter shutdown and thread state access */
PyObject * slp_getthreads(PyObject *self);
PyObject * slp_getthreads(PyObject *self, PyObject *unused);
void slp_head_lock(void);
void slp_head_unlock(void);
#define SLP_HEAD_LOCK() slp_head_lock()
Expand Down
4 changes: 2 additions & 2 deletions Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -4689,7 +4689,7 @@ Pickler_set_memo(PicklerObject *self, PyObject *obj, void *Py_UNUSED(ignored))

#ifdef STACKLESS
static PyObject *
Pickler_get_module_dict_ids(PicklerObject *p)
Pickler_get_module_dict_ids(PicklerObject *p, void *closure)
{
if (p->module_dict_ids == NULL)
PyErr_SetString(PyExc_AttributeError, "module_dict_ids");
Expand All @@ -4699,7 +4699,7 @@ Pickler_get_module_dict_ids(PicklerObject *p)
}

static int
Pickler_set_module_dict_ids(PicklerObject *p, PyObject *v)
Pickler_set_module_dict_ids(PicklerObject *p, PyObject *v, void *closure)
{
if (v == NULL) {
PyErr_SetString(PyExc_TypeError,
Expand Down
20 changes: 20 additions & 0 deletions Stackless/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ What's New in Stackless 3.X.X?

*Release date: 20XX-XX-XX*

- https://github.com/stackless-dev/stackless/issues/265
Fix invalid function cast warnings with gcc 8 for method conventions
different from METH_NOARGS, METH_O and METH_VARARGS excluding Argument Clinic
generated code.

- https://github.com/stackless-dev/stackless/issues/265
Most (getter)-functions, (setter)-functions and (PyCFunction)-functions with
flag METH_NOARGS used to lack an unused last argument, "void *" for (getter)
and (setter), "PyObject *" for (PyCFunction)-functions.

- https://github.com/stackless-dev/stackless/issues/265
Avoid undefined behavior, if Stackless classes implement __reduce__() and
__reduce_ex__() using a single C-function.

- https://github.com/stackless-dev/stackless/issues/265
Fix C-API functions PyChannel_Send(), PyChannel_Send_nr(),
PyChannel_Receive(), PyChannel_Receive_nr(), and
PyObject_Call(&PyTasklet_Type, PyObject *args, PyObject *kwargs) if called
from outside of the interpreter (with no main-tasklet).

- https://github.com/stackless-dev/stackless/issues/264
Implement bpo-35134 for Stackless: move header Include/slp_tstate.h to
Include/cpython/slp_tstate.h.
Expand Down
21 changes: 16 additions & 5 deletions Stackless/core/cframeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ cframe_traverse(PyCFrameObject *cf, visitproc visit, void *arg)

/* clearing a cframe while the object still exists */

static void
static int
cframe_clear(PyCFrameObject *cf)
{
/* The Python C-API documentation recomends to use Py_CLEAR() to release
Expand All @@ -86,6 +86,7 @@ cframe_clear(PyCFrameObject *cf)
Py_XDECREF(tmp_ob1);
Py_XDECREF(tmp_ob2);
Py_XDECREF(tmp_ob3);
return 0;
}


Expand Down Expand Up @@ -133,14 +134,19 @@ static PyObject * execute_soft_switchable_func(PyFrameObject *, int, PyObject *)
SLP_DEF_INVALID_EXEC(execute_soft_switchable_func)

static PyObject *
cframe_reduce(PyCFrameObject *cf)
cframe_reduce(PyCFrameObject *cf, PyObject *value)
{
PyObject *res = NULL, *exec_name = NULL;
PyObject *params = NULL;
int valid = 1;
PyObject *obs[3];
long i, n;

if (value && !PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
return NULL;
}

if (cf->f_execute == execute_soft_switchable_func) {
exec_name = (PyObject *) cf->any2;
assert(cf->any2);
Expand Down Expand Up @@ -240,7 +246,7 @@ cframe_setstate(PyObject *self, PyObject *args)

static PyMethodDef cframe_methods[] = {
{"__reduce__", (PyCFunction)cframe_reduce, METH_NOARGS, NULL},
{"__reduce_ex__", (PyCFunction)cframe_reduce, METH_VARARGS, NULL},
{"__reduce_ex__", (PyCFunction)cframe_reduce, METH_O, NULL},
{"__setstate__", (PyCFunction)cframe_setstate, METH_O, NULL},
{NULL, NULL}
};
Expand Down Expand Up @@ -386,8 +392,13 @@ slp_cframe_fini(void)
*/

static PyObject *
function_declaration_reduce(PyStacklessFunctionDeclarationObject *self)
function_declaration_reduce(PyStacklessFunctionDeclarationObject *self, PyObject *value)
{
if (value && !PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
return NULL;
}

if (self->name == NULL || *self->name == '\0') {
PyErr_SetString(PyExc_SystemError, "no function name");
return NULL;
Expand All @@ -397,7 +408,7 @@ function_declaration_reduce(PyStacklessFunctionDeclarationObject *self)

static PyMethodDef function_declaration_methods[] = {
{"__reduce__", (PyCFunction)function_declaration_reduce, METH_NOARGS, NULL},
{"__reduce_ex__", (PyCFunction)function_declaration_reduce, METH_VARARGS, NULL},
{"__reduce_ex__", (PyCFunction)function_declaration_reduce, METH_O, NULL},
{NULL, NULL}
};

Expand Down
4 changes: 2 additions & 2 deletions Stackless/module/_teststackless.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,11 +494,11 @@ static PyObject* test_PyEval_EvalFrameEx(PyObject *self, PyObject *args, PyObjec
static PyMethodDef _teststackless_methods[] = {
{"softswitchablefunc", _teststackless_softswitchabledemo, METH_VARARGS | METH_STACKLESS,
_teststackless_softswitchabledemo_doc},
{ "test_cframe", (PyCFunction) test_cframe, METH_VARARGS | METH_KEYWORDS,
{ "test_cframe", (PyCFunction)(void(*)(void))test_cframe, METH_VARARGS | METH_KEYWORDS,
test_cframe__doc__ },
{ "test_cstate", (PyCFunction) test_cstate, METH_O,
test_cstate__doc__ },
{ "test_PyEval_EvalFrameEx", (PyCFunction) test_PyEval_EvalFrameEx, METH_VARARGS | METH_KEYWORDS,
{ "test_PyEval_EvalFrameEx", (PyCFunction)(void(*)(void))test_PyEval_EvalFrameEx, METH_VARARGS | METH_KEYWORDS,
test_PyEval_EvalFrameEx__doc__ },
{NULL, NULL} /* sentinel */
};
Expand Down
47 changes: 31 additions & 16 deletions Stackless/module/channelobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ channel_traverse(PyChannelObject *ch, visitproc visit, void *arg)
return 0;
}

static void
static int
channel_clear(PyObject *ob)
{
PyChannelObject *ch = (PyChannelObject *) ob;
Expand All @@ -74,6 +74,7 @@ channel_clear(PyObject *ob)
ob = (PyObject *) slp_channel_remove(ch, NULL, NULL, NULL);
Py_DECREF(ob);
}
return 0;
}

static void
Expand Down Expand Up @@ -195,7 +196,7 @@ channel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}

static PyObject *
channel_get_queue(PyChannelObject *self)
channel_get_queue(PyChannelObject *self, void *closure)
{
PyObject *ret = (PyObject*) self->head;

Expand All @@ -208,11 +209,11 @@ channel_get_queue(PyChannelObject *self)
PyObject *
PyChannel_GetQueue(PyChannelObject *self)
{
return channel_get_queue(self);
return channel_get_queue(self, NULL);
}

static PyObject *
channel_get_closing(PyChannelObject *self)
channel_get_closing(PyChannelObject *self, void *closure)
{
return PyBool_FromLong(self->flags.closing);
}
Expand All @@ -224,7 +225,7 @@ PyChannel_GetClosing(PyChannelObject *self)
}

static PyObject *
channel_get_closed(PyChannelObject *self)
channel_get_closed(PyChannelObject *self, void *closure)
{
return PyBool_FromLong(self->flags.closing && self->balance == 0);
}
Expand All @@ -237,13 +238,13 @@ PyChannel_GetClosed(PyChannelObject *self)


static PyObject *
channel_get_preference(PyChannelObject *self)
channel_get_preference(PyChannelObject *self, void *closure)
{
return PyLong_FromLong(self->flags.preference);
}

static int
channel_set_preference(PyChannelObject *self, PyObject *value)
channel_set_preference(PyChannelObject *self, PyObject *value, void *closure)
{
int val;

Expand All @@ -267,13 +268,13 @@ PyChannel_SetPreference(PyChannelObject *self, int val)
}

static PyObject *
channel_get_schedule_all(PyChannelObject *self)
channel_get_schedule_all(PyChannelObject *self, void *closure)
{
return PyLong_FromLong(self->flags.schedule_all);
}

static int
channel_set_schedule_all(PyChannelObject *self, PyObject *value)
channel_set_schedule_all(PyChannelObject *self, PyObject *value, void *closure)
{
if (!PyLong_Check(value))
TYPE_ERROR("preference must be set to a bool or integer", -1);
Expand Down Expand Up @@ -425,10 +426,13 @@ the sender will be blocked. Otherwise, the receiver will\n\
be activated immediately, and the sender is put at the end of\n\
the runnables list.");

static PyObject *
impl_channel_send(PyChannelObject *self, PyObject *arg);

static PyObject *
PyChannel_Send_M(PyChannelObject *self, PyObject *arg)
{
PyMethodDef def = {"send", (PyCFunction)PyChannel_Send, METH_O};
PyMethodDef def = {"send", (PyCFunction)impl_channel_send, METH_O};
return PyStackless_CallCMethod_Main(&def, (PyObject *) self, "O", arg);
}

Expand Down Expand Up @@ -770,10 +774,16 @@ continue immediately, and the sender is put at the end of\n\
the runnables list.\n\
The above policy can be changed by setting channel flags.");

static PyObject *
PyChannel_Receive_cfunc(PyChannelObject *self, PyObject *unused)
{
return PyChannel_Receive(self);
}

static PyObject *
PyChannel_Receive_M(PyChannelObject *self)
{
PyMethodDef def = {"receive", (PyCFunction)PyChannel_Receive, METH_NOARGS};
PyMethodDef def = {"receive", (PyCFunction)PyChannel_Receive_cfunc, METH_NOARGS};
return PyStackless_CallCMethod_Main(&def, (PyObject *) self, NULL);
}

Expand Down Expand Up @@ -809,7 +819,7 @@ PyChannel_Receive(PyChannelObject *self)
}

static PyObject *
channel_receive(PyObject *self)
channel_receive(PyObject *self, PyObject *unused)
{
return impl_channel_receive((PyChannelObject*)self);
}
Expand Down Expand Up @@ -1059,7 +1069,7 @@ If the channel is not empty, the flag 'closing' becomes true.\n\
If the channel is empty, the flag 'closed' becomes true.");

static PyObject *
channel_close(PyChannelObject *self)
channel_close(PyChannelObject *self, PyObject *unused)
{
self->flags.closing = 1;

Expand All @@ -1077,7 +1087,7 @@ PyDoc_STRVAR(channel_open__doc__,
"channel.open() -- reopen a channel. See channel.close.");

static PyObject *
channel_open(PyChannelObject *self)
channel_open(PyChannelObject *self, PyObject *unused)
{
self->flags.closing = 0;

Expand All @@ -1095,12 +1105,17 @@ PyDoc_STRVAR(channel_reduce__doc__,
"channel.__reduce__() -- currently does not distinguish threads.");

static PyObject *
channel_reduce(PyChannelObject * ch)
channel_reduce(PyChannelObject * ch, PyObject *value)
{
PyObject *tup = NULL, *lis = NULL;
PyTaskletObject *t;
int i, n;

if (value && !PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
return NULL;
}

lis = PyList_New(0);
if (lis == NULL) goto err_exit;
t = ch->head;
Expand Down Expand Up @@ -1179,7 +1194,7 @@ channel_methods[] = {
channel_open__doc__},
{"__reduce__", (PCF)channel_reduce, METH_NOARGS,
channel_reduce__doc__},
{"__reduce_ex__", (PCF)channel_reduce, METH_VARARGS,
{"__reduce_ex__", (PCF)channel_reduce, METH_O,
channel_reduce__doc__},
{"__setstate__", (PCF)channel_setstate, METH_O,
channel_setstate__doc__},
Expand Down
12 changes: 9 additions & 3 deletions Stackless/module/scheduling.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ bomb_traverse(PyBombObject *bomb, visitproc visit, void *arg)
return 0;
}

static void
static int
bomb_clear(PyBombObject *bomb)
{
Py_CLEAR(bomb->curexc_type);
Py_CLEAR(bomb->curexc_value);
Py_CLEAR(bomb->curexc_traceback);
return 0;
}

PyBombObject *
Expand Down Expand Up @@ -216,10 +217,15 @@ slp_bomb_explode(PyObject *_bomb)
}

static PyObject *
bomb_reduce(PyBombObject *bomb)
bomb_reduce(PyBombObject *bomb, PyObject *value)
{
PyObject *tup;

if (value && !PyLong_Check(value)) {
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
return NULL;
}

tup = slp_into_tuple_with_nulls(&bomb->curexc_type, 3);
if (tup != NULL)
tup = Py_BuildValue("(O()O)", &PyBomb_Type, tup);
Expand All @@ -246,7 +252,7 @@ static PyMemberDef bomb_members[] = {

static PyMethodDef bomb_methods[] = {
{"__reduce__", (PyCFunction)bomb_reduce, METH_NOARGS},
{"__reduce_ex__", (PyCFunction)bomb_reduce, METH_VARARGS},
{"__reduce_ex__", (PyCFunction)bomb_reduce, METH_O},
{"__setstate__", (PyCFunction)bomb_setstate, METH_O},
{NULL, NULL} /* sentinel */
};
Expand Down
Loading

0 comments on commit b73166e

Please sign in to comment.