diff options
Diffstat (limited to 'venv/lib/python3.11/site-packages/greenlet/TGreenlet.cpp')
-rw-r--r-- | venv/lib/python3.11/site-packages/greenlet/TGreenlet.cpp | 714 |
1 files changed, 0 insertions, 714 deletions
diff --git a/venv/lib/python3.11/site-packages/greenlet/TGreenlet.cpp b/venv/lib/python3.11/site-packages/greenlet/TGreenlet.cpp deleted file mode 100644 index 51f8995..0000000 --- a/venv/lib/python3.11/site-packages/greenlet/TGreenlet.cpp +++ /dev/null @@ -1,714 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of greenlet::Greenlet. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ - -#include "greenlet_internal.hpp" -#include "greenlet_greenlet.hpp" -#include "greenlet_thread_state.hpp" - -#include "TGreenletGlobals.cpp" -#include "TThreadStateDestroy.cpp" - -namespace greenlet { - -Greenlet::Greenlet(PyGreenlet* p) -{ - p ->pimpl = this; -} - -Greenlet::~Greenlet() -{ - // XXX: Can't do this. tp_clear is a virtual function, and by the - // time we're here, we've sliced off our child classes. - //this->tp_clear(); -} - -Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack) - : stack_state(initial_stack) -{ - // can't use a delegating constructor because of - // MSVC for Python 2.7 - p->pimpl = this; -} - -bool -Greenlet::force_slp_switch_error() const noexcept -{ - return false; -} - -void -Greenlet::release_args() -{ - this->switch_args.CLEAR(); -} - -/** - * CAUTION: This will allocate memory and may trigger garbage - * collection and arbitrary Python code. - */ -OwnedObject -Greenlet::throw_GreenletExit_during_dealloc(const ThreadState& UNUSED(current_thread_state)) -{ - // If we're killed because we lost all references in the - // middle of a switch, that's ok. Don't reset the args/kwargs, - // we still want to pass them to the parent. - PyErr_SetString(mod_globs->PyExc_GreenletExit, - "Killing the greenlet because all references have vanished."); - // To get here it had to have run before - return this->g_switch(); -} - -inline void -Greenlet::slp_restore_state() noexcept -{ -#ifdef SLP_BEFORE_RESTORE_STATE - SLP_BEFORE_RESTORE_STATE(); -#endif - this->stack_state.copy_heap_to_stack( - this->thread_state()->borrow_current()->stack_state); -} - - -inline int -Greenlet::slp_save_state(char *const stackref) noexcept -{ - // XXX: This used to happen in the middle, before saving, but - // after finding the next owner. Does that matter? This is - // only defined for Sparc/GCC where it flushes register - // windows to the stack (I think) -#ifdef SLP_BEFORE_SAVE_STATE - SLP_BEFORE_SAVE_STATE(); -#endif - return this->stack_state.copy_stack_to_heap(stackref, - this->thread_state()->borrow_current()->stack_state); -} - -/** - * CAUTION: This will allocate memory and may trigger garbage - * collection and arbitrary Python code. - */ -OwnedObject -Greenlet::on_switchstack_or_initialstub_failure( - Greenlet* target, - const Greenlet::switchstack_result_t& err, - const bool target_was_me, - const bool was_initial_stub) -{ - // If we get here, either g_initialstub() - // failed, or g_switchstack() failed. Either one of those - // cases SHOULD leave us in the original greenlet with a valid stack. - if (!PyErr_Occurred()) { - PyErr_SetString( - PyExc_SystemError, - was_initial_stub - ? "Failed to switch stacks into a greenlet for the first time." - : "Failed to switch stacks into a running greenlet."); - } - this->release_args(); - - if (target && !target_was_me) { - target->murder_in_place(); - } - - assert(!err.the_new_current_greenlet); - assert(!err.origin_greenlet); - return OwnedObject(); - -} - -OwnedGreenlet -Greenlet::g_switchstack_success() noexcept -{ - PyThreadState* tstate = PyThreadState_GET(); - // restore the saved state - this->python_state >> tstate; - this->exception_state >> tstate; - - // The thread state hasn't been changed yet. - ThreadState* thread_state = this->thread_state(); - OwnedGreenlet result(thread_state->get_current()); - thread_state->set_current(this->self()); - //assert(thread_state->borrow_current().borrow() == this->_self); - return result; -} - -Greenlet::switchstack_result_t -Greenlet::g_switchstack(void) -{ - // if any of these assertions fail, it's likely because we - // switched away and tried to switch back to us. Early stages of - // switching are not reentrant because we re-use ``this->args()``. - // Switching away would happen if we trigger a garbage collection - // (by just using some Python APIs that happen to allocate Python - // objects) and some garbage had weakref callbacks or __del__ that - // switches (people don't write code like that by hand, but with - // gevent it's possible without realizing it) - assert(this->args() || PyErr_Occurred()); - { /* save state */ - if (this->thread_state()->is_current(this->self())) { - // Hmm, nothing to do. - // TODO: Does this bypass trace events that are - // important? - return switchstack_result_t(0, - this, this->thread_state()->borrow_current()); - } - BorrowedGreenlet current = this->thread_state()->borrow_current(); - PyThreadState* tstate = PyThreadState_GET(); - - current->python_state << tstate; - current->exception_state << tstate; - this->python_state.will_switch_from(tstate); - switching_thread_state = this; - current->expose_frames(); - } - assert(this->args() || PyErr_Occurred()); - // If this is the first switch into a greenlet, this will - // return twice, once with 1 in the new greenlet, once with 0 - // in the origin. - int err; - if (this->force_slp_switch_error()) { - err = -1; - } - else { - err = slp_switch(); - } - - if (err < 0) { /* error */ - // Tested by - // test_greenlet.TestBrokenGreenlets.test_failed_to_slp_switch_into_running - // - // It's not clear if it's worth trying to clean up and - // continue here. Failing to switch stacks is a big deal which - // may not be recoverable (who knows what state the stack is in). - // Also, we've stolen references in preparation for calling - // ``g_switchstack_success()`` and we don't have a clean - // mechanism for backing that all out. - Py_FatalError("greenlet: Failed low-level slp_switch(). The stack is probably corrupt."); - } - - // No stack-based variables are valid anymore. - - // But the global is volatile so we can reload it without the - // compiler caching it from earlier. - Greenlet* greenlet_that_switched_in = switching_thread_state; // aka this - switching_thread_state = nullptr; - // except that no stack variables are valid, we would: - // assert(this == greenlet_that_switched_in); - - // switchstack success is where we restore the exception state, - // etc. It returns the origin greenlet because its convenient. - - OwnedGreenlet origin = greenlet_that_switched_in->g_switchstack_success(); - assert(greenlet_that_switched_in->args() || PyErr_Occurred()); - return switchstack_result_t(err, greenlet_that_switched_in, origin); -} - - -inline void -Greenlet::check_switch_allowed() const -{ - // TODO: Make this take a parameter of the current greenlet, - // or current main greenlet, to make the check for - // cross-thread switching cheaper. Surely somewhere up the - // call stack we've already accessed the thread local variable. - - // We expect to always have a main greenlet now; accessing the thread state - // created it. However, if we get here and cleanup has already - // begun because we're a greenlet that was running in a - // (now dead) thread, these invariants will not hold true. In - // fact, accessing `this->thread_state` may not even be possible. - - // If the thread this greenlet was running in is dead, - // we'll still have a reference to a main greenlet, but the - // thread state pointer we have is bogus. - // TODO: Give the objects an API to determine if they belong - // to a dead thread. - - const BorrowedMainGreenlet main_greenlet = this->find_main_greenlet_in_lineage(); - - if (!main_greenlet) { - throw PyErrOccurred(mod_globs->PyExc_GreenletError, - "cannot switch to a garbage collected greenlet"); - } - - if (!main_greenlet->thread_state()) { - throw PyErrOccurred(mod_globs->PyExc_GreenletError, - "cannot switch to a different thread (which happens to have exited)"); - } - - // The main greenlet we found was from the .parent lineage. - // That may or may not have any relationship to the main - // greenlet of the running thread. We can't actually access - // our this->thread_state members to try to check that, - // because it could be in the process of getting destroyed, - // but setting the main_greenlet->thread_state member to NULL - // may not be visible yet. So we need to check against the - // current thread state (once the cheaper checks are out of - // the way) - const BorrowedMainGreenlet current_main_greenlet = GET_THREAD_STATE().state().borrow_main_greenlet(); - if ( - // lineage main greenlet is not this thread's greenlet - current_main_greenlet != main_greenlet - || ( - // atteched to some thread - this->main_greenlet() - // XXX: Same condition as above. Was this supposed to be - // this->main_greenlet()? - && current_main_greenlet != main_greenlet) - // switching into a known dead thread (XXX: which, if we get here, - // is bad, because we just accessed the thread state, which is - // gone!) - || (!current_main_greenlet->thread_state())) { - // CAUTION: This may trigger memory allocations, gc, and - // arbitrary Python code. - throw PyErrOccurred(mod_globs->PyExc_GreenletError, - "cannot switch to a different thread"); - } -} - -const OwnedObject -Greenlet::context() const -{ - using greenlet::PythonStateContext; - OwnedObject result; - - if (this->is_currently_running_in_some_thread()) { - /* Currently running greenlet: context is stored in the thread state, - not the greenlet object. */ - if (GET_THREAD_STATE().state().is_current(this->self())) { - result = PythonStateContext::context(PyThreadState_GET()); - } - else { - throw ValueError( - "cannot get context of a " - "greenlet that is running in a different thread"); - } - } - else { - /* Greenlet is not running: just return context. */ - result = this->python_state.context(); - } - if (!result) { - result = OwnedObject::None(); - } - return result; -} - - -void -Greenlet::context(BorrowedObject given) -{ - using greenlet::PythonStateContext; - if (!given) { - throw AttributeError("can't delete context attribute"); - } - if (given.is_None()) { - /* "Empty context" is stored as NULL, not None. */ - given = nullptr; - } - - //checks type, incrs refcnt - greenlet::refs::OwnedContext context(given); - PyThreadState* tstate = PyThreadState_GET(); - - if (this->is_currently_running_in_some_thread()) { - if (!GET_THREAD_STATE().state().is_current(this->self())) { - throw ValueError("cannot set context of a greenlet" - " that is running in a different thread"); - } - - /* Currently running greenlet: context is stored in the thread state, - not the greenlet object. */ - OwnedObject octx = OwnedObject::consuming(PythonStateContext::context(tstate)); - PythonStateContext::context(tstate, context.relinquish_ownership()); - } - else { - /* Greenlet is not running: just set context. Note that the - greenlet may be dead.*/ - this->python_state.context() = context; - } -} - -/** - * CAUTION: May invoke arbitrary Python code. - * - * Figure out what the result of ``greenlet.switch(arg, kwargs)`` - * should be and transfers ownership of it to the left-hand-side. - * - * If switch() was just passed an arg tuple, then we'll just return that. - * If only keyword arguments were passed, then we'll pass the keyword - * argument dict. Otherwise, we'll create a tuple of (args, kwargs) and - * return both. - * - * CAUTION: This may allocate a new tuple object, which may - * cause the Python garbage collector to run, which in turn may - * run arbitrary Python code that switches. - */ -OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept -{ - // Because this may invoke arbitrary Python code, which could - // result in switching back to us, we need to get the - // arguments locally on the stack. - assert(rhs); - OwnedObject args = rhs.args(); - OwnedObject kwargs = rhs.kwargs(); - rhs.CLEAR(); - // We shouldn't be called twice for the same switch. - assert(args || kwargs); - assert(!rhs); - - if (!kwargs) { - lhs = args; - } - else if (!PyDict_Size(kwargs.borrow())) { - lhs = args; - } - else if (!PySequence_Length(args.borrow())) { - lhs = kwargs; - } - else { - // PyTuple_Pack allocates memory, may GC, may run arbitrary - // Python code. - lhs = OwnedObject::consuming(PyTuple_Pack(2, args.borrow(), kwargs.borrow())); - } - return lhs; -} - -static OwnedObject -g_handle_exit(const OwnedObject& greenlet_result) -{ - if (!greenlet_result && mod_globs->PyExc_GreenletExit.PyExceptionMatches()) { - /* catch and ignore GreenletExit */ - PyErrFetchParam val; - PyErr_Fetch(PyErrFetchParam(), val, PyErrFetchParam()); - if (!val) { - return OwnedObject::None(); - } - return OwnedObject(val); - } - - if (greenlet_result) { - // package the result into a 1-tuple - // PyTuple_Pack increments the reference of its arguments, - // so we always need to decref the greenlet result; - // the owner will do that. - return OwnedObject::consuming(PyTuple_Pack(1, greenlet_result.borrow())); - } - - return OwnedObject(); -} - - - -/** - * May run arbitrary Python code. - */ -OwnedObject -Greenlet::g_switch_finish(const switchstack_result_t& err) -{ - assert(err.the_new_current_greenlet == this); - - ThreadState& state = *this->thread_state(); - // Because calling the trace function could do arbitrary things, - // including switching away from this greenlet and then maybe - // switching back, we need to capture the arguments now so that - // they don't change. - OwnedObject result; - if (this->args()) { - result <<= this->args(); - } - else { - assert(PyErr_Occurred()); - } - assert(!this->args()); - try { - // Our only caller handles the bad error case - assert(err.status >= 0); - assert(state.borrow_current() == this->self()); - if (OwnedObject tracefunc = state.get_tracefunc()) { - assert(result || PyErr_Occurred()); - g_calltrace(tracefunc, - result ? mod_globs->event_switch : mod_globs->event_throw, - err.origin_greenlet, - this->self()); - } - // The above could have invoked arbitrary Python code, but - // it couldn't switch back to this object and *also* - // throw an exception, so the args won't have changed. - - if (PyErr_Occurred()) { - // We get here if we fell of the end of the run() function - // raising an exception. The switch itself was - // successful, but the function raised. - // valgrind reports that memory allocated here can still - // be reached after a test run. - throw PyErrOccurred::from_current(); - } - return result; - } - catch (const PyErrOccurred&) { - /* Turn switch errors into switch throws */ - /* Turn trace errors into switch throws */ - this->release_args(); - throw; - } -} - -void -Greenlet::g_calltrace(const OwnedObject& tracefunc, - const greenlet::refs::ImmortalEventName& event, - const BorrowedGreenlet& origin, - const BorrowedGreenlet& target) -{ - PyErrPieces saved_exc; - try { - TracingGuard tracing_guard; - // TODO: We have saved the active exception (if any) that's - // about to be raised. In the 'throw' case, we could provide - // the exception to the tracefunction, which seems very helpful. - tracing_guard.CallTraceFunction(tracefunc, event, origin, target); - } - catch (const PyErrOccurred&) { - // In case of exceptions trace function is removed, - // and any existing exception is replaced with the tracing - // exception. - GET_THREAD_STATE().state().set_tracefunc(Py_None); - throw; - } - - saved_exc.PyErrRestore(); - assert( - (event == mod_globs->event_throw && PyErr_Occurred()) - || (event == mod_globs->event_switch && !PyErr_Occurred()) - ); -} - -void -Greenlet::murder_in_place() -{ - if (this->active()) { - assert(!this->is_currently_running_in_some_thread()); - this->deactivate_and_free(); - } -} - -inline void -Greenlet::deactivate_and_free() -{ - if (!this->active()) { - return; - } - // Throw away any saved stack. - this->stack_state = StackState(); - assert(!this->stack_state.active()); - // Throw away any Python references. - // We're holding a borrowed reference to the last - // frame we executed. Since we borrowed it, the - // normal traversal, clear, and dealloc functions - // ignore it, meaning it leaks. (The thread state - // object can't find it to clear it when that's - // deallocated either, because by definition if we - // got an object on this list, it wasn't - // running and the thread state doesn't have - // this frame.) - // So here, we *do* clear it. - this->python_state.tp_clear(true); -} - -bool -Greenlet::belongs_to_thread(const ThreadState* thread_state) const -{ - if (!this->thread_state() // not running anywhere, or thread - // exited - || !thread_state) { // same, or there is no thread state. - return false; - } - return true; -} - - -void -Greenlet::deallocing_greenlet_in_thread(const ThreadState* current_thread_state) -{ - /* Cannot raise an exception to kill the greenlet if - it is not running in the same thread! */ - if (this->belongs_to_thread(current_thread_state)) { - assert(current_thread_state); - // To get here it had to have run before - /* Send the greenlet a GreenletExit exception. */ - - // We don't care about the return value, only whether an - // exception happened. - this->throw_GreenletExit_during_dealloc(*current_thread_state); - return; - } - - // Not the same thread! Temporarily save the greenlet - // into its thread's deleteme list, *if* it exists. - // If that thread has already exited, and processed its pending - // cleanup, we'll never be able to clean everything up: we won't - // be able to raise an exception. - // That's mostly OK! Since we can't add it to a list, our refcount - // won't increase, and we'll go ahead with the DECREFs later. - ThreadState *const thread_state = this->thread_state(); - if (thread_state) { - thread_state->delete_when_thread_running(this->self()); - } - else { - // The thread is dead, we can't raise an exception. - // We need to make it look non-active, though, so that dealloc - // finishes killing it. - this->deactivate_and_free(); - } - return; -} - - -int -Greenlet::tp_traverse(visitproc visit, void* arg) -{ - - int result; - if ((result = this->exception_state.tp_traverse(visit, arg)) != 0) { - return result; - } - //XXX: This is ugly. But so is handling everything having to do - //with the top frame. - bool visit_top_frame = this->was_running_in_dead_thread(); - // When true, the thread is dead. Our implicit weak reference to the - // frame is now all that's left; we consider ourselves to - // strongly own it now. - if ((result = this->python_state.tp_traverse(visit, arg, visit_top_frame)) != 0) { - return result; - } - return 0; -} - -int -Greenlet::tp_clear() -{ - bool own_top_frame = this->was_running_in_dead_thread(); - this->exception_state.tp_clear(); - this->python_state.tp_clear(own_top_frame); - return 0; -} - -bool Greenlet::is_currently_running_in_some_thread() const -{ - return this->stack_state.active() && !this->python_state.top_frame(); -} - -#if GREENLET_PY312 -void GREENLET_NOINLINE(Greenlet::expose_frames)() -{ - if (!this->python_state.top_frame()) { - return; - } - - _PyInterpreterFrame* last_complete_iframe = nullptr; - _PyInterpreterFrame* iframe = this->python_state.top_frame()->f_frame; - while (iframe) { - // We must make a copy before looking at the iframe contents, - // since iframe might point to a portion of the greenlet's C stack - // that was spilled when switching greenlets. - _PyInterpreterFrame iframe_copy; - this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe)); - if (!_PyFrame_IsIncomplete(&iframe_copy)) { - // If the iframe were OWNED_BY_CSTACK then it would always be - // incomplete. Since it's not incomplete, it's not on the C stack - // and we can access it through the original `iframe` pointer - // directly. This is important since GetFrameObject might - // lazily _create_ the frame object and we don't want the - // interpreter to lose track of it. - assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK); - - // We really want to just write: - // PyFrameObject* frame = _PyFrame_GetFrameObject(iframe); - // but _PyFrame_GetFrameObject calls _PyFrame_MakeAndSetFrameObject - // which is not a visible symbol in libpython. The easiest - // way to get a public function to call it is using - // PyFrame_GetBack, which is defined as follows: - // assert(frame != NULL); - // assert(!_PyFrame_IsIncomplete(frame->f_frame)); - // PyFrameObject *back = frame->f_back; - // if (back == NULL) { - // _PyInterpreterFrame *prev = frame->f_frame->previous; - // prev = _PyFrame_GetFirstComplete(prev); - // if (prev) { - // back = _PyFrame_GetFrameObject(prev); - // } - // } - // return (PyFrameObject*)Py_XNewRef(back); - if (!iframe->frame_obj) { - PyFrameObject dummy_frame; - _PyInterpreterFrame dummy_iframe; - dummy_frame.f_back = nullptr; - dummy_frame.f_frame = &dummy_iframe; - // force the iframe to be considered complete without - // needing to check its code object: - dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR; - dummy_iframe.previous = iframe; - assert(!_PyFrame_IsIncomplete(&dummy_iframe)); - // Drop the returned reference immediately; the iframe - // continues to hold a strong reference - Py_XDECREF(PyFrame_GetBack(&dummy_frame)); - assert(iframe->frame_obj); - } - - // This is a complete frame, so make the last one of those we saw - // point at it, bypassing any incomplete frames (which may have - // been on the C stack) in between the two. We're overwriting - // last_complete_iframe->previous and need that to be reversible, - // so we store the original previous ptr in the frame object - // (which we must have created on a previous iteration through - // this loop). The frame object has a bunch of storage that is - // only used when its iframe is OWNED_BY_FRAME_OBJECT, which only - // occurs when the frame object outlives the frame's execution, - // which can't have happened yet because the frame is currently - // executing as far as the interpreter is concerned. So, we can - // reuse it for our own purposes. - assert(iframe->owner == FRAME_OWNED_BY_THREAD - || iframe->owner == FRAME_OWNED_BY_GENERATOR); - if (last_complete_iframe) { - assert(last_complete_iframe->frame_obj); - memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0], - &last_complete_iframe->previous, sizeof(void *)); - last_complete_iframe->previous = iframe; - } - last_complete_iframe = iframe; - } - // Frames that are OWNED_BY_FRAME_OBJECT are linked via the - // frame's f_back while all others are linked via the iframe's - // previous ptr. Since all the frames we traverse are running - // as far as the interpreter is concerned, we don't have to - // worry about the OWNED_BY_FRAME_OBJECT case. - iframe = iframe_copy.previous; - } - - // Give the outermost complete iframe a null previous pointer to - // account for any potential incomplete/C-stack iframes between it - // and the actual top-of-stack - if (last_complete_iframe) { - assert(last_complete_iframe->frame_obj); - memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0], - &last_complete_iframe->previous, sizeof(void *)); - last_complete_iframe->previous = nullptr; - } -} -#else -void Greenlet::expose_frames() -{ - -} -#endif - -}; // namespace greenlet |