From 12cf076118570eebbff08c6b3090e0d4798447a1 Mon Sep 17 00:00:00 2001 From: cyfraeviolae Date: Wed, 3 Apr 2024 03:17:55 -0400 Subject: no venv --- .../site-packages/greenlet/TUserGreenlet.cpp | 667 --------------------- 1 file changed, 667 deletions(-) delete mode 100644 venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp (limited to 'venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp') diff --git a/venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp b/venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp deleted file mode 100644 index 495a794..0000000 --- a/venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp +++ /dev/null @@ -1,667 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of greenlet::UserGreenlet. - * - * 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 "TThreadStateDestroy.cpp" - - -namespace greenlet { -using greenlet::refs::BorrowedMainGreenlet; -greenlet::PythonAllocator UserGreenlet::allocator; - -void* UserGreenlet::operator new(size_t UNUSED(count)) -{ - return allocator.allocate(1); -} - - -void UserGreenlet::operator delete(void* ptr) -{ - return allocator.deallocate(static_cast(ptr), - 1); -} - - -UserGreenlet::UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent) - : Greenlet(p), _parent(the_parent) -{ - this->_self = p; -} - -UserGreenlet::~UserGreenlet() -{ - // Python 3.11: If we don't clear out the raw frame datastack - // when deleting an unfinished greenlet, - // TestLeaks.test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main fails. - this->python_state.did_finish(nullptr); - this->tp_clear(); -} - -BorrowedGreenlet -UserGreenlet::self() const noexcept -{ - return this->_self; -} - - - -const BorrowedMainGreenlet -UserGreenlet::main_greenlet() const -{ - return this->_main_greenlet; -} - - -BorrowedMainGreenlet -UserGreenlet::find_main_greenlet_in_lineage() const -{ - if (this->started()) { - assert(this->_main_greenlet); - return BorrowedMainGreenlet(this->_main_greenlet); - } - - if (!this->_parent) { - /* garbage collected greenlet in chain */ - // XXX: WHAT? - return BorrowedMainGreenlet(nullptr); - } - - return this->_parent->find_main_greenlet_in_lineage(); -} - - -/** - * CAUTION: This will allocate memory and may trigger garbage - * collection and arbitrary Python code. - */ -OwnedObject -UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state) -{ - /* The dying greenlet cannot be a parent of ts_current - because the 'parent' field chain would hold a - reference */ - UserGreenlet::ParentIsCurrentGuard with_current_parent(this, current_thread_state); - - // We don't care about the return value, only whether an - // exception happened. Whether or not an exception happens, - // we need to restore the parent in case the greenlet gets - // resurrected. - return Greenlet::throw_GreenletExit_during_dealloc(current_thread_state); -} - -ThreadState* -UserGreenlet::thread_state() const noexcept -{ - // TODO: maybe make this throw, if the thread state isn't there? - // if (!this->main_greenlet) { - // throw std::runtime_error("No thread state"); // TODO: Better exception - // } - if (!this->_main_greenlet) { - return nullptr; - } - return this->_main_greenlet->thread_state(); -} - - -bool -UserGreenlet::was_running_in_dead_thread() const noexcept -{ - return this->_main_greenlet && !this->thread_state(); -} - -OwnedObject -UserGreenlet::g_switch() -{ - assert(this->args() || PyErr_Occurred()); - - try { - this->check_switch_allowed(); - } - catch (const PyErrOccurred&) { - this->release_args(); - throw; - } - - // Switching greenlets used to attempt to clean out ones that need - // deleted *if* we detected a thread switch. Should it still do - // that? - // An issue is that if we delete a greenlet from another thread, - // it gets queued to this thread, and ``kill_greenlet()`` switches - // back into the greenlet - - /* find the real target by ignoring dead greenlets, - and if necessary starting a greenlet. */ - switchstack_result_t err; - Greenlet* target = this; - // TODO: probably cleaner to handle the case where we do - // switch to ourself separately from the other cases. - // This can probably even further be simplified if we keep - // track of the switching_state we're going for and just call - // into g_switch() if it's not ourself. The main problem with that - // is that we would be using more stack space. - bool target_was_me = true; - bool was_initial_stub = false; - while (target) { - if (target->active()) { - if (!target_was_me) { - target->args() <<= this->args(); - assert(!this->args()); - } - err = target->g_switchstack(); - break; - } - if (!target->started()) { - // We never encounter a main greenlet that's not started. - assert(!target->main()); - UserGreenlet* real_target = static_cast(target); - assert(real_target); - void* dummymarker; - was_initial_stub = true; - if (!target_was_me) { - target->args() <<= this->args(); - assert(!this->args()); - } - try { - // This can only throw back to us while we're - // still in this greenlet. Once the new greenlet - // is bootstrapped, it has its own exception state. - err = real_target->g_initialstub(&dummymarker); - } - catch (const PyErrOccurred&) { - this->release_args(); - throw; - } - catch (const GreenletStartedWhileInPython&) { - // The greenlet was started sometime before this - // greenlet actually switched to it, i.e., - // "concurrent" calls to switch() or throw(). - // We need to retry the switch. - // Note that the current greenlet has been reset - // to this one (or we wouldn't be running!) - continue; - } - break; - } - - target = target->parent(); - target_was_me = false; - } - // The ``this`` pointer and all other stack or register based - // variables are invalid now, at least where things succeed - // above. - // But this one, probably not so much? It's not clear if it's - // safe to throw an exception at this point. - - if (err.status < 0) { - // 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. - return this->on_switchstack_or_initialstub_failure(target, err, target_was_me, was_initial_stub); - } - - // err.the_new_current_greenlet would be the same as ``target``, - // if target wasn't probably corrupt. - return err.the_new_current_greenlet->g_switch_finish(err); -} - - - -Greenlet::switchstack_result_t -UserGreenlet::g_initialstub(void* mark) -{ - OwnedObject run; - - // We need to grab a reference to the current switch arguments - // in case we're entered concurrently during the call to - // GetAttr() and have to try again. - // We'll restore them when we return in that case. - // Scope them tightly to avoid ref leaks. - { - SwitchingArgs args(this->args()); - - /* save exception in case getattr clears it */ - PyErrPieces saved; - - /* - self.run is the object to call in the new greenlet. - This could run arbitrary python code and switch greenlets! - */ - run = this->_self.PyRequireAttr(mod_globs->str_run); - /* restore saved exception */ - saved.PyErrRestore(); - - - /* recheck that it's safe to switch in case greenlet reparented anywhere above */ - this->check_switch_allowed(); - - /* by the time we got here another start could happen elsewhere, - * that means it should now be a regular switch. - * This can happen if the Python code is a subclass that implements - * __getattribute__ or __getattr__, or makes ``run`` a descriptor; - * all of those can run arbitrary code that switches back into - * this greenlet. - */ - if (this->stack_state.started()) { - // the successful switch cleared these out, we need to - // restore our version. They will be copied on up to the - // next target. - assert(!this->args()); - this->args() <<= args; - throw GreenletStartedWhileInPython(); - } - } - - // Sweet, if we got here, we have the go-ahead and will switch - // greenlets. - // Nothing we do from here on out should allow for a thread or - // greenlet switch: No arbitrary calls to Python, including - // decref'ing - -#if GREENLET_USE_CFRAME - /* OK, we need it, we're about to switch greenlets, save the state. */ - /* - See green_new(). This is a stack-allocated variable used - while *self* is in PyObject_Call(). - We want to defer copying the state info until we're sure - we need it and are in a stable place to do so. - */ - _PyCFrame trace_info; - - this->python_state.set_new_cframe(trace_info); -#endif - /* start the greenlet */ - ThreadState& thread_state = GET_THREAD_STATE().state(); - this->stack_state = StackState(mark, - thread_state.borrow_current()->stack_state); - this->python_state.set_initial_state(PyThreadState_GET()); - this->exception_state.clear(); - this->_main_greenlet = thread_state.get_main_greenlet(); - - /* perform the initial switch */ - switchstack_result_t err = this->g_switchstack(); - /* returns twice! - The 1st time with ``err == 1``: we are in the new greenlet. - This one owns a greenlet that used to be current. - The 2nd time with ``err <= 0``: back in the caller's - greenlet; this happens if the child finishes or switches - explicitly to us. Either way, the ``err`` variable is - created twice at the same memory location, but possibly - having different ``origin`` values. Note that it's not - constructed for the second time until the switch actually happens. - */ - if (err.status == 1) { - // In the new greenlet. - - // This never returns! Calling inner_bootstrap steals - // the contents of our run object within this stack frame, so - // it is not valid to do anything with it. - try { - this->inner_bootstrap(err.origin_greenlet.relinquish_ownership(), - run.relinquish_ownership()); - } - // Getting a C++ exception here isn't good. It's probably a - // bug in the underlying greenlet, meaning it's probably a - // C++ extension. We're going to abort anyway, but try to - // display some nice information *if* possible. Some obscure - // platforms don't properly support this (old 32-bit Arm, see see - // https://github.com/python-greenlet/greenlet/issues/385); that's not - // great, but should usually be OK because, as mentioned above, we're - // terminating anyway. - // - // The catching is tested by - // ``test_cpp.CPPTests.test_unhandled_exception_in_greenlet_aborts``. - // - // PyErrOccurred can theoretically be thrown by - // inner_bootstrap() -> g_switch_finish(), but that should - // never make it back to here. It is a std::exception and - // would be caught if it is. - catch (const std::exception& e) { - std::string base = "greenlet: Unhandled C++ exception: "; - base += e.what(); - Py_FatalError(base.c_str()); - } - catch (...) { - // Some compilers/runtimes use exceptions internally. - // It appears that GCC on Linux with libstdc++ throws an - // exception internally at process shutdown time to unwind - // stacks and clean up resources. Depending on exactly - // where we are when the process exits, that could result - // in an unknown exception getting here. If we - // Py_FatalError() or abort() here, we interfere with - // orderly process shutdown. Throwing the exception on up - // is the right thing to do. - // - // gevent's ``examples/dns_mass_resolve.py`` demonstrates this. -#ifndef NDEBUG - fprintf(stderr, - "greenlet: inner_bootstrap threw unknown exception; " - "is the process terminating?\n"); -#endif - throw; - } - Py_FatalError("greenlet: inner_bootstrap returned with no exception.\n"); - } - - - // In contrast, notice that we're keeping the origin greenlet - // around as an owned reference; we need it to call the trace - // function for the switch back into the parent. It was only - // captured at the time the switch actually happened, though, - // so we haven't been keeping an extra reference around this - // whole time. - - /* back in the parent */ - if (err.status < 0) { - /* start failed badly, restore greenlet state */ - this->stack_state = StackState(); - this->_main_greenlet.CLEAR(); - // CAUTION: This may run arbitrary Python code. - run.CLEAR(); // inner_bootstrap didn't run, we own the reference. - } - - // In the success case, the spawned code (inner_bootstrap) will - // take care of decrefing this, so we relinquish ownership so as - // to not double-decref. - - run.relinquish_ownership(); - - return err; -} - - -void -UserGreenlet::inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run) -{ - // The arguments here would be another great place for move. - // As it is, we take them as a reference so that when we clear - // them we clear what's on the stack above us. Do that NOW, and - // without using a C++ RAII object, - // so there's no way that exiting the parent frame can clear it, - // or we clear it unexpectedly. This arises in the context of the - // interpreter shutting down. See https://github.com/python-greenlet/greenlet/issues/325 - //PyObject* run = _run.relinquish_ownership(); - - /* in the new greenlet */ - assert(this->thread_state()->borrow_current() == this->_self); - // C++ exceptions cannot propagate to the parent greenlet from - // here. (TODO: Do we need a catch(...) clause, perhaps on the - // function itself? ALl we could do is terminate the program.) - // NOTE: On 32-bit Windows, the call chain is extremely - // important here in ways that are subtle, having to do with - // the depth of the SEH list. The call to restore it MUST NOT - // add a new SEH handler to the list, or we'll restore it to - // the wrong thing. - this->thread_state()->restore_exception_state(); - /* stack variables from above are no good and also will not unwind! */ - // EXCEPT: That can't be true, we access run, among others, here. - - this->stack_state.set_active(); /* running */ - - // We're about to possibly run Python code again, which - // could switch back/away to/from us, so we need to grab the - // arguments locally. - SwitchingArgs args; - args <<= this->args(); - assert(!this->args()); - - // XXX: We could clear this much earlier, right? - // Or would that introduce the possibility of running Python - // code when we don't want to? - // CAUTION: This may run arbitrary Python code. - this->_run_callable.CLEAR(); - - - // The first switch we need to manually call the trace - // function here instead of in g_switch_finish, because we - // never return there. - if (OwnedObject tracefunc = this->thread_state()->get_tracefunc()) { - OwnedGreenlet trace_origin; - trace_origin = origin_greenlet; - try { - g_calltrace(tracefunc, - args ? mod_globs->event_switch : mod_globs->event_throw, - trace_origin, - this->_self); - } - catch (const PyErrOccurred&) { - /* Turn trace errors into switch throws */ - args.CLEAR(); - } - } - - // We no longer need the origin, it was only here for - // tracing. - // We may never actually exit this stack frame so we need - // to explicitly clear it. - // This could run Python code and switch. - Py_CLEAR(origin_greenlet); - - OwnedObject result; - if (!args) { - /* pending exception */ - result = NULL; - } - else { - /* call g.run(*args, **kwargs) */ - // This could result in further switches - try { - //result = run.PyCall(args.args(), args.kwargs()); - // CAUTION: Just invoking this, before the function even - // runs, may cause memory allocations, which may trigger - // GC, which may run arbitrary Python code. - result = OwnedObject::consuming(PyObject_Call(run, args.args().borrow(), args.kwargs().borrow())); - } - catch (...) { - // Unhandled C++ exception! - - // If we declare ourselves as noexcept, if we don't catch - // this here, most platforms will just abort() the - // process. But on 64-bit Windows with older versions of - // the C runtime, this can actually corrupt memory and - // just return. We see this when compiling with the - // Windows 7.0 SDK targeting Windows Server 2008, but not - // when using the Appveyor Visual Studio 2019 image. So - // this currently only affects Python 2.7 on Windows 64. - // That is, the tests pass and the runtime aborts - // everywhere else. - // - // However, if we catch it and try to continue with a - // Python error, then all Windows 64 bit platforms corrupt - // memory. So all we can do is manually abort, hopefully - // with a good error message. (Note that the above was - // tested WITHOUT the `/EHr` switch being used at compile - // time, so MSVC may have "optimized" out important - // checking. Using that switch, we may be in a better - // place in terms of memory corruption.) But sometimes it - // can't be caught here at all, which is confusing but not - // terribly surprising; so again, the G_NOEXCEPT_WIN32 - // plus "/EHr". - // - // Hopefully the basic C stdlib is still functional enough - // for us to at least print an error. - // - // It gets more complicated than that, though, on some - // platforms, specifically at least Linux/gcc/libstdc++. They use - // an exception to unwind the stack when a background - // thread exits. (See comments about noexcept.) So this - // may not actually represent anything untoward. On those - // platforms we allow throws of this to propagate, or - // attempt to anyway. -# if defined(WIN32) || defined(_WIN32) - Py_FatalError( - "greenlet: Unhandled C++ exception from a greenlet run function. " - "Because memory is likely corrupted, terminating process."); - std::abort(); -#else - throw; -#endif - } - } - // These lines may run arbitrary code - args.CLEAR(); - Py_CLEAR(run); - - if (!result - && mod_globs->PyExc_GreenletExit.PyExceptionMatches() - && (this->args())) { - // This can happen, for example, if our only reference - // goes away after we switch back to the parent. - // See test_dealloc_switch_args_not_lost - PyErrPieces clear_error; - result <<= this->args(); - result = single_result(result); - } - this->release_args(); - this->python_state.did_finish(PyThreadState_GET()); - - result = g_handle_exit(result); - assert(this->thread_state()->borrow_current() == this->_self); - - /* jump back to parent */ - this->stack_state.set_inactive(); /* dead */ - - - // TODO: Can we decref some things here? Release our main greenlet - // and maybe parent? - for (Greenlet* parent = this->_parent; - parent; - parent = parent->parent()) { - // We need to somewhere consume a reference to - // the result; in most cases we'll never have control - // back in this stack frame again. Calling - // green_switch actually adds another reference! - // This would probably be clearer with a specific API - // to hand results to the parent. - parent->args() <<= result; - assert(!result); - // The parent greenlet now owns the result; in the - // typical case we'll never get back here to assign to - // result and thus release the reference. - try { - result = parent->g_switch(); - } - catch (const PyErrOccurred&) { - // Ignore, keep passing the error on up. - } - - /* Return here means switch to parent failed, - * in which case we throw *current* exception - * to the next parent in chain. - */ - assert(!result); - } - /* We ran out of parents, cannot continue */ - PyErr_WriteUnraisable(this->self().borrow_o()); - Py_FatalError("greenlet: ran out of parent greenlets while propagating exception; " - "cannot continue"); - std::abort(); -} - -void -UserGreenlet::run(const BorrowedObject nrun) -{ - if (this->started()) { - throw AttributeError( - "run cannot be set " - "after the start of the greenlet"); - } - this->_run_callable = nrun; -} - -const OwnedGreenlet -UserGreenlet::parent() const -{ - return this->_parent; -} - -void -UserGreenlet::parent(const BorrowedObject raw_new_parent) -{ - if (!raw_new_parent) { - throw AttributeError("can't delete attribute"); - } - - BorrowedMainGreenlet main_greenlet_of_new_parent; - BorrowedGreenlet new_parent(raw_new_parent.borrow()); // could - // throw - // TypeError! - for (BorrowedGreenlet p = new_parent; p; p = p->parent()) { - if (p == this->_self) { - throw ValueError("cyclic parent chain"); - } - main_greenlet_of_new_parent = p->main_greenlet(); - } - - if (!main_greenlet_of_new_parent) { - throw ValueError("parent must not be garbage collected"); - } - - if (this->started() - && this->_main_greenlet != main_greenlet_of_new_parent) { - throw ValueError("parent cannot be on a different thread"); - } - - this->_parent = new_parent; -} - -void -UserGreenlet::murder_in_place() -{ - this->_main_greenlet.CLEAR(); - Greenlet::murder_in_place(); -} - -bool -UserGreenlet::belongs_to_thread(const ThreadState* thread_state) const -{ - return Greenlet::belongs_to_thread(thread_state) && this->_main_greenlet == thread_state->borrow_main_greenlet(); -} - - -int -UserGreenlet::tp_traverse(visitproc visit, void* arg) -{ - Py_VISIT(this->_parent.borrow_o()); - Py_VISIT(this->_main_greenlet.borrow_o()); - Py_VISIT(this->_run_callable.borrow_o()); - - return Greenlet::tp_traverse(visit, arg); -} - -int -UserGreenlet::tp_clear() -{ - Greenlet::tp_clear(); - this->_parent.CLEAR(); - this->_main_greenlet.CLEAR(); - this->_run_callable.CLEAR(); - return 0; -} - -UserGreenlet::ParentIsCurrentGuard::ParentIsCurrentGuard(UserGreenlet* p, - const ThreadState& thread_state) - : oldparent(p->_parent), - greenlet(p) -{ - p->_parent = thread_state.get_current(); -} - -UserGreenlet::ParentIsCurrentGuard::~ParentIsCurrentGuard() -{ - this->greenlet->_parent = oldparent; - oldparent.CLEAR(); -} - -}; //namespace greenlet -- cgit v1.2.3