/****************************************************** The CFrame and friends ******************************************************/ /* * The purpose of a CFrame is to allow any callable to be run as * a tasklet. * A CFrame does not appear in tracebacks, but it does * play a role in frame chains. * At the moment, it can only appear as a toplevel frame of a tasklet. * * For simplicity, it mimicks the fields which slp_transfer needs * to do a proper switch, and the standard frame fields have * been slightly rearranged, to keep the size of CFrame small. * I have looked through all reachable C extensions to find out * which fields need to be present. * * The tasklet holds either a frame or a cframe and knows * how to handle them. * * In order to avoid incompatibilities with standard modules * like traceback, which might reach a cframe through * the extract_stack function, there is a couple of * computed attributes provided. */ #include "Python.h" #ifdef STACKLESS #include "stackless_impl.h" #include "prickelpit.h" /* access macro to the members which are floating "behind" the object */ #define PyBaseFrame_GET_MEMBERS(frame) \ ((PyObject **)(((char *)frame) + (frame)->ob_type->tp_basicsize)) static void baseframe_dealloc(PyBaseFrameObject *c) { int i; PyObject ** f_params = PyBaseFrame_GET_MEMBERS(c); PyObject_GC_UnTrack(c); Py_XDECREF(c->f_back); for (i=0; i < c->ob_size; ++i) { Py_XDECREF(f_params[i]); } PyObject_GC_Del(c); } static int baseframe_traverse(PyBaseFrameObject *c, visitproc visit, void *arg) { int i, err; PyObject ** f_params = PyBaseFrame_GET_MEMBERS(c); #define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;} VISIT(c->f_back); for (i=0; i < c->ob_size; ++i) { VISIT(f_params[i]); } return 0; } /* clearing a baseframe while the objects still exists */ static void baseframe_clear(PyBaseFrameObject *c) { int i; PyObject ** f_params = PyBaseFrame_GET_MEMBERS(c); for (i=0; i < c->ob_size; ++i) { PyObject *hold = f_params[i]; if (hold != NULL) { f_params[i] = NULL; Py_DECREF(hold); } } /* note that the object is still alive, and ready for normal destruction without side effects */ } PyBaseFrameObject * slp_baseframe_new(PyFrame_ExecFunc *exec, unsigned int linked, unsigned int extra) { PyThreadState *ts = PyThreadState_GET(); PyBaseFrameObject *c; PyFrameObject *back; int i; c = PyObject_GC_NewVar(PyBaseFrameObject, &PyBaseFrame_Type, extra); if (c == NULL) return NULL; back = ts->frame; if (!linked) back = NULL; Py_XINCREF(back); c->f_back = back; for (i=0; i < c->ob_size; ++i) { PyObject ** f_params = PyBaseFrame_GET_MEMBERS(c); f_params[i] = NULL; } c->f_execute = exec; _PyObject_GC_TRACK(c); return c; } /* pickling support for baseframes and simple derived objects */ #define baseframetuplefmt "iSO" static PyObject * baseframe_reduce(PyBaseFrameObject *f) { PyObject *res = NULL, *exec_name = NULL; PyObject *params = NULL; int valid = 1, extra; if ((exec_name = slp_find_execname((PyFrameObject *) f, &valid)) == NULL) return NULL; extra = f->ob_size; params = slp_into_tuple_with_nulls(PyBaseFrame_GET_MEMBERS(f), extra); if (params == NULL) goto err_exit; res = Py_BuildValue ("(O(" baseframetuplefmt "))", f->ob_type, valid, exec_name, params ); err_exit: Py_XDECREF(exec_name); Py_XDECREF(params); return res; } static PyMethodDef baseframe_methods[] = { {"__reduce__", (PyCFunction)baseframe_reduce, METH_NOARGS, NULL}, {NULL, NULL} }; #define baseframetuplenewfmt "iSO!:baseframe" static PyObject * baseframe_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyBaseFrameObject *f; int valid; PyObject *exec_name = NULL; PyFrame_ExecFunc *good_func, *bad_func; PyObject *params; int extra; if (kwds != NULL) { PyErr_SetString(PyExc_ValueError, "Keyword parameters not supported for baseframe unpickling"); return NULL; } if (!PyArg_ParseTuple (args, baseframetuplenewfmt, &valid, &exec_name, &PyTuple_Type, ¶ms )) return NULL; if (slp_find_execfuncs(type, exec_name, &good_func, &bad_func)) return NULL; extra = PyTuple_GET_SIZE(params)-1; f = slp_baseframe_new(valid ? good_func : bad_func, 0, extra); /* mark as from unpickling */ Py_INCREF(Py_None); f->f_back = (PyFrameObject *) Py_None; f->ob_type = type; slp_from_tuple_with_nulls(PyBaseFrame_GET_MEMBERS(f), params); return (PyObject *) f; } static PyObject * run_cframe(PyFrameObject *f) { PyThreadState *ts = PyThreadState_GET(); PyCFrameObject *c = (PyCFrameObject*) f; PyObject *result = NULL; ts->frame = f; result = ts->st.tempval; ts->st.tempval = NULL; if (result == NULL) goto exit_run_cframe; if (c->callable == NULL) { /* we were left by a soft call */ } else { /* * try to save stack space by inlining PyCFunction with METH_KEYWORDS. * These are simple to handle, and also most suitable for tasklets, * which should try to look like any other Python function. */ Py_DECREF(result); if (PyCFunction_Check(c->callable) && PyCFunction_GET_FLAGS(c->callable) & METH_KEYWORDS && PyCFunction_GET_FUNCTION(c->callable) != NULL) { STACKLESS_PROPOSE_FLAG(PyCFunction_GET_FLAGS(c->callable) & METH_STACKLESS); result = (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(c->callable))( PyCFunction_GET_SELF(c->callable), c->args, c->kwds); } else { STACKLESS_PROPOSE_ALL(); result = PyObject_Call(c->callable, c->args, c->kwds); } STACKLESS_ASSERT(); if (result == Py_UnwindToken) { Py_DECREF(c->callable); c->callable = NULL; return Py_UnwindToken; } } /* pop frame */ exit_run_cframe: ts->frame = c->bf.f_back; Py_DECREF(f); return result; } DEF_INVALID_EXEC(run_cframe) PyCFrameObject * slp_cframe_new(PyObject *func, PyObject *args, PyObject *kwds, unsigned int linked) { PyCFrameObject *c; if (func == NULL || !PyCallable_Check(func)) return TYPE_ERROR("cframe function must be a callable", NULL); c = (PyCFrameObject *)slp_baseframe_new(run_cframe, linked, 3); if (c == NULL) return NULL; c->bf.ob_type = &PyCFrame_Type; Py_INCREF(func); c->callable = func; Py_INCREF(args); c->args = args; Py_XINCREF(kwds); c->kwds = kwds; return c; } static PyMemberDef baseframe_memberlist[] = { {"f_back", T_OBJECT, offsetof(PyCFrameObject, bf.f_back), RO}, {"_exec_adr", T_INT, offsetof(PyCFrameObject, bf.f_execute), RO, "The address of the execute function of this frame.\n" "use f._exec_map[adr] to find its pickling name."}, {NULL} /* Sentinel */ }; static PyMemberDef cframe_memberlist[] = { {"f_back", T_OBJECT, offsetof(PyCFrameObject, bf.f_back), RO}, {"callable", T_OBJECT, offsetof(PyCFrameObject, callable), RO}, {"args", T_OBJECT, offsetof(PyCFrameObject, args), RO}, {"kwds", T_OBJECT, offsetof(PyCFrameObject, kwds),RO}, {"_exec_adr", T_INT, offsetof(PyCFrameObject, bf.f_execute), RO, "The address of the execute function of this frame.\n" "use f._exec_map[adr] to find its pickling name."}, {NULL} /* Sentinel */ }; PyTypeObject PyBaseFrame_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "stackless.baseframe", sizeof(PyBaseFrameObject), sizeof(PyObject *), (destructor)baseframe_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)baseframe_traverse, /* tp_traverse */ (inquiry) baseframe_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ baseframe_methods, /* tp_methods */ baseframe_memberlist, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ baseframe_new, /* tp_new */ _PyObject_Del, /* tp_free */ }; PyTypeObject PyCFrame_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "stackless.cframe", sizeof(PyBaseFrameObject), sizeof(PyObject *), (destructor)baseframe_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)baseframe_traverse, /* tp_traverse */ (inquiry) baseframe_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ cframe_memberlist, /* tp_members */ 0, /* tp_getset */ &PyBaseFrame_Type, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ baseframe_new, /* tp_new */ _PyObject_Del, /* tp_free */ }; int init_cframetype(void) { /* register the cframe exec func */ slp_register_execute(&PyCFrame_Type, "run_frame", run_cframe, REF_INVALID_EXEC(run_cframe)); return 0; } #endif