summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp
diff options
context:
space:
mode:
authorcyfraeviolae <cyfraeviolae>2024-04-03 03:17:55 -0400
committercyfraeviolae <cyfraeviolae>2024-04-03 03:17:55 -0400
commit12cf076118570eebbff08c6b3090e0d4798447a1 (patch)
tree3ba25e17e3c3a5e82316558ba3864b955919ff72 /venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp
parentc45662ff3923b34614ddcc8feb9195541166dcc5 (diff)
no venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp')
-rw-r--r--venv/lib/python3.11/site-packages/greenlet/TUserGreenlet.cpp667
1 files changed, 0 insertions, 667 deletions
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> 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<UserGreenlet*>(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<UserGreenlet*>(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