Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
PyGreenlet.cpp842 linesDownload Raw Back to greenlet
1/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
2#ifndef PYGREENLET_CPP
3#define PYGREENLET_CPP
4/*****************
5The Python slot functions for TGreenlet.
6 */
7
8
9#define PY_SSIZE_T_CLEAN
10#include <Python.h>
11#include "structmember.h" // PyMemberDef
12
13#include "greenlet_internal.hpp"
14#include "TThreadStateDestroy.cpp"
15#include "TGreenlet.hpp"
16// #include "TUserGreenlet.cpp"
17// #include "TMainGreenlet.cpp"
18// #include "TBrokenGreenlet.cpp"
19
20
21#include "greenlet_refs.hpp"
22#include "greenlet_slp_switch.hpp"
23
24#include "greenlet_thread_support.hpp"
25#include "TGreenlet.hpp"
26
27#include "TGreenletGlobals.cpp"
28#include "TThreadStateDestroy.cpp"
29#include "PyGreenlet.hpp"
30// #include "TGreenlet.cpp"
31
32// #include "TExceptionState.cpp"
33// #include "TPythonState.cpp"
34// #include "TStackState.cpp"
35
36using greenlet::LockGuard;
37using greenlet::LockInitError;
38using greenlet::PyErrOccurred;
39using greenlet::Require;
40
41using greenlet::g_handle_exit;
42using greenlet::single_result;
43
44using greenlet::Greenlet;
45using greenlet::UserGreenlet;
46using greenlet::MainGreenlet;
47using greenlet::BrokenGreenlet;
48using greenlet::ThreadState;
49using greenlet::PythonState;
50using greenlet::refs::PyCriticalObjectSection;
51
52
53static PyGreenlet*
54green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds))
55{
56    PyGreenlet* o =
57        (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict);
58    if (o) {
59        // Recall: borrowing or getting the current greenlet
60        // causes the "deleteme list" to get cleared. So constructing a greenlet
61        // can do things like cause other greenlets to get finalized.
62        UserGreenlet* c = new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current());
63        assert(Py_REFCNT(o) == 1);
64        // Also: This looks like a memory leak, but isn't.
65        // Constructing the C++ object assigns it to the pimpl pointer
66        // of the Python object (o); we'll need that later.
67        assert(c == o->pimpl);
68    }
69    return o;
70}
71
72
73// green_init is used in the tp_init slot. So it's important that
74// it can be called directly from CPython. Thus, we don't use
75// BorrowedGreenlet and BorrowedObject --- although in theory
76// these should be binary layout compatible, that may not be
77// guaranteed to be the case (32-bit linux ppc possibly).
78static int
79green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs)
80{
81    PyArgParseParam run;
82    PyArgParseParam nparent;
83    static const char* kwlist[] = {
84        "run",
85        "parent",
86        NULL
87    };
88
89    // recall: The O specifier does NOT increase the reference count.
90    if (!PyArg_ParseTupleAndKeywords(
91             args, kwargs, "|OO:green", (char**)kwlist, &run, &nparent)) {
92        return -1;
93    }
94
95    if (run) {
96        if (green_setrun(self, run, NULL)) {
97            return -1;
98        }
99    }
100    if (nparent && !nparent.is_None()) {
101        return green_setparent(self, nparent, NULL);
102    }
103    return 0;
104}
105
106
107
108static int
109green_traverse(PyGreenlet* self, visitproc visit, void* arg)
110{
111    // We must only visit referenced objects, i.e. only objects
112    // Py_INCREF'ed by this greenlet (directly or indirectly):
113    //
114    // - stack_prev is not visited: holds previous stack pointer, but it's not
115    //    referenced
116    // - frames are not visited as we don't strongly reference them;
117    //    alive greenlets are not garbage collected
118    //    anyway. This can be a problem, however, if this greenlet is
119    //    never allowed to finish, and is referenced from the frame: we
120    //    have an uncollectible cycle in that case. Note that the
121    //    frame object itself is also frequently not even tracked by the GC
122    //    starting with Python 3.7 (frames are allocated by the
123    //    interpreter untracked, and only become tracked when their
124    //    evaluation is finished if they have a refcount > 1). All of
125    //    this is to say that we should probably strongly reference
126    //    the frame object. Doing so, while always allowing GC on a
127    //    greenlet, solves several leaks for us.
128
129    Py_VISIT(self->dict);
130    if (!self->pimpl) {
131        // Hmm. I have seen this at interpreter shutdown time,
132        // I think. That's very odd because this doesn't go away until
133        // we're ``green_dealloc()``, at which point we shouldn't be
134        // traversed anymore.
135        return 0;
136    }
137
138    return self->pimpl->tp_traverse(visit, arg);
139}
140
141static int
142green_is_gc(PyObject* _self)
143{
144    BorrowedGreenlet self(_self);
145    int result = 0;
146    /* Main greenlet can be garbage collected since it can only
147       become unreachable if the underlying thread exited.
148       Active greenlets --- including those that are suspended ---
149       cannot be garbage collected, however.
150    */
151    if (self->main() || !self->active()) {
152        result = 1;
153    }
154    // The main greenlet pointer will eventually go away after the thread dies.
155    if (self->was_running_in_dead_thread()) {
156        // Our thread is dead! We can never run again. Might as well
157        // GC us. Note that if a tuple containing only us and other
158        // immutable objects had been scanned before this, when we
159        // would have returned 0, the tuple will take itself out of GC
160        // tracking and never be investigated again. So that could
161        // result in both us and the tuple leaking due to an
162        // unreachable/uncollectible reference. The same goes for
163        // dictionaries.
164        //
165        // It's not a great idea to be changing our GC state on the
166        // fly.
167        result = 1;
168    }
169    return result;
170}
171
172
173static int
174green_clear(PyGreenlet* self)
175{
176    /* Greenlet is only cleared if it is about to be collected.
177       Since active greenlets are not garbage collectable, we can
178       be sure that, even if they are deallocated during clear,
179       nothing they reference is in unreachable or finalizers,
180       so even if it switches we are relatively safe. */
181    // XXX: Are we responsible for clearing weakrefs here?
182    Py_CLEAR(self->dict);
183    return self->pimpl->tp_clear();
184}
185
186/**
187 * Returns 0 on failure (the object was resurrected) or 1 on success.
188 **/
189static int
190_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
191{
192    // During interpreter finalization, we cannot safely throw GreenletExit
193    // into the greenlet. Doing so calls g_switch(), which performs a stack
194    // switch and runs Python code via _PyEval_EvalFrameDefault. On Python
195    // < 3.11, executing Python code in a partially-torn-down interpreter
196    // leads to SIGSEGV (greenlet 3.x) or SIGABRT (greenlet 2.x).
197    //
198    // Python 3.11+ restructured interpreter finalization internals (frame
199    // representation, data stack management, recursion tracking) so that
200    // g_switch() during finalization is safe. On older Pythons, we simply
201    // mark the greenlet dead without throwing, which avoids the crash at
202    // the cost of not running any cleanup code inside the greenlet.
203    //
204    // See: https://github.com/python-greenlet/greenlet/issues/411
205    //      https://github.com/python-greenlet/greenlet/issues/351
206    if (greenlet::IsShuttingDown()) {
207        self->murder_in_place();
208        return 1;
209    }
210
211    /* Hacks hacks hacks copied from instance_dealloc() */
212    /* Temporarily resurrect the greenlet. */
213    assert(self.REFCNT() == 0);
214    Py_SET_REFCNT(self.borrow(), 1);
215    /* Save the current exception, if any. */
216    PyErrPieces saved_err;
217    try {
218        // BY THE TIME WE GET HERE, the state may actually be going
219        // away
220        // if we're shutting down the interpreter and freeing thread
221        // entries,
222        // this could result in freeing greenlets that were leaked. So
223        // we can't try to read the state.
224        self->deallocing_greenlet_in_thread(
225              self->thread_state()
226              ? static_cast<ThreadState*>(GET_THREAD_STATE())
227              : nullptr);
228    }
229    catch (const PyErrOccurred&) {
230        PyErr_WriteUnraisable(self.borrow_o());
231        /* XXX what else should we do? */
232    }
233    /* Check for no resurrection must be done while we keep
234     * our internal reference, otherwise PyFile_WriteObject
235     * causes recursion if using Py_INCREF/Py_DECREF
236     */
237    if (self.REFCNT() == 1 && self->active()) {
238        /* Not resurrected, but still not dead!
239           XXX what else should we do? we complain. */
240        PyObject* f = PySys_GetObject("stderr");
241        Py_INCREF(self.borrow_o()); /* leak! */
242        if (f != NULL) {
243            // PySys_GetObject returns a borrowed ref which could go
244            // away when we run arbitrary code, as we do for any of
245            // the ``PyFile_Write`` APIs.
246            Py_INCREF(f);
247            // Note that we're not handling errors here. They either
248            // work or they don't, and any exception they raised will
249            // be replaced by PyErrRestore.
250            PyFile_WriteString("GreenletExit did not kill ", f);
251            PyFile_WriteObject(self.borrow_o(), f, 0);
252            PyFile_WriteString("\n", f);
253            Py_DECREF(f);
254        }
255    }
256    /* Restore the saved exception. */
257    saved_err.PyErrRestore();
258    /* Undo the temporary resurrection; can't use DECREF here,
259     * it would cause a recursive call.
260     */
261    assert(self.REFCNT() > 0);
262
263    Py_ssize_t refcnt = self.REFCNT() - 1;
264    Py_SET_REFCNT(self.borrow_o(), refcnt);
265    if (refcnt != 0) {
266        /* Resurrected! */
267        _Py_NewReference(self.borrow_o());
268        Py_SET_REFCNT(self.borrow_o(), refcnt);
269        /* Better to use tp_finalizer slot (PEP 442)
270         * and call ``PyObject_CallFinalizerFromDealloc``,
271         * but that's only supported in Python 3.4+; see
272         * Modules/_io/iobase.c for an example.
273         * TODO: We no longer run on anything that old, switch to finalizers.
274         *
275         * The following approach is copied from iobase.c in CPython 2.7.
276         * (along with much of this function in general). Here's their
277         * comment:
278         *
279         * When called from a heap type's dealloc, the type will be
280         * decref'ed on return (see e.g. subtype_dealloc in typeobject.c).
281         *
282         * On free-threaded builds of CPython, the type is meant to be immortal
283         * so we probably shouldn't mess with this? See
284         * test_issue_245_reference_counting_subclass_no_threads
285         */
286        if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) {
287            Py_INCREF(self.TYPE());
288        }
289
290        PyObject_GC_Track((PyObject*)self);
291
292        GREENLET_Py_DEC_REFTOTAL;
293#ifdef COUNT_ALLOCS
294        --Py_TYPE(self)->tp_frees;
295        --Py_TYPE(self)->tp_allocs;
296#endif /* COUNT_ALLOCS */
297        return 0;
298    }
299    return 1;
300}
301
302
303static void
304green_dealloc(PyGreenlet* self)
305{
306    PyObject_GC_UnTrack(self);
307    BorrowedGreenlet me(self);
308    if (me->active()
309        && me->started()
310        && !me->main()) {
311        if (!_green_dealloc_kill_started_non_main_greenlet(me)) {
312            return;
313        }
314    }
315
316    if (self->weakreflist != NULL) {
317        PyObject_ClearWeakRefs((PyObject*)self);
318    }
319    Py_CLEAR(self->dict);
320
321    if (self->pimpl) {
322        // In case deleting this, which frees some memory,
323        // somehow winds up calling back into us. That's usually a
324        //bug in our code.
325        Greenlet* p = self->pimpl;
326        self->pimpl = nullptr;
327        delete p;
328    }
329    // and finally we're done. self is now invalid.
330    Py_TYPE(self)->tp_free((PyObject*)self);
331}
332
333
334
335static OwnedObject
336internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces)
337{
338    PyObject* result = nullptr;
339    err_pieces.PyErrRestore();
340    assert(PyErr_Occurred());
341    if (self->started() && !self->active()) {
342        /* dead greenlet: turn GreenletExit into a regular return */
343        result = g_handle_exit(OwnedObject()).relinquish_ownership();
344    }
345    self->args() <<= result;
346
347    return single_result(self->g_switch());
348}
349
350
351
352PyDoc_STRVAR(
353    green_switch_doc,
354    "switch(*args, **kwargs)\n"
355    "\n"
356    "Switch execution to this greenlet.\n"
357    "\n"
358    "If this greenlet has never been run, then this greenlet\n"
359    "will be switched to using the body of ``self.run(*args, **kwargs)``.\n"
360    "\n"
361    "If the greenlet is active (has been run, but was switch()'ed\n"
362    "out before leaving its run function), then this greenlet will\n"
363    "be resumed and the return value to its switch call will be\n"
364    "None if no arguments are given, the given argument if one\n"
365    "argument is given, or the args tuple and keyword args dict if\n"
366    "multiple arguments are given.\n"
367    "\n"
368    "If the greenlet is dead, or is the current greenlet then this\n"
369    "function will simply return the arguments using the same rules as\n"
370    "above.\n");
371
372static PyObject*
373green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs)
374{
375    // Our use of Greenlet::args() makes this method non-reentrant.
376    // Therefore, check to be sure the switch will be allowed ---
377    // we're calling from the same thread that ``self`` belongs to ---
378    // BEFORE doing anything with args(). If we don't do this, we can
379    // find args() getting clobbered by switches that will never
380    // succeed.
381    //
382    // TODO: We're only doing this for free-threaded builds because
383    // those are the only ones that have demonstrated an issue,
384    // trusting our later checks in g_switch to perform the same
385    // function and the GIL to keep us from being reentered in regular
386    // builds. BUT should we always do this as an extra measure of
387    // safety in case we run code at unexpected times (e.g., a GC?)
388#ifdef Py_GIL_DISABLED
389    try {
390        self->pimpl->check_switch_allowed();
391    }
392    catch (const PyErrOccurred&) {
393        return nullptr;
394    }
395#endif
396
397
398    using greenlet::SwitchingArgs;
399    SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs));
400    self->pimpl->may_switch_away();
401    self->pimpl->args() <<= switch_args;
402
403    // If we're switching out of a greenlet, and that switch is the
404    // last thing the greenlet does, the greenlet ought to be able to
405    // go ahead and die at that point. Currently, someone else must
406    // manually switch back to the greenlet so that we "fall off the
407    // end" and can perform cleanup. You'd think we'd be able to
408    // figure out that this is happening using the frame's ``f_lasti``
409    // member, which is supposed to be an index into
410    // ``frame->f_code->co_code``, the bytecode string. However, in
411    // recent interpreters, ``f_lasti`` tends not to be updated thanks
412    // to things like the PREDICT() macros in ceval.c. So it doesn't
413    // really work to do that in many cases. For example, the Python
414    // code:
415    //     def run():
416    //         greenlet.getcurrent().parent.switch()
417    // produces bytecode of len 16, with the actual call to switch()
418    // being at index 10 (in Python 3.10). However, the reported
419    // ``f_lasti`` we actually see is...5! (Which happens to be the
420    // second byte of the CALL_METHOD op for ``getcurrent()``).
421
422    try {
423        OwnedObject result(single_result(self->pimpl->g_switch()));
424#ifndef NDEBUG
425        // Note that the current greenlet isn't necessarily self. If self
426        // finished, we went to one of its parents.
427        assert(!self->pimpl->args());
428
429        const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current();
430        // It's possible it's never been switched to.
431        assert(!current->args());
432#endif
433        PyObject* p = result.relinquish_ownership();
434
435        if (!p && !PyErr_Occurred()) {
436            // This shouldn't be happening anymore, so the asserts
437            // are there for debug builds. Non-debug builds
438            // crash "gracefully" in this case, although there is an
439            // argument to be made for killing the process in all
440            // cases --- for this to be the case, our switches
441            // probably nested in an incorrect way, so the state is
442            // suspicious. Nothing should be corrupt though, just
443            // confused at the Python level. Letting this propagate is
444            // probably good enough.
445            assert(p || PyErr_Occurred());
446            throw PyErrOccurred(
447                mod_globs->PyExc_GreenletError,
448                "Greenlet.switch() returned NULL without an exception set."
449            );
450        }
451        return p;
452    }
453    catch(const PyErrOccurred&) {
454        return nullptr;
455    }
456}
457
458PyDoc_STRVAR(
459    green_throw_doc,
460    "Switches execution to this greenlet, but immediately raises the\n"
461    "given exception in this greenlet.  If no argument is provided, the "
462    "exception\n"
463    "defaults to `greenlet.GreenletExit`.  The normal exception\n"
464    "propagation rules apply, as described for `switch`.  Note that calling "
465    "this\n"
466    "method is almost equivalent to the following::\n"
467    "\n"
468    "    def raiser():\n"
469    "        raise typ, val, tb\n"
470    "    g_raiser = greenlet(raiser, parent=g)\n"
471    "    g_raiser.switch()\n"
472    "\n"
473    "except that this trick does not work for the\n"
474    "`greenlet.GreenletExit` exception, which would not propagate\n"
475    "from ``g_raiser`` to ``g``.\n");
476
477static PyObject*
478green_throw(PyGreenlet* self, PyObject* args)
479{
480    // See green_switch for why we call this early.
481#ifdef Py_GIL_DISABLED
482    try {
483        self->pimpl->check_switch_allowed();
484    }
485    catch (const PyErrOccurred&) {
486        return nullptr;
487    }
488#endif
489
490    PyArgParseParam typ(mod_globs->PyExc_GreenletExit);
491    PyArgParseParam val;
492    PyArgParseParam tb;
493
494    if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) {
495        return nullptr;
496    }
497
498    assert(typ.borrow() || val.borrow());
499
500    self->pimpl->may_switch_away();
501    try {
502        // Both normalizing the error and the actual throw_greenlet
503        // could throw PyErrOccurred.
504        PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow());
505
506        return internal_green_throw(self, err_pieces).relinquish_ownership();
507    }
508    catch (const PyErrOccurred&) {
509        return nullptr;
510    }
511}
512
513static int
514green_bool(PyGreenlet* self)
515{
516    return self->pimpl->active();
517}
518
519/**
520 * CAUTION: Allocates memory, may run GC and arbitrary Python code.
521 */
522static PyObject*
523green_getdict(PyGreenlet* self, void* UNUSED(context))
524{
525    PyCriticalObjectSection cs(self);
526    if (self->dict == NULL) {
527        self->dict = PyDict_New();
528        if (self->dict == NULL) {
529            return NULL;
530        }
531    }
532    Py_INCREF(self->dict);
533    return self->dict;
534}
535
536static int
537green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context))
538{
539    if (val == NULL) {
540        PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted");
541        return -1;
542    }
543    if (!PyDict_Check(val)) {
544        PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary");
545        return -1;
546    }
547    PyCriticalObjectSection cs(self);
548    PyObject* tmp = self->dict;
549    Py_INCREF(val);
550    self->dict = val;
551    Py_XDECREF(tmp);
552    return 0;
553}
554
555static bool
556_green_not_dead(BorrowedGreenlet self)
557{
558    // XXX: Where else should we do this?
559    // Probably on entry to most Python-facing functions?
560    if (self->was_running_in_dead_thread()) {
561        self->deactivate_and_free();
562        return false;
563    }
564    return self->active() || !self->started();
565}
566
567
568static PyObject*
569green_getdead(PyGreenlet* self, void* UNUSED(context))
570{
571    PyCriticalObjectSection cs(self);
572    if (_green_not_dead(self)) {
573        Py_RETURN_FALSE;
574    }
575    else {
576        Py_RETURN_TRUE;
577    }
578}
579
580static PyObject*
581green_get_stack_saved(PyGreenlet* self, void* UNUSED(context))
582{
583    return PyLong_FromSsize_t(self->pimpl->stack_saved());
584}
585
586
587static PyObject*
588green_getrun(PyGreenlet* self, void* UNUSED(context))
589{
590    PyCriticalObjectSection cs(self);
591    try {
592        OwnedObject result(BorrowedGreenlet(self)->run());
593        return result.relinquish_ownership();
594    }
595    catch(const PyErrOccurred&) {
596        return nullptr;
597    }
598}
599
600
601static int
602green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context))
603{
604    PyCriticalObjectSection cs(self);
605    try {
606        BorrowedGreenlet(self)->run(nrun);
607        return 0;
608    }
609    catch(const PyErrOccurred&) {
610        return -1;
611    }
612}
613
614static PyObject*
615green_getparent(PyGreenlet* self, void* UNUSED(context))
616{
617    PyCriticalObjectSection cs(self);
618    return BorrowedGreenlet(self)->parent().acquire_or_None();
619}
620
621
622static int
623green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context))
624{
625    PyCriticalObjectSection cs(self);
626    try {
627        BorrowedGreenlet(self)->parent(nparent);
628    }
629    catch(const PyErrOccurred&) {
630        return -1;
631    }
632    return 0;
633}
634
635
636static PyObject*
637green_getcontext(const PyGreenlet* self, void* UNUSED(context))
638{
639    PyCriticalObjectSection cs(self);
640    const Greenlet *const g = self->pimpl;
641    try {
642        OwnedObject result(g->context());
643        return result.relinquish_ownership();
644    }
645    catch(const PyErrOccurred&) {
646        return nullptr;
647    }
648}
649
650static int
651green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context))
652{
653    PyCriticalObjectSection cs(self);
654    try {
655        BorrowedGreenlet(self)->context(nctx);
656        return 0;
657    }
658    catch(const PyErrOccurred&) {
659        return -1;
660    }
661}
662
663
664static PyObject*
665green_getframe(PyGreenlet* self, void* UNUSED(context))
666{
667    PyCriticalObjectSection cs(self);
668    const PythonState::OwnedFrame& top_frame = BorrowedGreenlet(self)->top_frame();
669    return top_frame.acquire_or_None();
670}
671
672
673static PyObject*
674green_getstate(PyGreenlet* self)
675{
676    PyErr_Format(PyExc_TypeError,
677                 "cannot serialize '%s' object",
678                 Py_TYPE(self)->tp_name);
679    return nullptr;
680}
681
682static PyObject*
683green_repr(PyGreenlet* _self)
684{
685    BorrowedGreenlet self(_self);
686    /*
687      Return a string like
688      <greenlet.greenlet at 0xdeadbeef [current][active started]|dead main>
689
690      The handling of greenlets across threads is not super good.
691      We mostly use the internal definitions of these terms, but they
692      generally should make sense to users as well.
693     */
694    PyObject* result;
695    int never_started = !self->started() && !self->active();
696
697    const char* const tp_name = Py_TYPE(self)->tp_name;
698
699    if (_green_not_dead(self)) {
700        /* XXX: The otid= is almost useless because you can't correlate it to
701         any thread identifier exposed to Python. We could use
702         PyThreadState_GET()->thread_id, but we'd need to save that in the
703         greenlet, or save the whole PyThreadState object itself.
704
705         As it stands, its only useful for identifying greenlets from the same thread.
706        */
707        const char* state_in_thread;
708        if (self->was_running_in_dead_thread()) {
709            // The thread it was running in is dead!
710            // This can happen, especially at interpreter shut down.
711            // It complicates debugging output because it may be
712            // impossible to access the current thread state at that
713            // time. Thus, don't access the current thread state.
714            state_in_thread = " (thread exited)";
715        }
716        else {
717            state_in_thread = GET_THREAD_STATE().state().is_current(self)
718                ? " current"
719                : (self->started() ? " suspended" : "");
720        }
721        result = PyUnicode_FromFormat(
722            "<%s object at %p (otid=%p)%s%s%s%s>",
723            tp_name,
724            self.borrow_o(),
725            self->thread_state(),
726            state_in_thread,
727            self->active() ? " active" : "",
728            never_started ? " pending" : " started",
729            self->main() ? " main" : ""
730        );
731    }
732    else {
733        result = PyUnicode_FromFormat(
734            "<%s object at %p (otid=%p) %sdead>",
735            tp_name,
736            self.borrow_o(),
737            self->thread_state(),
738            self->was_running_in_dead_thread()
739            ? "(thread exited) "
740            : ""
741            );
742    }
743
744    return result;
745}
746
747
748static PyMethodDef green_methods[] = {
749    {
750      .ml_name="switch",
751      .ml_meth=reinterpret_cast<PyCFunction>(green_switch),
752      .ml_flags=METH_VARARGS | METH_KEYWORDS,
753      .ml_doc=green_switch_doc
754    },
755    {.ml_name="throw", .ml_meth=(PyCFunction)green_throw, .ml_flags=METH_VARARGS, .ml_doc=green_throw_doc},
756    {.ml_name="__getstate__", .ml_meth=(PyCFunction)green_getstate, .ml_flags=METH_NOARGS, .ml_doc=NULL},
757    {.ml_name=NULL, .ml_meth=NULL} /* sentinel */
758};
759
760static PyGetSetDef green_getsets[] = {
761    /* name, getter, setter, doc, context pointer */
762    {.name="__dict__", .get=(getter)green_getdict, .set=(setter)green_setdict},
763    {.name="run", .get=(getter)green_getrun, .set=(setter)green_setrun},
764    {.name="parent", .get=(getter)green_getparent, .set=(setter)green_setparent},
765    {.name="gr_frame", .get=(getter)green_getframe },
766    {
767      .name="gr_context",
768      .get=(getter)green_getcontext,
769      .set=(setter)green_setcontext
770    },
771    {.name="dead", .get=(getter)green_getdead},
772    {.name="_stack_saved", .get=(getter)green_get_stack_saved},
773    {.name=NULL}
774};
775
776static PyMemberDef green_members[] = {
777    {.name=NULL}
778};
779
780static PyNumberMethods green_as_number = {
781  .nb_bool=(inquiry)green_bool,
782};
783
784
785PyTypeObject PyGreenlet_Type = {
786    .ob_base=PyVarObject_HEAD_INIT(NULL, 0)
787    .tp_name="greenlet.greenlet", /* tp_name */
788    .tp_basicsize=sizeof(PyGreenlet),  /* tp_basicsize */
789    /* methods */
790    .tp_dealloc=(destructor)green_dealloc, /* tp_dealloc */
791    .tp_repr=(reprfunc)green_repr,      /* tp_repr */
792    .tp_as_number=&green_as_number,          /* tp_as _number*/
793    .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
794    .tp_doc="greenlet(run=None, parent=None) -> greenlet\n\n"
795    "Creates a new greenlet object (without running it).\n\n"
796    " - *run* -- The callable to invoke.\n"
797    " - *parent* -- The parent greenlet. The default is the current "
798    "greenlet.",                        /* tp_doc */
799    .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */
800    .tp_clear=(inquiry)green_clear,         /* tp_clear */
801    .tp_weaklistoffset=offsetof(PyGreenlet, weakreflist),  /* tp_weaklistoffset */
802
803    .tp_methods=green_methods,                      /* tp_methods */
804    .tp_members=green_members,                      /* tp_members */
805    .tp_getset=green_getsets,                      /* tp_getset */
806    .tp_dictoffset=offsetof(PyGreenlet, dict),         /* tp_dictoffset */
807    .tp_init=(initproc)green_init,               /* tp_init */
808    .tp_alloc=PyType_GenericAlloc,                  /* tp_alloc */
809    .tp_new=(newfunc)green_new,                          /* tp_new */
810    .tp_free=PyObject_GC_Del,                   /* tp_free */
811#ifndef Py_GIL_DISABLED
812/*
813  We may have been handling this wrong all along.
814
815  It shows as a problem with the GIL disabled. In builds of 3.14 with
816  assertions enabled, we break the garbage collector if we *ever*
817  return false from this function. The docs say this is to distinguish
818  some objects that are collectable vs some that are not, specifically
819  giving the example of PyTypeObject as the only place this is done,
820  where it distinguishes between static types like this one (allocated
821  by the C runtime at load time) and dynamic heap types (created at
822  runtime as objects). With the GIL disabled, all allocations that are
823  potentially collectable go in the mimalloc heap, and the collector
824  asserts that tp_is_gc() is true for them as it walks through the
825  heap object by object. Since we set the Py_TPFLAGS_HAS_GC bit, we
826  are always allocated in that mimalloc heap, so we must always be
827  collectable.
828
829  XXX: TODO: Could this be responsible for some apparent leaks, even
830  on GIL builds, at least in 3.14? See if we can catch an assertion
831  failure in the GC on regular 3.14 as well.
832 */
833    .tp_is_gc=(inquiry)green_is_gc,         /* tp_is_gc */
834#endif
835};
836
837#endif
838
839// Local Variables:
840// flycheck-clang-include-path: ("/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8")
841// End:
842 
codekingpro/portable-devtools · Team Ai