|
|
|
#ifndef PYGREENLET_CPP |
|
#define PYGREENLET_CPP |
|
|
|
|
|
|
|
|
|
|
|
#define PY_SSIZE_T_CLEAN |
|
#include <Python.h> |
|
#include "structmember.h" |
|
|
|
#include "greenlet_internal.hpp" |
|
#include "TThreadStateDestroy.cpp" |
|
#include "TGreenlet.hpp" |
|
|
|
|
|
|
|
|
|
|
|
#include "greenlet_refs.hpp" |
|
#include "greenlet_slp_switch.hpp" |
|
|
|
#include "greenlet_thread_support.hpp" |
|
#include "TGreenlet.hpp" |
|
|
|
#include "TGreenletGlobals.cpp" |
|
#include "TThreadStateDestroy.cpp" |
|
#include "PyGreenlet.hpp" |
|
|
|
|
|
|
|
|
|
|
|
|
|
using greenlet::LockGuard; |
|
using greenlet::LockInitError; |
|
using greenlet::PyErrOccurred; |
|
using greenlet::Require; |
|
|
|
using greenlet::g_handle_exit; |
|
using greenlet::single_result; |
|
|
|
using greenlet::Greenlet; |
|
using greenlet::UserGreenlet; |
|
using greenlet::MainGreenlet; |
|
using greenlet::BrokenGreenlet; |
|
using greenlet::ThreadState; |
|
using greenlet::PythonState; |
|
|
|
|
|
|
|
static PyGreenlet* |
|
green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds)) |
|
{ |
|
PyGreenlet* o = |
|
(PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict); |
|
if (o) { |
|
new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current()); |
|
assert(Py_REFCNT(o) == 1); |
|
} |
|
return o; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int |
|
green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs) |
|
{ |
|
PyArgParseParam run; |
|
PyArgParseParam nparent; |
|
static const char* kwlist[] = { |
|
"run", |
|
"parent", |
|
NULL |
|
}; |
|
|
|
|
|
if (!PyArg_ParseTupleAndKeywords( |
|
args, kwargs, "|OO:green", (char**)kwlist, &run, &nparent)) { |
|
return -1; |
|
} |
|
|
|
if (run) { |
|
if (green_setrun(self, run, NULL)) { |
|
return -1; |
|
} |
|
} |
|
if (nparent && !nparent.is_None()) { |
|
return green_setparent(self, nparent, NULL); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
|
|
static int |
|
green_traverse(PyGreenlet* self, visitproc visit, void* arg) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Py_VISIT(self->dict); |
|
if (!self->pimpl) { |
|
|
|
|
|
|
|
|
|
return 0; |
|
} |
|
|
|
return self->pimpl->tp_traverse(visit, arg); |
|
} |
|
|
|
static int |
|
green_is_gc(PyObject* _self) |
|
{ |
|
BorrowedGreenlet self(_self); |
|
int result = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (self->main() || !self->active()) { |
|
result = 1; |
|
} |
|
|
|
if (self->was_running_in_dead_thread()) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = 1; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
static int |
|
green_clear(PyGreenlet* self) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
Py_CLEAR(self->dict); |
|
return self->pimpl->tp_clear(); |
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self) |
|
{ |
|
|
|
|
|
assert(self.REFCNT() == 0); |
|
Py_SET_REFCNT(self.borrow(), 1); |
|
|
|
PyErrPieces saved_err; |
|
try { |
|
|
|
|
|
|
|
|
|
|
|
|
|
self->deallocing_greenlet_in_thread( |
|
self->thread_state() |
|
? static_cast<ThreadState*>(GET_THREAD_STATE()) |
|
: nullptr); |
|
} |
|
catch (const PyErrOccurred&) { |
|
PyErr_WriteUnraisable(self.borrow_o()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (self.REFCNT() == 1 && self->active()) { |
|
|
|
|
|
PyObject* f = PySys_GetObject("stderr"); |
|
Py_INCREF(self.borrow_o()); |
|
if (f != NULL) { |
|
PyFile_WriteString("GreenletExit did not kill ", f); |
|
PyFile_WriteObject(self.borrow_o(), f, 0); |
|
PyFile_WriteString("\n", f); |
|
} |
|
} |
|
|
|
saved_err.PyErrRestore(); |
|
|
|
|
|
|
|
assert(self.REFCNT() > 0); |
|
|
|
Py_ssize_t refcnt = self.REFCNT() - 1; |
|
Py_SET_REFCNT(self.borrow_o(), refcnt); |
|
if (refcnt != 0) { |
|
|
|
_Py_NewReference(self.borrow_o()); |
|
Py_SET_REFCNT(self.borrow_o(), refcnt); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) { |
|
Py_INCREF(self.TYPE()); |
|
} |
|
|
|
PyObject_GC_Track((PyObject*)self); |
|
|
|
_Py_DEC_REFTOTAL; |
|
#ifdef COUNT_ALLOCS |
|
--Py_TYPE(self)->tp_frees; |
|
--Py_TYPE(self)->tp_allocs; |
|
#endif |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
|
|
static void |
|
green_dealloc(PyGreenlet* self) |
|
{ |
|
PyObject_GC_UnTrack(self); |
|
BorrowedGreenlet me(self); |
|
if (me->active() |
|
&& me->started() |
|
&& !me->main()) { |
|
if (!_green_dealloc_kill_started_non_main_greenlet(me)) { |
|
return; |
|
} |
|
} |
|
|
|
if (self->weakreflist != NULL) { |
|
PyObject_ClearWeakRefs((PyObject*)self); |
|
} |
|
Py_CLEAR(self->dict); |
|
|
|
if (self->pimpl) { |
|
|
|
|
|
|
|
Greenlet* p = self->pimpl; |
|
self->pimpl = nullptr; |
|
delete p; |
|
} |
|
|
|
Py_TYPE(self)->tp_free((PyObject*)self); |
|
} |
|
|
|
|
|
|
|
static OwnedObject |
|
internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces) |
|
{ |
|
PyObject* result = nullptr; |
|
err_pieces.PyErrRestore(); |
|
assert(PyErr_Occurred()); |
|
if (self->started() && !self->active()) { |
|
|
|
result = g_handle_exit(OwnedObject()).relinquish_ownership(); |
|
} |
|
self->args() <<= result; |
|
|
|
return single_result(self->g_switch()); |
|
} |
|
|
|
|
|
|
|
PyDoc_STRVAR( |
|
green_switch_doc, |
|
"switch(*args, **kwargs)\n" |
|
"\n" |
|
"Switch execution to this greenlet.\n" |
|
"\n" |
|
"If this greenlet has never been run, then this greenlet\n" |
|
"will be switched to using the body of ``self.run(*args, **kwargs)``.\n" |
|
"\n" |
|
"If the greenlet is active (has been run, but was switch()'ed\n" |
|
"out before leaving its run function), then this greenlet will\n" |
|
"be resumed and the return value to its switch call will be\n" |
|
"None if no arguments are given, the given argument if one\n" |
|
"argument is given, or the args tuple and keyword args dict if\n" |
|
"multiple arguments are given.\n" |
|
"\n" |
|
"If the greenlet is dead, or is the current greenlet then this\n" |
|
"function will simply return the arguments using the same rules as\n" |
|
"above.\n"); |
|
|
|
static PyObject* |
|
green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs) |
|
{ |
|
using greenlet::SwitchingArgs; |
|
SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs)); |
|
self->pimpl->may_switch_away(); |
|
self->pimpl->args() <<= switch_args; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
OwnedObject result(single_result(self->pimpl->g_switch())); |
|
#ifndef NDEBUG |
|
|
|
|
|
assert(!self->pimpl->args()); |
|
|
|
const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current(); |
|
|
|
assert(!current->args()); |
|
#endif |
|
PyObject* p = result.relinquish_ownership(); |
|
|
|
if (!p && !PyErr_Occurred()) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert(p || PyErr_Occurred()); |
|
throw PyErrOccurred( |
|
mod_globs->PyExc_GreenletError, |
|
"Greenlet.switch() returned NULL without an exception set." |
|
); |
|
} |
|
return p; |
|
} |
|
catch(const PyErrOccurred&) { |
|
return nullptr; |
|
} |
|
} |
|
|
|
PyDoc_STRVAR( |
|
green_throw_doc, |
|
"Switches execution to this greenlet, but immediately raises the\n" |
|
"given exception in this greenlet. If no argument is provided, the " |
|
"exception\n" |
|
"defaults to `greenlet.GreenletExit`. The normal exception\n" |
|
"propagation rules apply, as described for `switch`. Note that calling " |
|
"this\n" |
|
"method is almost equivalent to the following::\n" |
|
"\n" |
|
" def raiser():\n" |
|
" raise typ, val, tb\n" |
|
" g_raiser = greenlet(raiser, parent=g)\n" |
|
" g_raiser.switch()\n" |
|
"\n" |
|
"except that this trick does not work for the\n" |
|
"`greenlet.GreenletExit` exception, which would not propagate\n" |
|
"from ``g_raiser`` to ``g``.\n"); |
|
|
|
static PyObject* |
|
green_throw(PyGreenlet* self, PyObject* args) |
|
{ |
|
PyArgParseParam typ(mod_globs->PyExc_GreenletExit); |
|
PyArgParseParam val; |
|
PyArgParseParam tb; |
|
|
|
if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) { |
|
return nullptr; |
|
} |
|
|
|
assert(typ.borrow() || val.borrow()); |
|
|
|
self->pimpl->may_switch_away(); |
|
try { |
|
|
|
|
|
PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow()); |
|
|
|
return internal_green_throw(self, err_pieces).relinquish_ownership(); |
|
} |
|
catch (const PyErrOccurred&) { |
|
return nullptr; |
|
} |
|
} |
|
|
|
static int |
|
green_bool(PyGreenlet* self) |
|
{ |
|
return self->pimpl->active(); |
|
} |
|
|
|
|
|
|
|
|
|
static PyObject* |
|
green_getdict(PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
if (self->dict == NULL) { |
|
self->dict = PyDict_New(); |
|
if (self->dict == NULL) { |
|
return NULL; |
|
} |
|
} |
|
Py_INCREF(self->dict); |
|
return self->dict; |
|
} |
|
|
|
static int |
|
green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context)) |
|
{ |
|
PyObject* tmp; |
|
|
|
if (val == NULL) { |
|
PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted"); |
|
return -1; |
|
} |
|
if (!PyDict_Check(val)) { |
|
PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary"); |
|
return -1; |
|
} |
|
tmp = self->dict; |
|
Py_INCREF(val); |
|
self->dict = val; |
|
Py_XDECREF(tmp); |
|
return 0; |
|
} |
|
|
|
static bool |
|
_green_not_dead(BorrowedGreenlet self) |
|
{ |
|
|
|
|
|
if (self->was_running_in_dead_thread()) { |
|
self->deactivate_and_free(); |
|
return false; |
|
} |
|
return self->active() || !self->started(); |
|
} |
|
|
|
|
|
static PyObject* |
|
green_getdead(PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
if (_green_not_dead(self)) { |
|
Py_RETURN_FALSE; |
|
} |
|
else { |
|
Py_RETURN_TRUE; |
|
} |
|
} |
|
|
|
static PyObject* |
|
green_get_stack_saved(PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
return PyLong_FromSsize_t(self->pimpl->stack_saved()); |
|
} |
|
|
|
|
|
static PyObject* |
|
green_getrun(PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
try { |
|
OwnedObject result(BorrowedGreenlet(self)->run()); |
|
return result.relinquish_ownership(); |
|
} |
|
catch(const PyErrOccurred&) { |
|
return nullptr; |
|
} |
|
} |
|
|
|
|
|
static int |
|
green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context)) |
|
{ |
|
try { |
|
BorrowedGreenlet(self)->run(nrun); |
|
return 0; |
|
} |
|
catch(const PyErrOccurred&) { |
|
return -1; |
|
} |
|
} |
|
|
|
static PyObject* |
|
green_getparent(PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
return BorrowedGreenlet(self)->parent().acquire_or_None(); |
|
} |
|
|
|
|
|
static int |
|
green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context)) |
|
{ |
|
try { |
|
BorrowedGreenlet(self)->parent(nparent); |
|
} |
|
catch(const PyErrOccurred&) { |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
static PyObject* |
|
green_getcontext(const PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
const Greenlet *const g = self->pimpl; |
|
try { |
|
OwnedObject result(g->context()); |
|
return result.relinquish_ownership(); |
|
} |
|
catch(const PyErrOccurred&) { |
|
return nullptr; |
|
} |
|
} |
|
|
|
static int |
|
green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context)) |
|
{ |
|
try { |
|
BorrowedGreenlet(self)->context(nctx); |
|
return 0; |
|
} |
|
catch(const PyErrOccurred&) { |
|
return -1; |
|
} |
|
} |
|
|
|
|
|
static PyObject* |
|
green_getframe(PyGreenlet* self, void* UNUSED(context)) |
|
{ |
|
const PythonState::OwnedFrame& top_frame = BorrowedGreenlet(self)->top_frame(); |
|
return top_frame.acquire_or_None(); |
|
} |
|
|
|
|
|
static PyObject* |
|
green_getstate(PyGreenlet* self) |
|
{ |
|
PyErr_Format(PyExc_TypeError, |
|
"cannot serialize '%s' object", |
|
Py_TYPE(self)->tp_name); |
|
return nullptr; |
|
} |
|
|
|
static PyObject* |
|
green_repr(PyGreenlet* _self) |
|
{ |
|
BorrowedGreenlet self(_self); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PyObject* result; |
|
int never_started = !self->started() && !self->active(); |
|
|
|
const char* const tp_name = Py_TYPE(self)->tp_name; |
|
|
|
if (_green_not_dead(self)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char* state_in_thread; |
|
if (self->was_running_in_dead_thread()) { |
|
|
|
|
|
|
|
|
|
|
|
state_in_thread = " (thread exited)"; |
|
} |
|
else { |
|
state_in_thread = GET_THREAD_STATE().state().is_current(self) |
|
? " current" |
|
: (self->started() ? " suspended" : ""); |
|
} |
|
result = PyUnicode_FromFormat( |
|
"<%s object at %p (otid=%p)%s%s%s%s>", |
|
tp_name, |
|
self.borrow_o(), |
|
self->thread_state(), |
|
state_in_thread, |
|
self->active() ? " active" : "", |
|
never_started ? " pending" : " started", |
|
self->main() ? " main" : "" |
|
); |
|
} |
|
else { |
|
result = PyUnicode_FromFormat( |
|
"<%s object at %p (otid=%p) %sdead>", |
|
tp_name, |
|
self.borrow_o(), |
|
self->thread_state(), |
|
self->was_running_in_dead_thread() |
|
? "(thread exited) " |
|
: "" |
|
); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
static PyMethodDef green_methods[] = { |
|
{ |
|
.ml_name="switch", |
|
.ml_meth=reinterpret_cast<PyCFunction>(green_switch), |
|
.ml_flags=METH_VARARGS | METH_KEYWORDS, |
|
.ml_doc=green_switch_doc |
|
}, |
|
{.ml_name="throw", .ml_meth=(PyCFunction)green_throw, .ml_flags=METH_VARARGS, .ml_doc=green_throw_doc}, |
|
{.ml_name="__getstate__", .ml_meth=(PyCFunction)green_getstate, .ml_flags=METH_NOARGS, .ml_doc=NULL}, |
|
{.ml_name=NULL, .ml_meth=NULL} |
|
}; |
|
|
|
static PyGetSetDef green_getsets[] = { |
|
|
|
{.name="__dict__", .get=(getter)green_getdict, .set=(setter)green_setdict}, |
|
{.name="run", .get=(getter)green_getrun, .set=(setter)green_setrun}, |
|
{.name="parent", .get=(getter)green_getparent, .set=(setter)green_setparent}, |
|
{.name="gr_frame", .get=(getter)green_getframe }, |
|
{ |
|
.name="gr_context", |
|
.get=(getter)green_getcontext, |
|
.set=(setter)green_setcontext |
|
}, |
|
{.name="dead", .get=(getter)green_getdead}, |
|
{.name="_stack_saved", .get=(getter)green_get_stack_saved}, |
|
{.name=NULL} |
|
}; |
|
|
|
static PyMemberDef green_members[] = { |
|
{.name=NULL} |
|
}; |
|
|
|
static PyNumberMethods green_as_number = { |
|
.nb_bool=(inquiry)green_bool, |
|
}; |
|
|
|
|
|
PyTypeObject PyGreenlet_Type = { |
|
.ob_base=PyVarObject_HEAD_INIT(NULL, 0) |
|
.tp_name="greenlet.greenlet", |
|
.tp_basicsize=sizeof(PyGreenlet), |
|
|
|
.tp_dealloc=(destructor)green_dealloc, |
|
.tp_repr=(reprfunc)green_repr, |
|
.tp_as_number=&green_as_number, |
|
.tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, |
|
.tp_doc="greenlet(run=None, parent=None) -> greenlet\n\n" |
|
"Creates a new greenlet object (without running it).\n\n" |
|
" - *run* -- The callable to invoke.\n" |
|
" - *parent* -- The parent greenlet. The default is the current " |
|
"greenlet.", |
|
.tp_traverse=(traverseproc)green_traverse, |
|
.tp_clear=(inquiry)green_clear, |
|
.tp_weaklistoffset=offsetof(PyGreenlet, weakreflist), |
|
|
|
.tp_methods=green_methods, |
|
.tp_members=green_members, |
|
.tp_getset=green_getsets, |
|
.tp_dictoffset=offsetof(PyGreenlet, dict), |
|
.tp_init=(initproc)green_init, |
|
.tp_alloc=PyType_GenericAlloc, |
|
.tp_new=(newfunc)green_new, |
|
.tp_free=PyObject_GC_Del, |
|
.tp_is_gc=(inquiry)green_is_gc, |
|
}; |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|