Qucs-S S-parameter Viewer & RF Synthesis Tools
Loading...
Searching...
No Matches
PyGreenlet.cpp
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;
39using greenlet::Require;
40
41using greenlet::g_handle_exit;
42using greenlet::single_result;
43
50
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. Constructing the
65 // C++ object assigns it to the pimpl pointer of the Python object (o);
66 // 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{
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
189static int
190_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self)
191{
192 /* Hacks hacks hacks copied from instance_dealloc() */
193 /* Temporarily resurrect the greenlet. */
194 assert(self.REFCNT() == 0);
195 Py_SET_REFCNT(self.borrow(), 1);
196 /* Save the current exception, if any. */
197 PyErrPieces saved_err;
198 try {
199 // BY THE TIME WE GET HERE, the state may actually be going
200 // away
201 // if we're shutting down the interpreter and freeing thread
202 // entries,
203 // this could result in freeing greenlets that were leaked. So
204 // we can't try to read the state.
205 self->deallocing_greenlet_in_thread(
206 self->thread_state()
207 ? static_cast<ThreadState*>(GET_THREAD_STATE())
208 : nullptr);
209 }
210 catch (const PyErrOccurred&) {
211 PyErr_WriteUnraisable(self.borrow_o());
212 /* XXX what else should we do? */
213 }
214 /* Check for no resurrection must be done while we keep
215 * our internal reference, otherwise PyFile_WriteObject
216 * causes recursion if using Py_INCREF/Py_DECREF
217 */
218 if (self.REFCNT() == 1 && self->active()) {
219 /* Not resurrected, but still not dead!
220 XXX what else should we do? we complain. */
221 PyObject* f = PySys_GetObject("stderr");
222 Py_INCREF(self.borrow_o()); /* leak! */
223 if (f != NULL) {
224 PyFile_WriteString("GreenletExit did not kill ", f);
225 PyFile_WriteObject(self.borrow_o(), f, 0);
226 PyFile_WriteString("\n", f);
227 }
228 }
229 /* Restore the saved exception. */
230 saved_err.PyErrRestore();
231 /* Undo the temporary resurrection; can't use DECREF here,
232 * it would cause a recursive call.
233 */
234 assert(self.REFCNT() > 0);
235
236 Py_ssize_t refcnt = self.REFCNT() - 1;
237 Py_SET_REFCNT(self.borrow_o(), refcnt);
238 if (refcnt != 0) {
239 /* Resurrected! */
240 _Py_NewReference(self.borrow_o());
241 Py_SET_REFCNT(self.borrow_o(), refcnt);
242 /* Better to use tp_finalizer slot (PEP 442)
243 * and call ``PyObject_CallFinalizerFromDealloc``,
244 * but that's only supported in Python 3.4+; see
245 * Modules/_io/iobase.c for an example.
246 * TODO: We no longer run on anything that old, switch to finalizers.
247 *
248 * The following approach is copied from iobase.c in CPython 2.7.
249 * (along with much of this function in general). Here's their
250 * comment:
251 *
252 * When called from a heap type's dealloc, the type will be
253 * decref'ed on return (see e.g. subtype_dealloc in typeobject.c).
254 *
255 * On free-threaded builds of CPython, the type is meant to be immortal
256 * so we probably shouldn't mess with this? See
257 * test_issue_245_reference_counting_subclass_no_threads
258 */
259 if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) {
260 Py_INCREF(self.TYPE());
261 }
262
263 PyObject_GC_Track((PyObject*)self);
264
265 GREENLET_Py_DEC_REFTOTAL;
266#ifdef COUNT_ALLOCS
267 --Py_TYPE(self)->tp_frees;
268 --Py_TYPE(self)->tp_allocs;
269#endif /* COUNT_ALLOCS */
270 return 0;
271 }
272 return 1;
273}
274
275
276static void
277green_dealloc(PyGreenlet* self)
278{
279 PyObject_GC_UnTrack(self);
280 BorrowedGreenlet me(self);
281 if (me->active()
282 && me->started()
283 && !me->main()) {
284 if (!_green_dealloc_kill_started_non_main_greenlet(me)) {
285 return;
286 }
287 }
288
289 if (self->weakreflist != NULL) {
290 PyObject_ClearWeakRefs((PyObject*)self);
291 }
292 Py_CLEAR(self->dict);
293
294 if (self->pimpl) {
295 // In case deleting this, which frees some memory,
296 // somehow winds up calling back into us. That's usually a
297 //bug in our code.
298 Greenlet* p = self->pimpl;
299 self->pimpl = nullptr;
300 delete p;
301 }
302 // and finally we're done. self is now invalid.
303 Py_TYPE(self)->tp_free((PyObject*)self);
304}
305
306
307
308static OwnedObject
309internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces)
310{
311 PyObject* result = nullptr;
312 err_pieces.PyErrRestore();
313 assert(PyErr_Occurred());
314 if (self->started() && !self->active()) {
315 /* dead greenlet: turn GreenletExit into a regular return */
316 result = g_handle_exit(OwnedObject()).relinquish_ownership();
317 }
318 self->args() <<= result;
319
320 return single_result(self->g_switch());
321}
322
323
324
325PyDoc_STRVAR(
326 green_switch_doc,
327 "switch(*args, **kwargs)\n"
328 "\n"
329 "Switch execution to this greenlet.\n"
330 "\n"
331 "If this greenlet has never been run, then this greenlet\n"
332 "will be switched to using the body of ``self.run(*args, **kwargs)``.\n"
333 "\n"
334 "If the greenlet is active (has been run, but was switch()'ed\n"
335 "out before leaving its run function), then this greenlet will\n"
336 "be resumed and the return value to its switch call will be\n"
337 "None if no arguments are given, the given argument if one\n"
338 "argument is given, or the args tuple and keyword args dict if\n"
339 "multiple arguments are given.\n"
340 "\n"
341 "If the greenlet is dead, or is the current greenlet then this\n"
342 "function will simply return the arguments using the same rules as\n"
343 "above.\n");
344
345static PyObject*
346green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs)
347{
349 SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs));
350 self->pimpl->may_switch_away();
351 self->pimpl->args() <<= switch_args;
352
353 // If we're switching out of a greenlet, and that switch is the
354 // last thing the greenlet does, the greenlet ought to be able to
355 // go ahead and die at that point. Currently, someone else must
356 // manually switch back to the greenlet so that we "fall off the
357 // end" and can perform cleanup. You'd think we'd be able to
358 // figure out that this is happening using the frame's ``f_lasti``
359 // member, which is supposed to be an index into
360 // ``frame->f_code->co_code``, the bytecode string. However, in
361 // recent interpreters, ``f_lasti`` tends not to be updated thanks
362 // to things like the PREDICT() macros in ceval.c. So it doesn't
363 // really work to do that in many cases. For example, the Python
364 // code:
365 // def run():
366 // greenlet.getcurrent().parent.switch()
367 // produces bytecode of len 16, with the actual call to switch()
368 // being at index 10 (in Python 3.10). However, the reported
369 // ``f_lasti`` we actually see is...5! (Which happens to be the
370 // second byte of the CALL_METHOD op for ``getcurrent()``).
371
372 try {
373 //OwnedObject result = single_result(self->pimpl->g_switch());
374 OwnedObject result(single_result(self->pimpl->g_switch()));
375#ifndef NDEBUG
376 // Note that the current greenlet isn't necessarily self. If self
377 // finished, we went to one of its parents.
378 assert(!self->pimpl->args());
379
380 const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current();
381 // It's possible it's never been switched to.
382 assert(!current->args());
383#endif
384 PyObject* p = result.relinquish_ownership();
385
386 if (!p && !PyErr_Occurred()) {
387 // This shouldn't be happening anymore, so the asserts
388 // are there for debug builds. Non-debug builds
389 // crash "gracefully" in this case, although there is an
390 // argument to be made for killing the process in all
391 // cases --- for this to be the case, our switches
392 // probably nested in an incorrect way, so the state is
393 // suspicious. Nothing should be corrupt though, just
394 // confused at the Python level. Letting this propagate is
395 // probably good enough.
396 assert(p || PyErr_Occurred());
397 throw PyErrOccurred(
398 mod_globs->PyExc_GreenletError,
399 "Greenlet.switch() returned NULL without an exception set."
400 );
401 }
402 return p;
403 }
404 catch(const PyErrOccurred&) {
405 return nullptr;
406 }
407}
408
409PyDoc_STRVAR(
410 green_throw_doc,
411 "Switches execution to this greenlet, but immediately raises the\n"
412 "given exception in this greenlet. If no argument is provided, the "
413 "exception\n"
414 "defaults to `greenlet.GreenletExit`. The normal exception\n"
415 "propagation rules apply, as described for `switch`. Note that calling "
416 "this\n"
417 "method is almost equivalent to the following::\n"
418 "\n"
419 " def raiser():\n"
420 " raise typ, val, tb\n"
421 " g_raiser = greenlet(raiser, parent=g)\n"
422 " g_raiser.switch()\n"
423 "\n"
424 "except that this trick does not work for the\n"
425 "`greenlet.GreenletExit` exception, which would not propagate\n"
426 "from ``g_raiser`` to ``g``.\n");
427
428static PyObject*
429green_throw(PyGreenlet* self, PyObject* args)
430{
431 PyArgParseParam typ(mod_globs->PyExc_GreenletExit);
432 PyArgParseParam val;
434
435 if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) {
436 return nullptr;
437 }
438
439 assert(typ.borrow() || val.borrow());
440
441 self->pimpl->may_switch_away();
442 try {
443 // Both normalizing the error and the actual throw_greenlet
444 // could throw PyErrOccurred.
445 PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow());
446
447 return internal_green_throw(self, err_pieces).relinquish_ownership();
448 }
449 catch (const PyErrOccurred&) {
450 return nullptr;
451 }
452}
453
454static int
455green_bool(PyGreenlet* self)
456{
457 return self->pimpl->active();
458}
459
463static PyObject*
464green_getdict(PyGreenlet* self, void* UNUSED(context))
465{
466 if (self->dict == NULL) {
467 self->dict = PyDict_New();
468 if (self->dict == NULL) {
469 return NULL;
470 }
471 }
472 Py_INCREF(self->dict);
473 return self->dict;
474}
475
476static int
477green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context))
478{
479 PyObject* tmp;
480
481 if (val == NULL) {
482 PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted");
483 return -1;
484 }
485 if (!PyDict_Check(val)) {
486 PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary");
487 return -1;
488 }
489 tmp = self->dict;
490 Py_INCREF(val);
491 self->dict = val;
492 Py_XDECREF(tmp);
493 return 0;
494}
495
496static bool
497_green_not_dead(BorrowedGreenlet self)
498{
499 // XXX: Where else should we do this?
500 // Probably on entry to most Python-facing functions?
501 if (self->was_running_in_dead_thread()) {
502 self->deactivate_and_free();
503 return false;
504 }
505 return self->active() || !self->started();
506}
507
508
509static PyObject*
510green_getdead(PyGreenlet* self, void* UNUSED(context))
511{
512 if (_green_not_dead(self)) {
513 Py_RETURN_FALSE;
514 }
515 else {
516 Py_RETURN_TRUE;
517 }
518}
519
520static PyObject*
521green_get_stack_saved(PyGreenlet* self, void* UNUSED(context))
522{
523 return PyLong_FromSsize_t(self->pimpl->stack_saved());
524}
525
526
527static PyObject*
528green_getrun(PyGreenlet* self, void* UNUSED(context))
529{
530 try {
531 OwnedObject result(BorrowedGreenlet(self)->run());
532 return result.relinquish_ownership();
533 }
534 catch(const PyErrOccurred&) {
535 return nullptr;
536 }
537}
538
539
540static int
541green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context))
542{
543 try {
544 BorrowedGreenlet(self)->run(nrun);
545 return 0;
546 }
547 catch(const PyErrOccurred&) {
548 return -1;
549 }
550}
551
552static PyObject*
553green_getparent(PyGreenlet* self, void* UNUSED(context))
554{
555 return BorrowedGreenlet(self)->parent().acquire_or_None();
556}
557
558
559static int
560green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context))
561{
562 try {
563 BorrowedGreenlet(self)->parent(nparent);
564 }
565 catch(const PyErrOccurred&) {
566 return -1;
567 }
568 return 0;
569}
570
571
572static PyObject*
573green_getcontext(const PyGreenlet* self, void* UNUSED(context))
574{
575 const Greenlet *const g = self->pimpl;
576 try {
577 OwnedObject result(g->context());
578 return result.relinquish_ownership();
579 }
580 catch(const PyErrOccurred&) {
581 return nullptr;
582 }
583}
584
585static int
586green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context))
587{
588 try {
589 BorrowedGreenlet(self)->context(nctx);
590 return 0;
591 }
592 catch(const PyErrOccurred&) {
593 return -1;
594 }
595}
596
597
598static PyObject*
599green_getframe(PyGreenlet* self, void* UNUSED(context))
600{
601 const PythonState::OwnedFrame& top_frame = BorrowedGreenlet(self)->top_frame();
602 return top_frame.acquire_or_None();
603}
604
605
606static PyObject*
607green_getstate(PyGreenlet* self)
608{
609 PyErr_Format(PyExc_TypeError,
610 "cannot serialize '%s' object",
611 Py_TYPE(self)->tp_name);
612 return nullptr;
613}
614
615static PyObject*
616green_repr(PyGreenlet* _self)
617{
618 BorrowedGreenlet self(_self);
619 /*
620 Return a string like
621 <greenlet.greenlet at 0xdeadbeef [current][active started]|dead main>
622
623 The handling of greenlets across threads is not super good.
624 We mostly use the internal definitions of these terms, but they
625 generally should make sense to users as well.
626 */
627 PyObject* result;
628 int never_started = !self->started() && !self->active();
629
630 const char* const tp_name = Py_TYPE(self)->tp_name;
631
632 if (_green_not_dead(self)) {
633 /* XXX: The otid= is almost useless because you can't correlate it to
634 any thread identifier exposed to Python. We could use
635 PyThreadState_GET()->thread_id, but we'd need to save that in the
636 greenlet, or save the whole PyThreadState object itself.
637
638 As it stands, its only useful for identifying greenlets from the same thread.
639 */
640 const char* state_in_thread;
641 if (self->was_running_in_dead_thread()) {
642 // The thread it was running in is dead!
643 // This can happen, especially at interpreter shut down.
644 // It complicates debugging output because it may be
645 // impossible to access the current thread state at that
646 // time. Thus, don't access the current thread state.
647 state_in_thread = " (thread exited)";
648 }
649 else {
650 state_in_thread = GET_THREAD_STATE().state().is_current(self)
651 ? " current"
652 : (self->started() ? " suspended" : "");
653 }
654 result = PyUnicode_FromFormat(
655 "<%s object at %p (otid=%p)%s%s%s%s>",
656 tp_name,
657 self.borrow_o(),
658 self->thread_state(),
659 state_in_thread,
660 self->active() ? " active" : "",
661 never_started ? " pending" : " started",
662 self->main() ? " main" : ""
663 );
664 }
665 else {
666 result = PyUnicode_FromFormat(
667 "<%s object at %p (otid=%p) %sdead>",
668 tp_name,
669 self.borrow_o(),
670 self->thread_state(),
671 self->was_running_in_dead_thread()
672 ? "(thread exited) "
673 : ""
674 );
675 }
676
677 return result;
678}
679
680
681static PyMethodDef green_methods[] = {
682 {
683 .ml_name="switch",
684 .ml_meth=reinterpret_cast<PyCFunction>(green_switch),
685 .ml_flags=METH_VARARGS | METH_KEYWORDS,
686 .ml_doc=green_switch_doc
687 },
688 {.ml_name="throw", .ml_meth=(PyCFunction)green_throw, .ml_flags=METH_VARARGS, .ml_doc=green_throw_doc},
689 {.ml_name="__getstate__", .ml_meth=(PyCFunction)green_getstate, .ml_flags=METH_NOARGS, .ml_doc=NULL},
690 {.ml_name=NULL, .ml_meth=NULL} /* sentinel */
691};
692
693static PyGetSetDef green_getsets[] = {
694 /* name, getter, setter, doc, context pointer */
695 {.name="__dict__", .get=(getter)green_getdict, .set=(setter)green_setdict},
696 {.name="run", .get=(getter)green_getrun, .set=(setter)green_setrun},
697 {.name="parent", .get=(getter)green_getparent, .set=(setter)green_setparent},
698 {.name="gr_frame", .get=(getter)green_getframe },
699 {
700 .name="gr_context",
701 .get=(getter)green_getcontext,
702 .set=(setter)green_setcontext
703 },
704 {.name="dead", .get=(getter)green_getdead},
705 {.name="_stack_saved", .get=(getter)green_get_stack_saved},
706 {.name=NULL}
707};
708
709static PyMemberDef green_members[] = {
710 {.name=NULL}
711};
712
713static PyNumberMethods green_as_number = {
714 .nb_bool=(inquiry)green_bool,
715};
716
717
718PyTypeObject PyGreenlet_Type = {
719 .ob_base=PyVarObject_HEAD_INIT(NULL, 0)
720 .tp_name="greenlet.greenlet", /* tp_name */
721 .tp_basicsize=sizeof(PyGreenlet), /* tp_basicsize */
722 /* methods */
723 .tp_dealloc=(destructor)green_dealloc, /* tp_dealloc */
724 .tp_repr=(reprfunc)green_repr, /* tp_repr */
725 .tp_as_number=&green_as_number, /* tp_as _number*/
726 .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
727 .tp_doc="greenlet(run=None, parent=None) -> greenlet\n\n"
728 "Creates a new greenlet object (without running it).\n\n"
729 " - *run* -- The callable to invoke.\n"
730 " - *parent* -- The parent greenlet. The default is the current "
731 "greenlet.", /* tp_doc */
732 .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */
733 .tp_clear=(inquiry)green_clear, /* tp_clear */
734 .tp_weaklistoffset=offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */
735
736 .tp_methods=green_methods, /* tp_methods */
737 .tp_members=green_members, /* tp_members */
738 .tp_getset=green_getsets, /* tp_getset */
739 .tp_dictoffset=offsetof(PyGreenlet, dict), /* tp_dictoffset */
740 .tp_init=(initproc)green_init, /* tp_init */
741 .tp_alloc=PyType_GenericAlloc, /* tp_alloc */
742 .tp_new=(newfunc)green_new, /* tp_new */
743 .tp_free=PyObject_GC_Del, /* tp_free */
744#ifndef Py_GIL_DISABLED
745/*
746 We may have been handling this wrong all along.
747
748 It shows as a problem with the GIL disabled. In builds of 3.14 with
749 assertions enabled, we break the garbage collector if we *ever*
750 return false from this function. The docs say this is to distinguish
751 some objects that are collectable vs some that are not, specifically
752 giving the example of PyTypeObject as the only place this is done,
753 where it distinguishes between static types like this one (allocated
754 by the C runtime at load time) and dynamic heap types (created at
755 runtime as objects). With the GIL disabled, all allocations that are
756 potentially collectable go in the mimalloc heap, and the collector
757 asserts that tp_is_gc() is true for them as it walks through the
758 heap object by object. Since we set the Py_TPFLAGS_HAS_GC bit, we
759 are always allocated in that mimalloc heap, so we must always be
760 collectable.
761
762 XXX: TODO: Could this be responsible for some apparent leaks, even
763 on GIL builds, at least in 3.14? See if we can catch an assertion
764 failure in the GC on regular 3.14 as well.
765 */
766 .tp_is_gc=(inquiry)green_is_gc, /* tp_is_gc */
767#endif
768};
769
770#endif
771
772// Local Variables:
773// flycheck-clang-include-path: ("/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8")
774// End:
Definition TGreenlet.hpp:731
Definition TGreenlet.hpp:343
Definition greenlet_thread_support.hpp:23
Definition TGreenlet.hpp:752
Definition greenlet_exceptions.hpp:17
Definition TGreenlet.hpp:105
Definition TGreenlet.hpp:242
Definition TThreadState.hpp:87
Definition TGreenlet.hpp:675
Definition greenlet_refs.hpp:1102
Definition greenlet_refs.hpp:995
Definition greenlet.h:22