Qucs-S S-parameter Viewer & RF Synthesis Tools
Loading...
Searching...
No Matches
TGreenlet.cpp
1/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
12#ifndef TGREENLET_CPP
13#define TGREENLET_CPP
14#include "greenlet_internal.hpp"
15#include "TGreenlet.hpp"
16
17
18#include "TGreenletGlobals.cpp"
19#include "TThreadStateDestroy.cpp"
20
21namespace greenlet {
22
23Greenlet::Greenlet(PyGreenlet* p)
24 : Greenlet(p, StackState())
25{
26}
27
28Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack)
29 : _self(p), stack_state(initial_stack)
30{
31 assert(p->pimpl == nullptr);
32 p->pimpl = this;
33}
34
35Greenlet::~Greenlet()
36{
37 // XXX: Can't do this. tp_clear is a virtual function, and by the
38 // time we're here, we've sliced off our child classes.
39 //this->tp_clear();
40 this->_self->pimpl = nullptr;
41}
42
43bool
44Greenlet::force_slp_switch_error() const noexcept
45{
46 return false;
47}
48
49void
50Greenlet::release_args()
51{
52 this->switch_args.CLEAR();
53}
54
59OwnedObject
60Greenlet::throw_GreenletExit_during_dealloc(const ThreadState& UNUSED(current_thread_state))
61{
62 // If we're killed because we lost all references in the
63 // middle of a switch, that's ok. Don't reset the args/kwargs,
64 // we still want to pass them to the parent.
65 PyErr_SetString(mod_globs->PyExc_GreenletExit,
66 "Killing the greenlet because all references have vanished.");
67 // To get here it had to have run before
68 return this->g_switch();
69}
70
71inline void
72Greenlet::slp_restore_state() noexcept
73{
74#ifdef SLP_BEFORE_RESTORE_STATE
75 SLP_BEFORE_RESTORE_STATE();
76#endif
77 this->stack_state.copy_heap_to_stack(
78 this->thread_state()->borrow_current()->stack_state);
79}
80
81
82inline int
83Greenlet::slp_save_state(char *const stackref) noexcept
84{
85 // XXX: This used to happen in the middle, before saving, but
86 // after finding the next owner. Does that matter? This is
87 // only defined for Sparc/GCC where it flushes register
88 // windows to the stack (I think)
89#ifdef SLP_BEFORE_SAVE_STATE
90 SLP_BEFORE_SAVE_STATE();
91#endif
92 return this->stack_state.copy_stack_to_heap(stackref,
93 this->thread_state()->borrow_current()->stack_state);
94}
95
100OwnedObject
101Greenlet::on_switchstack_or_initialstub_failure(
102 Greenlet* target,
104 const bool target_was_me,
105 const bool was_initial_stub)
106{
107 // If we get here, either g_initialstub()
108 // failed, or g_switchstack() failed. Either one of those
109 // cases SHOULD leave us in the original greenlet with a valid stack.
110 if (!PyErr_Occurred()) {
111 PyErr_SetString(
112 PyExc_SystemError,
113 was_initial_stub
114 ? "Failed to switch stacks into a greenlet for the first time."
115 : "Failed to switch stacks into a running greenlet.");
116 }
117 this->release_args();
118
119 if (target && !target_was_me) {
120 target->murder_in_place();
121 }
122
123 assert(!err.the_new_current_greenlet);
124 assert(!err.origin_greenlet);
125 return OwnedObject();
126
127}
128
129OwnedGreenlet
130Greenlet::g_switchstack_success() noexcept
131{
132 PyThreadState* tstate = PyThreadState_GET();
133 // restore the saved state
134 this->python_state >> tstate;
135 this->exception_state >> tstate;
136
137 // The thread state hasn't been changed yet.
138 ThreadState* thread_state = this->thread_state();
139 OwnedGreenlet result(thread_state->get_current());
140 thread_state->set_current(this->self());
141 //assert(thread_state->borrow_current().borrow() == this->_self);
142 return result;
143}
144
145Greenlet::switchstack_result_t
146Greenlet::g_switchstack(void)
147{
148 // if any of these assertions fail, it's likely because we
149 // switched away and tried to switch back to us. Early stages of
150 // switching are not reentrant because we re-use ``this->args()``.
151 // Switching away would happen if we trigger a garbage collection
152 // (by just using some Python APIs that happen to allocate Python
153 // objects) and some garbage had weakref callbacks or __del__ that
154 // switches (people don't write code like that by hand, but with
155 // gevent it's possible without realizing it)
156 assert(this->args() || PyErr_Occurred());
157 { /* save state */
158 if (this->thread_state()->is_current(this->self())) {
159 // Hmm, nothing to do.
160 // TODO: Does this bypass trace events that are
161 // important?
162 return switchstack_result_t(0,
163 this, this->thread_state()->borrow_current());
164 }
165 BorrowedGreenlet current = this->thread_state()->borrow_current();
166 PyThreadState* tstate = PyThreadState_GET();
167
168 current->python_state << tstate;
169 current->exception_state << tstate;
170 this->python_state.will_switch_from(tstate);
171 switching_thread_state = this;
172 current->expose_frames();
173 }
174 assert(this->args() || PyErr_Occurred());
175 // If this is the first switch into a greenlet, this will
176 // return twice, once with 1 in the new greenlet, once with 0
177 // in the origin.
178 int err;
179 if (this->force_slp_switch_error()) {
180 err = -1;
181 }
182 else {
183 err = slp_switch();
184 }
185
186 if (err < 0) { /* error */
187 // Tested by
188 // test_greenlet.TestBrokenGreenlets.test_failed_to_slp_switch_into_running
189 //
190 // It's not clear if it's worth trying to clean up and
191 // continue here. Failing to switch stacks is a big deal which
192 // may not be recoverable (who knows what state the stack is in).
193 // Also, we've stolen references in preparation for calling
194 // ``g_switchstack_success()`` and we don't have a clean
195 // mechanism for backing that all out.
196 Py_FatalError("greenlet: Failed low-level slp_switch(). The stack is probably corrupt.");
197 }
198
199 // No stack-based variables are valid anymore.
200
201 // But the global is volatile so we can reload it without the
202 // compiler caching it from earlier.
203 Greenlet* greenlet_that_switched_in = switching_thread_state; // aka this
204 switching_thread_state = nullptr;
205 // except that no stack variables are valid, we would:
206 // assert(this == greenlet_that_switched_in);
207
208 // switchstack success is where we restore the exception state,
209 // etc. It returns the origin greenlet because its convenient.
210
211 OwnedGreenlet origin = greenlet_that_switched_in->g_switchstack_success();
212 assert(greenlet_that_switched_in->args() || PyErr_Occurred());
213 return switchstack_result_t(err, greenlet_that_switched_in, origin);
214}
215
216
217inline void
218Greenlet::check_switch_allowed() const
219{
220 // TODO: Make this take a parameter of the current greenlet,
221 // or current main greenlet, to make the check for
222 // cross-thread switching cheaper. Surely somewhere up the
223 // call stack we've already accessed the thread local variable.
224
225 // We expect to always have a main greenlet now; accessing the thread state
226 // created it. However, if we get here and cleanup has already
227 // begun because we're a greenlet that was running in a
228 // (now dead) thread, these invariants will not hold true. In
229 // fact, accessing `this->thread_state` may not even be possible.
230
231 // If the thread this greenlet was running in is dead,
232 // we'll still have a reference to a main greenlet, but the
233 // thread state pointer we have is bogus.
234 // TODO: Give the objects an API to determine if they belong
235 // to a dead thread.
236
237 const BorrowedMainGreenlet main_greenlet = this->find_main_greenlet_in_lineage();
238
239 if (!main_greenlet) {
240 throw PyErrOccurred(mod_globs->PyExc_GreenletError,
241 "cannot switch to a garbage collected greenlet");
242 }
243
244 if (!main_greenlet->thread_state()) {
245 throw PyErrOccurred(mod_globs->PyExc_GreenletError,
246 "cannot switch to a different thread (which happens to have exited)");
247 }
248
249 // The main greenlet we found was from the .parent lineage.
250 // That may or may not have any relationship to the main
251 // greenlet of the running thread. We can't actually access
252 // our this->thread_state members to try to check that,
253 // because it could be in the process of getting destroyed,
254 // but setting the main_greenlet->thread_state member to NULL
255 // may not be visible yet. So we need to check against the
256 // current thread state (once the cheaper checks are out of
257 // the way)
258 const BorrowedMainGreenlet current_main_greenlet = GET_THREAD_STATE().state().borrow_main_greenlet();
259 if (
260 // lineage main greenlet is not this thread's greenlet
261 current_main_greenlet != main_greenlet
262 || (
263 // atteched to some thread
264 this->main_greenlet()
265 // XXX: Same condition as above. Was this supposed to be
266 // this->main_greenlet()?
267 && current_main_greenlet != main_greenlet)
268 // switching into a known dead thread (XXX: which, if we get here,
269 // is bad, because we just accessed the thread state, which is
270 // gone!)
271 || (!current_main_greenlet->thread_state())) {
272 // CAUTION: This may trigger memory allocations, gc, and
273 // arbitrary Python code.
274 throw PyErrOccurred(
275 mod_globs->PyExc_GreenletError,
276 "Cannot switch to a different thread\n\tCurrent: %R\n\tExpected: %R",
277 current_main_greenlet, main_greenlet);
278 }
279}
280
281const OwnedObject
282Greenlet::context() const
283{
285 OwnedObject result;
286
287 if (this->is_currently_running_in_some_thread()) {
288 /* Currently running greenlet: context is stored in the thread state,
289 not the greenlet object. */
290 if (GET_THREAD_STATE().state().is_current(this->self())) {
291 result = PythonStateContext::context(PyThreadState_GET());
292 }
293 else {
294 throw ValueError(
295 "cannot get context of a "
296 "greenlet that is running in a different thread");
297 }
298 }
299 else {
300 /* Greenlet is not running: just return context. */
301 result = this->python_state.context();
302 }
303 if (!result) {
304 result = OwnedObject::None();
305 }
306 return result;
307}
308
309
310void
311Greenlet::context(BorrowedObject given)
312{
314 if (!given) {
315 throw AttributeError("can't delete context attribute");
316 }
317 if (given.is_None()) {
318 /* "Empty context" is stored as NULL, not None. */
319 given = nullptr;
320 }
321
322 //checks type, incrs refcnt
323 greenlet::refs::OwnedContext context(given);
324 PyThreadState* tstate = PyThreadState_GET();
325
326 if (this->is_currently_running_in_some_thread()) {
327 if (!GET_THREAD_STATE().state().is_current(this->self())) {
328 throw ValueError("cannot set context of a greenlet"
329 " that is running in a different thread");
330 }
331
332 /* Currently running greenlet: context is stored in the thread state,
333 not the greenlet object. */
334 OwnedObject octx = OwnedObject::consuming(PythonStateContext::context(tstate));
335 PythonStateContext::context(tstate, context.relinquish_ownership());
336 }
337 else {
338 /* Greenlet is not running: just set context. Note that the
339 greenlet may be dead.*/
340 this->python_state.context() = context;
341 }
342}
343
359OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept
360{
361 // Because this may invoke arbitrary Python code, which could
362 // result in switching back to us, we need to get the
363 // arguments locally on the stack.
364 assert(rhs);
365 OwnedObject args = rhs.args();
366 OwnedObject kwargs = rhs.kwargs();
367 rhs.CLEAR();
368 // We shouldn't be called twice for the same switch.
369 assert(args || kwargs);
370 assert(!rhs);
371
372 if (!kwargs) {
373 lhs = args;
374 }
375 else if (!PyDict_Size(kwargs.borrow())) {
376 lhs = args;
377 }
378 else if (!PySequence_Length(args.borrow())) {
379 lhs = kwargs;
380 }
381 else {
382 // PyTuple_Pack allocates memory, may GC, may run arbitrary
383 // Python code.
384 lhs = OwnedObject::consuming(PyTuple_Pack(2, args.borrow(), kwargs.borrow()));
385 }
386 return lhs;
387}
388
389static OwnedObject
390g_handle_exit(const OwnedObject& greenlet_result)
391{
392 if (!greenlet_result && mod_globs->PyExc_GreenletExit.PyExceptionMatches()) {
393 /* catch and ignore GreenletExit */
394 PyErrFetchParam val;
395 PyErr_Fetch(PyErrFetchParam(), val, PyErrFetchParam());
396 if (!val) {
397 return OwnedObject::None();
398 }
399 return OwnedObject(val);
400 }
401
402 if (greenlet_result) {
403 // package the result into a 1-tuple
404 // PyTuple_Pack increments the reference of its arguments,
405 // so we always need to decref the greenlet result;
406 // the owner will do that.
407 return OwnedObject::consuming(PyTuple_Pack(1, greenlet_result.borrow()));
408 }
409
410 return OwnedObject();
411}
412
413
414
418OwnedObject
419Greenlet::g_switch_finish(const switchstack_result_t& err)
420{
421 assert(err.the_new_current_greenlet == this);
422
423 ThreadState& state = *this->thread_state();
424 // Because calling the trace function could do arbitrary things,
425 // including switching away from this greenlet and then maybe
426 // switching back, we need to capture the arguments now so that
427 // they don't change.
428 OwnedObject result;
429 if (this->args()) {
430 result <<= this->args();
431 }
432 else {
433 assert(PyErr_Occurred());
434 }
435 assert(!this->args());
436 try {
437 // Our only caller handles the bad error case
438 assert(err.status >= 0);
439 assert(state.borrow_current() == this->self());
440 if (OwnedObject tracefunc = state.get_tracefunc()) {
441 assert(result || PyErr_Occurred());
442 g_calltrace(tracefunc,
443 result ? mod_globs->event_switch : mod_globs->event_throw,
444 err.origin_greenlet,
445 this->self());
446 }
447 // The above could have invoked arbitrary Python code, but
448 // it couldn't switch back to this object and *also*
449 // throw an exception, so the args won't have changed.
450
451 if (PyErr_Occurred()) {
452 // We get here if we fell of the end of the run() function
453 // raising an exception. The switch itself was
454 // successful, but the function raised.
455 // valgrind reports that memory allocated here can still
456 // be reached after a test run.
457 throw PyErrOccurred::from_current();
458 }
459 return result;
460 }
461 catch (const PyErrOccurred&) {
462 /* Turn switch errors into switch throws */
463 /* Turn trace errors into switch throws */
464 this->release_args();
465 throw;
466 }
467}
468
469void
470Greenlet::g_calltrace(const OwnedObject& tracefunc,
472 const BorrowedGreenlet& origin,
473 const BorrowedGreenlet& target)
474{
475 PyErrPieces saved_exc;
476 try {
477 TracingGuard tracing_guard;
478 // TODO: We have saved the active exception (if any) that's
479 // about to be raised. In the 'throw' case, we could provide
480 // the exception to the tracefunction, which seems very helpful.
481 tracing_guard.CallTraceFunction(tracefunc, event, origin, target);
482 }
483 catch (const PyErrOccurred&) {
484 // In case of exceptions trace function is removed,
485 // and any existing exception is replaced with the tracing
486 // exception.
487 GET_THREAD_STATE().state().set_tracefunc(Py_None);
488 throw;
489 }
490
491 saved_exc.PyErrRestore();
492 assert(
493 (event == mod_globs->event_throw && PyErr_Occurred())
494 || (event == mod_globs->event_switch && !PyErr_Occurred())
495 );
496}
497
498void
499Greenlet::murder_in_place()
500{
501 if (this->active()) {
502 assert(!this->is_currently_running_in_some_thread());
503 this->deactivate_and_free();
504 }
505}
506
507inline void
508Greenlet::deactivate_and_free()
509{
510 if (!this->active()) {
511 return;
512 }
513 // Throw away any saved stack.
514 this->stack_state = StackState();
515 assert(!this->stack_state.active());
516 // Throw away any Python references.
517 // We're holding a borrowed reference to the last
518 // frame we executed. Since we borrowed it, the
519 // normal traversal, clear, and dealloc functions
520 // ignore it, meaning it leaks. (The thread state
521 // object can't find it to clear it when that's
522 // deallocated either, because by definition if we
523 // got an object on this list, it wasn't
524 // running and the thread state doesn't have
525 // this frame.)
526 // So here, we *do* clear it.
527 this->python_state.tp_clear(true);
528}
529
530bool
531Greenlet::belongs_to_thread(const ThreadState* thread_state) const
532{
533 if (!this->thread_state() // not running anywhere, or thread
534 // exited
535 || !thread_state) { // same, or there is no thread state.
536 return false;
537 }
538 return true;
539}
540
541
542void
543Greenlet::deallocing_greenlet_in_thread(const ThreadState* current_thread_state)
544{
545 /* Cannot raise an exception to kill the greenlet if
546 it is not running in the same thread! */
547 if (this->belongs_to_thread(current_thread_state)) {
548 assert(current_thread_state);
549 // To get here it had to have run before
550 /* Send the greenlet a GreenletExit exception. */
551
552 // We don't care about the return value, only whether an
553 // exception happened.
554 this->throw_GreenletExit_during_dealloc(*current_thread_state);
555 return;
556 }
557
558 // Not the same thread! Temporarily save the greenlet
559 // into its thread's deleteme list, *if* it exists.
560 // If that thread has already exited, and processed its pending
561 // cleanup, we'll never be able to clean everything up: we won't
562 // be able to raise an exception.
563 // That's mostly OK! Since we can't add it to a list, our refcount
564 // won't increase, and we'll go ahead with the DECREFs later.
565
566 ThreadState *const thread_state = this->thread_state();
567 if (thread_state) {
568 thread_state->delete_when_thread_running(this->self());
569 }
570 else {
571 // The thread is dead, we can't raise an exception.
572 // We need to make it look non-active, though, so that dealloc
573 // finishes killing it.
574 this->deactivate_and_free();
575 }
576 return;
577}
578
579
580int
581Greenlet::tp_traverse(visitproc visit, void* arg)
582{
583
584 int result;
585 if ((result = this->exception_state.tp_traverse(visit, arg)) != 0) {
586 return result;
587 }
588 //XXX: This is ugly. But so is handling everything having to do
589 //with the top frame.
590 bool visit_top_frame = this->was_running_in_dead_thread();
591 // When true, the thread is dead. Our implicit weak reference to the
592 // frame is now all that's left; we consider ourselves to
593 // strongly own it now.
594 if ((result = this->python_state.tp_traverse(visit, arg, visit_top_frame)) != 0) {
595 return result;
596 }
597 return 0;
598}
599
600int
601Greenlet::tp_clear()
602{
603 bool own_top_frame = this->was_running_in_dead_thread();
604 this->exception_state.tp_clear();
605 this->python_state.tp_clear(own_top_frame);
606 return 0;
607}
608
609bool Greenlet::is_currently_running_in_some_thread() const
610{
611 return this->stack_state.active() && !this->python_state.top_frame();
612}
613
614#if GREENLET_PY312
615void GREENLET_NOINLINE(Greenlet::expose_frames)()
616{
617 if (!this->python_state.top_frame()) {
618 return;
619 }
620
621 _PyInterpreterFrame* last_complete_iframe = nullptr;
622 _PyInterpreterFrame* iframe = this->python_state.top_frame()->f_frame;
623 while (iframe) {
624 // We must make a copy before looking at the iframe contents,
625 // since iframe might point to a portion of the greenlet's C stack
626 // that was spilled when switching greenlets.
627 _PyInterpreterFrame iframe_copy;
628 this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe));
629 if (!_PyFrame_IsIncomplete(&iframe_copy)) {
630 // If the iframe were OWNED_BY_CSTACK then it would always be
631 // incomplete. Since it's not incomplete, it's not on the C stack
632 // and we can access it through the original `iframe` pointer
633 // directly. This is important since GetFrameObject might
634 // lazily _create_ the frame object and we don't want the
635 // interpreter to lose track of it.
636 assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK);
637
638 // We really want to just write:
639 // PyFrameObject* frame = _PyFrame_GetFrameObject(iframe);
640 // but _PyFrame_GetFrameObject calls _PyFrame_MakeAndSetFrameObject
641 // which is not a visible symbol in libpython. The easiest
642 // way to get a public function to call it is using
643 // PyFrame_GetBack, which is defined as follows:
644 // assert(frame != NULL);
645 // assert(!_PyFrame_IsIncomplete(frame->f_frame));
646 // PyFrameObject *back = frame->f_back;
647 // if (back == NULL) {
648 // _PyInterpreterFrame *prev = frame->f_frame->previous;
649 // prev = _PyFrame_GetFirstComplete(prev);
650 // if (prev) {
651 // back = _PyFrame_GetFrameObject(prev);
652 // }
653 // }
654 // return (PyFrameObject*)Py_XNewRef(back);
655 if (!iframe->frame_obj) {
656 PyFrameObject dummy_frame;
657 _PyInterpreterFrame dummy_iframe;
658 dummy_frame.f_back = nullptr;
659 dummy_frame.f_frame = &dummy_iframe;
660 // force the iframe to be considered complete without
661 // needing to check its code object:
662 dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR;
663 dummy_iframe.previous = iframe;
664 assert(!_PyFrame_IsIncomplete(&dummy_iframe));
665 // Drop the returned reference immediately; the iframe
666 // continues to hold a strong reference
667 Py_XDECREF(PyFrame_GetBack(&dummy_frame));
668 assert(iframe->frame_obj);
669 }
670
671 // This is a complete frame, so make the last one of those we saw
672 // point at it, bypassing any incomplete frames (which may have
673 // been on the C stack) in between the two. We're overwriting
674 // last_complete_iframe->previous and need that to be reversible,
675 // so we store the original previous ptr in the frame object
676 // (which we must have created on a previous iteration through
677 // this loop). The frame object has a bunch of storage that is
678 // only used when its iframe is OWNED_BY_FRAME_OBJECT, which only
679 // occurs when the frame object outlives the frame's execution,
680 // which can't have happened yet because the frame is currently
681 // executing as far as the interpreter is concerned. So, we can
682 // reuse it for our own purposes.
683 assert(iframe->owner == FRAME_OWNED_BY_THREAD
684 || iframe->owner == FRAME_OWNED_BY_GENERATOR);
685 if (last_complete_iframe) {
686 assert(last_complete_iframe->frame_obj);
687 memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
688 &last_complete_iframe->previous, sizeof(void *));
689 last_complete_iframe->previous = iframe;
690 }
691 last_complete_iframe = iframe;
692 }
693 // Frames that are OWNED_BY_FRAME_OBJECT are linked via the
694 // frame's f_back while all others are linked via the iframe's
695 // previous ptr. Since all the frames we traverse are running
696 // as far as the interpreter is concerned, we don't have to
697 // worry about the OWNED_BY_FRAME_OBJECT case.
698 iframe = iframe_copy.previous;
699 }
700
701 // Give the outermost complete iframe a null previous pointer to
702 // account for any potential incomplete/C-stack iframes between it
703 // and the actual top-of-stack
704 if (last_complete_iframe) {
705 assert(last_complete_iframe->frame_obj);
706 memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
707 &last_complete_iframe->previous, sizeof(void *));
708 last_complete_iframe->previous = nullptr;
709 }
710}
711#else
712void Greenlet::expose_frames()
713{
714
715}
716#endif
717
718}; // namespace greenlet
719#endif
Definition TGreenlet.hpp:343
Definition greenlet_exceptions.hpp:17
Definition TGreenlet.hpp:72
Definition TGreenlet.hpp:185
Definition TGreenlet.hpp:242
Definition TThreadState.hpp:87
OwnedGreenlet get_current()
Definition TThreadState.hpp:233
BorrowedGreenlet borrow_current()
Definition TThreadState.hpp:246
Definition greenlet_refs.hpp:600
Definition greenlet_refs.hpp:686
Definition greenlet_refs.hpp:925
Definition greenlet_refs.hpp:995
Definition __init__.py:1
OwnedObject & operator<<=(OwnedObject &lhs, greenlet::SwitchingArgs &rhs) noexcept
Definition TGreenlet.cpp:359
Definition greenlet.h:22
Definition TGreenlet.hpp:513