Update 2025-04-24_11:44:19
This commit is contained in:
738
venv/lib/python3.11/site-packages/greenlet/PyGreenlet.cpp
Normal file
738
venv/lib/python3.11/site-packages/greenlet/PyGreenlet.cpp
Normal file
@ -0,0 +1,738 @@
|
||||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
#ifndef PYGREENLET_CPP
|
||||
#define PYGREENLET_CPP
|
||||
/*****************
|
||||
The Python slot functions for TGreenlet.
|
||||
*/
|
||||
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include "structmember.h" // PyMemberDef
|
||||
|
||||
#include "greenlet_internal.hpp"
|
||||
#include "TThreadStateDestroy.cpp"
|
||||
#include "TGreenlet.hpp"
|
||||
// #include "TUserGreenlet.cpp"
|
||||
// #include "TMainGreenlet.cpp"
|
||||
// #include "TBrokenGreenlet.cpp"
|
||||
|
||||
|
||||
#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"
|
||||
// #include "TGreenlet.cpp"
|
||||
|
||||
// #include "TExceptionState.cpp"
|
||||
// #include "TPythonState.cpp"
|
||||
// #include "TStackState.cpp"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// green_init is used in the tp_init slot. So it's important that
|
||||
// it can be called directly from CPython. Thus, we don't use
|
||||
// BorrowedGreenlet and BorrowedObject --- although in theory
|
||||
// these should be binary layout compatible, that may not be
|
||||
// guaranteed to be the case (32-bit linux ppc possibly).
|
||||
static int
|
||||
green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs)
|
||||
{
|
||||
PyArgParseParam run;
|
||||
PyArgParseParam nparent;
|
||||
static const char* kwlist[] = {
|
||||
"run",
|
||||
"parent",
|
||||
NULL
|
||||
};
|
||||
|
||||
// recall: The O specifier does NOT increase the reference count.
|
||||
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)
|
||||
{
|
||||
// We must only visit referenced objects, i.e. only objects
|
||||
// Py_INCREF'ed by this greenlet (directly or indirectly):
|
||||
//
|
||||
// - stack_prev is not visited: holds previous stack pointer, but it's not
|
||||
// referenced
|
||||
// - frames are not visited as we don't strongly reference them;
|
||||
// alive greenlets are not garbage collected
|
||||
// anyway. This can be a problem, however, if this greenlet is
|
||||
// never allowed to finish, and is referenced from the frame: we
|
||||
// have an uncollectible cycle in that case. Note that the
|
||||
// frame object itself is also frequently not even tracked by the GC
|
||||
// starting with Python 3.7 (frames are allocated by the
|
||||
// interpreter untracked, and only become tracked when their
|
||||
// evaluation is finished if they have a refcount > 1). All of
|
||||
// this is to say that we should probably strongly reference
|
||||
// the frame object. Doing so, while always allowing GC on a
|
||||
// greenlet, solves several leaks for us.
|
||||
|
||||
Py_VISIT(self->dict);
|
||||
if (!self->pimpl) {
|
||||
// Hmm. I have seen this at interpreter shutdown time,
|
||||
// I think. That's very odd because this doesn't go away until
|
||||
// we're ``green_dealloc()``, at which point we shouldn't be
|
||||
// traversed anymore.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return self->pimpl->tp_traverse(visit, arg);
|
||||
}
|
||||
|
||||
static int
|
||||
green_is_gc(PyObject* _self)
|
||||
{
|
||||
BorrowedGreenlet self(_self);
|
||||
int result = 0;
|
||||
/* Main greenlet can be garbage collected since it can only
|
||||
become unreachable if the underlying thread exited.
|
||||
Active greenlets --- including those that are suspended ---
|
||||
cannot be garbage collected, however.
|
||||
*/
|
||||
if (self->main() || !self->active()) {
|
||||
result = 1;
|
||||
}
|
||||
// The main greenlet pointer will eventually go away after the thread dies.
|
||||
if (self->was_running_in_dead_thread()) {
|
||||
// Our thread is dead! We can never run again. Might as well
|
||||
// GC us. Note that if a tuple containing only us and other
|
||||
// immutable objects had been scanned before this, when we
|
||||
// would have returned 0, the tuple will take itself out of GC
|
||||
// tracking and never be investigated again. So that could
|
||||
// result in both us and the tuple leaking due to an
|
||||
// unreachable/uncollectible reference. The same goes for
|
||||
// dictionaries.
|
||||
//
|
||||
// It's not a great idea to be changing our GC state on the
|
||||
// fly.
|
||||
result = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
green_clear(PyGreenlet* self)
|
||||
{
|
||||
/* Greenlet is only cleared if it is about to be collected.
|
||||
Since active greenlets are not garbage collectable, we can
|
||||
be sure that, even if they are deallocated during clear,
|
||||
nothing they reference is in unreachable or finalizers,
|
||||
so even if it switches we are relatively safe. */
|
||||
// XXX: Are we responsible for clearing weakrefs here?
|
||||
Py_CLEAR(self->dict);
|
||||
return self->pimpl->tp_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 on failure (the object was resurrected) or 1 on success.
|
||||
**/
|
||||
static int
|
||||
_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
|
||||
{
|
||||
/* Hacks hacks hacks copied from instance_dealloc() */
|
||||
/* Temporarily resurrect the greenlet. */
|
||||
assert(self.REFCNT() == 0);
|
||||
Py_SET_REFCNT(self.borrow(), 1);
|
||||
/* Save the current exception, if any. */
|
||||
PyErrPieces saved_err;
|
||||
try {
|
||||
// BY THE TIME WE GET HERE, the state may actually be going
|
||||
// away
|
||||
// if we're shutting down the interpreter and freeing thread
|
||||
// entries,
|
||||
// this could result in freeing greenlets that were leaked. So
|
||||
// we can't try to read the state.
|
||||
self->deallocing_greenlet_in_thread(
|
||||
self->thread_state()
|
||||
? static_cast<ThreadState*>(GET_THREAD_STATE())
|
||||
: nullptr);
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
PyErr_WriteUnraisable(self.borrow_o());
|
||||
/* XXX what else should we do? */
|
||||
}
|
||||
/* Check for no resurrection must be done while we keep
|
||||
* our internal reference, otherwise PyFile_WriteObject
|
||||
* causes recursion if using Py_INCREF/Py_DECREF
|
||||
*/
|
||||
if (self.REFCNT() == 1 && self->active()) {
|
||||
/* Not resurrected, but still not dead!
|
||||
XXX what else should we do? we complain. */
|
||||
PyObject* f = PySys_GetObject("stderr");
|
||||
Py_INCREF(self.borrow_o()); /* leak! */
|
||||
if (f != NULL) {
|
||||
PyFile_WriteString("GreenletExit did not kill ", f);
|
||||
PyFile_WriteObject(self.borrow_o(), f, 0);
|
||||
PyFile_WriteString("\n", f);
|
||||
}
|
||||
}
|
||||
/* Restore the saved exception. */
|
||||
saved_err.PyErrRestore();
|
||||
/* Undo the temporary resurrection; can't use DECREF here,
|
||||
* it would cause a recursive call.
|
||||
*/
|
||||
assert(self.REFCNT() > 0);
|
||||
|
||||
Py_ssize_t refcnt = self.REFCNT() - 1;
|
||||
Py_SET_REFCNT(self.borrow_o(), refcnt);
|
||||
if (refcnt != 0) {
|
||||
/* Resurrected! */
|
||||
_Py_NewReference(self.borrow_o());
|
||||
Py_SET_REFCNT(self.borrow_o(), refcnt);
|
||||
/* Better to use tp_finalizer slot (PEP 442)
|
||||
* and call ``PyObject_CallFinalizerFromDealloc``,
|
||||
* but that's only supported in Python 3.4+; see
|
||||
* Modules/_io/iobase.c for an example.
|
||||
*
|
||||
* The following approach is copied from iobase.c in CPython 2.7.
|
||||
* (along with much of this function in general). Here's their
|
||||
* comment:
|
||||
*
|
||||
* When called from a heap type's dealloc, the type will be
|
||||
* decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */
|
||||
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 /* COUNT_ALLOCS */
|
||||
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) {
|
||||
// In case deleting this, which frees some memory,
|
||||
// somehow winds up calling back into us. That's usually a
|
||||
//bug in our code.
|
||||
Greenlet* p = self->pimpl;
|
||||
self->pimpl = nullptr;
|
||||
delete p;
|
||||
}
|
||||
// and finally we're done. self is now invalid.
|
||||
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()) {
|
||||
/* dead greenlet: turn GreenletExit into a regular return */
|
||||
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;
|
||||
|
||||
// If we're switching out of a greenlet, and that switch is the
|
||||
// last thing the greenlet does, the greenlet ought to be able to
|
||||
// go ahead and die at that point. Currently, someone else must
|
||||
// manually switch back to the greenlet so that we "fall off the
|
||||
// end" and can perform cleanup. You'd think we'd be able to
|
||||
// figure out that this is happening using the frame's ``f_lasti``
|
||||
// member, which is supposed to be an index into
|
||||
// ``frame->f_code->co_code``, the bytecode string. However, in
|
||||
// recent interpreters, ``f_lasti`` tends not to be updated thanks
|
||||
// to things like the PREDICT() macros in ceval.c. So it doesn't
|
||||
// really work to do that in many cases. For example, the Python
|
||||
// code:
|
||||
// def run():
|
||||
// greenlet.getcurrent().parent.switch()
|
||||
// produces bytecode of len 16, with the actual call to switch()
|
||||
// being at index 10 (in Python 3.10). However, the reported
|
||||
// ``f_lasti`` we actually see is...5! (Which happens to be the
|
||||
// second byte of the CALL_METHOD op for ``getcurrent()``).
|
||||
|
||||
try {
|
||||
//OwnedObject result = single_result(self->pimpl->g_switch());
|
||||
OwnedObject result(single_result(self->pimpl->g_switch()));
|
||||
#ifndef NDEBUG
|
||||
// Note that the current greenlet isn't necessarily self. If self
|
||||
// finished, we went to one of its parents.
|
||||
assert(!self->pimpl->args());
|
||||
|
||||
const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current();
|
||||
// It's possible it's never been switched to.
|
||||
assert(!current->args());
|
||||
#endif
|
||||
PyObject* p = result.relinquish_ownership();
|
||||
|
||||
if (!p && !PyErr_Occurred()) {
|
||||
// This shouldn't be happening anymore, so the asserts
|
||||
// are there for debug builds. Non-debug builds
|
||||
// crash "gracefully" in this case, although there is an
|
||||
// argument to be made for killing the process in all
|
||||
// cases --- for this to be the case, our switches
|
||||
// probably nested in an incorrect way, so the state is
|
||||
// suspicious. Nothing should be corrupt though, just
|
||||
// confused at the Python level. Letting this propagate is
|
||||
// probably good enough.
|
||||
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 {
|
||||
// Both normalizing the error and the actual throw_greenlet
|
||||
// could throw PyErrOccurred.
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* CAUTION: Allocates memory, may run GC and arbitrary Python code.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
// XXX: Where else should we do this?
|
||||
// Probably on entry to most Python-facing functions?
|
||||
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);
|
||||
/*
|
||||
Return a string like
|
||||
<greenlet.greenlet at 0xdeadbeef [current][active started]|dead main>
|
||||
|
||||
The handling of greenlets across threads is not super good.
|
||||
We mostly use the internal definitions of these terms, but they
|
||||
generally should make sense to users as well.
|
||||
*/
|
||||
PyObject* result;
|
||||
int never_started = !self->started() && !self->active();
|
||||
|
||||
const char* const tp_name = Py_TYPE(self)->tp_name;
|
||||
|
||||
if (_green_not_dead(self)) {
|
||||
/* XXX: The otid= is almost useless because you can't correlate it to
|
||||
any thread identifier exposed to Python. We could use
|
||||
PyThreadState_GET()->thread_id, but we'd need to save that in the
|
||||
greenlet, or save the whole PyThreadState object itself.
|
||||
|
||||
As it stands, its only useful for identifying greenlets from the same thread.
|
||||
*/
|
||||
const char* state_in_thread;
|
||||
if (self->was_running_in_dead_thread()) {
|
||||
// The thread it was running in is dead!
|
||||
// This can happen, especially at interpreter shut down.
|
||||
// It complicates debugging output because it may be
|
||||
// impossible to access the current thread state at that
|
||||
// time. Thus, don't access the current thread state.
|
||||
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} /* sentinel */
|
||||
};
|
||||
|
||||
static PyGetSetDef green_getsets[] = {
|
||||
/* name, getter, setter, doc, context pointer */
|
||||
{.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_name */
|
||||
.tp_basicsize=sizeof(PyGreenlet), /* tp_basicsize */
|
||||
/* methods */
|
||||
.tp_dealloc=(destructor)green_dealloc, /* tp_dealloc */
|
||||
.tp_repr=(reprfunc)green_repr, /* tp_repr */
|
||||
.tp_as_number=&green_as_number, /* tp_as _number*/
|
||||
.tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
.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_doc */
|
||||
.tp_traverse=(traverseproc)green_traverse, /* tp_traverse */
|
||||
.tp_clear=(inquiry)green_clear, /* tp_clear */
|
||||
.tp_weaklistoffset=offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */
|
||||
|
||||
.tp_methods=green_methods, /* tp_methods */
|
||||
.tp_members=green_members, /* tp_members */
|
||||
.tp_getset=green_getsets, /* tp_getset */
|
||||
.tp_dictoffset=offsetof(PyGreenlet, dict), /* tp_dictoffset */
|
||||
.tp_init=(initproc)green_init, /* tp_init */
|
||||
.tp_alloc=PyType_GenericAlloc, /* tp_alloc */
|
||||
.tp_new=(newfunc)green_new, /* tp_new */
|
||||
.tp_free=PyObject_GC_Del, /* tp_free */
|
||||
.tp_is_gc=(inquiry)green_is_gc, /* tp_is_gc */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// Local Variables:
|
||||
// flycheck-clang-include-path: ("/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8")
|
||||
// End:
|
Reference in New Issue
Block a user