Загрузить файлы в «venv/Lib/site-packages/greenlet»
This commit is contained in:
62
venv/Lib/site-packages/greenlet/TExceptionState.cpp
Normal file
62
venv/Lib/site-packages/greenlet/TExceptionState.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef GREENLET_EXCEPTION_STATE_CPP
|
||||
#define GREENLET_EXCEPTION_STATE_CPP
|
||||
|
||||
#include <Python.h>
|
||||
#include "TGreenlet.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
|
||||
ExceptionState::ExceptionState()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void ExceptionState::operator<<(const PyThreadState *const tstate) noexcept
|
||||
{
|
||||
this->exc_info = tstate->exc_info;
|
||||
this->exc_state = tstate->exc_state;
|
||||
}
|
||||
|
||||
void ExceptionState::operator>>(PyThreadState *const tstate) noexcept
|
||||
{
|
||||
tstate->exc_state = this->exc_state;
|
||||
tstate->exc_info =
|
||||
this->exc_info ? this->exc_info : &tstate->exc_state;
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void ExceptionState::clear() noexcept
|
||||
{
|
||||
this->exc_info = nullptr;
|
||||
this->exc_state.exc_value = nullptr;
|
||||
#if !GREENLET_PY311
|
||||
this->exc_state.exc_type = nullptr;
|
||||
this->exc_state.exc_traceback = nullptr;
|
||||
#endif
|
||||
this->exc_state.previous_item = nullptr;
|
||||
}
|
||||
|
||||
int ExceptionState::tp_traverse(visitproc visit, void* arg) noexcept
|
||||
{
|
||||
Py_VISIT(this->exc_state.exc_value);
|
||||
#if !GREENLET_PY311
|
||||
Py_VISIT(this->exc_state.exc_type);
|
||||
Py_VISIT(this->exc_state.exc_traceback);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExceptionState::tp_clear() noexcept
|
||||
{
|
||||
Py_CLEAR(this->exc_state.exc_value);
|
||||
#if !GREENLET_PY311
|
||||
Py_CLEAR(this->exc_state.exc_type);
|
||||
Py_CLEAR(this->exc_state.exc_traceback);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
#endif // GREENLET_EXCEPTION_STATE_CPP
|
||||
733
venv/Lib/site-packages/greenlet/TGreenlet.cpp
Normal file
733
venv/Lib/site-packages/greenlet/TGreenlet.cpp
Normal file
@@ -0,0 +1,733 @@
|
||||
/* -*- 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"
|
||||
*/
|
||||
#ifndef TGREENLET_CPP
|
||||
#define TGREENLET_CPP
|
||||
#include "greenlet_internal.hpp"
|
||||
#include "TGreenlet.hpp"
|
||||
|
||||
|
||||
#include "TGreenletGlobals.cpp"
|
||||
#include "TThreadStateDestroy.cpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
Greenlet::Greenlet(PyGreenlet* p)
|
||||
: Greenlet(p, StackState())
|
||||
{
|
||||
}
|
||||
|
||||
Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack)
|
||||
: _self(p), stack_state(initial_stack)
|
||||
{
|
||||
assert(p->pimpl == nullptr);
|
||||
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();
|
||||
this->_self->pimpl = nullptr;
|
||||
}
|
||||
|
||||
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 thread_local 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 (should be nullptr)
|
||||
// TODO: Give the objects an API to determine if they belong
|
||||
// to a dead thread.
|
||||
|
||||
const BorrowedMainGreenlet my_main_greenlet = this->find_main_greenlet_in_lineage();
|
||||
|
||||
if (!my_main_greenlet) {
|
||||
throw PyErrOccurred(mod_globs->PyExc_GreenletError,
|
||||
"cannot switch to a garbage collected greenlet");
|
||||
}
|
||||
|
||||
if (!my_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 main_greenlet_cur_thread = GET_THREAD_STATE().state().borrow_main_greenlet();
|
||||
if (
|
||||
// lineage main greenlet is not this thread's greenlet
|
||||
main_greenlet_cur_thread != my_main_greenlet
|
||||
|| (
|
||||
// atteched to some thread
|
||||
this->main_greenlet()
|
||||
// XXX: Same condition as above. Was this supposed to be
|
||||
// this->main_greenlet()?
|
||||
&& main_greenlet_cur_thread != my_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!)
|
||||
|| (!main_greenlet_cur_thread->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\n\tCurrent: %R\n\tExpected: %R",
|
||||
main_greenlet_cur_thread, my_main_greenlet);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// TODO: When we run on 3.12+ only (GREENLET_312), switch to the
|
||||
// ``PyErr_GetRaisedException`` family of functions. The
|
||||
// ``PyErr_Fetch`` family is deprecated on 3.12+, but is part
|
||||
// of the stable ABI so it's not going anywhere.
|
||||
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);
|
||||
if (own_top_frame) {
|
||||
// Throw away any saved stack state since the owned frame is cleared.
|
||||
this->stack_state.set_inactive();
|
||||
}
|
||||
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.
|
||||
//
|
||||
#if !GREENLET_PY315
|
||||
// This enum value was removed in
|
||||
// https://github.com/python/cpython/pull/141108
|
||||
|
||||
assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK);
|
||||
#endif
|
||||
|
||||
// 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
|
||||
#endif
|
||||
858
venv/Lib/site-packages/greenlet/TGreenlet.hpp
Normal file
858
venv/Lib/site-packages/greenlet/TGreenlet.hpp
Normal file
@@ -0,0 +1,858 @@
|
||||
#ifndef GREENLET_GREENLET_HPP
|
||||
#define GREENLET_GREENLET_HPP
|
||||
/*
|
||||
* Declarations of the core data structures.
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "greenlet_compiler_compat.hpp"
|
||||
#include "greenlet_refs.hpp"
|
||||
#include "greenlet_cpython_compat.hpp"
|
||||
#include "greenlet_allocator.hpp"
|
||||
|
||||
using greenlet::refs::OwnedObject;
|
||||
using greenlet::refs::OwnedGreenlet;
|
||||
using greenlet::refs::OwnedMainGreenlet;
|
||||
using greenlet::refs::BorrowedGreenlet;
|
||||
|
||||
#if PY_VERSION_HEX < 0x30B00A6
|
||||
// prior to 3.11.0a6
|
||||
# define _PyCFrame CFrame
|
||||
# define _PyInterpreterFrame _interpreter_frame
|
||||
#endif
|
||||
|
||||
#if GREENLET_PY312
|
||||
# define Py_BUILD_CORE
|
||||
# include "internal/pycore_frame.h"
|
||||
#endif
|
||||
|
||||
#if GREENLET_PY314
|
||||
# include "internal/pycore_interpframe_structs.h"
|
||||
#if defined(_MSC_VER) || defined(__MINGW64__)
|
||||
# include "greenlet_msvc_compat.hpp"
|
||||
#else
|
||||
# include "internal/pycore_interpframe.h"
|
||||
#endif
|
||||
#ifdef Py_GIL_DISABLED
|
||||
# include "internal/pycore_tstate.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if GREENLET_PY315
|
||||
#include "internal/pycore_gc.h"
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW64__)
|
||||
#include "internal/pycore_stackref.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// XXX: TODO: Work to remove all virtual functions
|
||||
// for speed of calling and size of objects (no vtable).
|
||||
// One pattern is the Curiously Recurring Template
|
||||
namespace greenlet
|
||||
{
|
||||
class ExceptionState
|
||||
{
|
||||
private:
|
||||
G_NO_COPIES_OF_CLS(ExceptionState);
|
||||
|
||||
// Even though these are borrowed objects, we actually own
|
||||
// them, when they're not null.
|
||||
// XXX: Express that in the API.
|
||||
private:
|
||||
_PyErr_StackItem* exc_info;
|
||||
_PyErr_StackItem exc_state;
|
||||
public:
|
||||
ExceptionState();
|
||||
void operator<<(const PyThreadState *const tstate) noexcept;
|
||||
void operator>>(PyThreadState* tstate) noexcept;
|
||||
void clear() noexcept;
|
||||
|
||||
int tp_traverse(visitproc visit, void* arg) noexcept;
|
||||
void tp_clear() noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void operator<<(const PyThreadState *const tstate, T& exc);
|
||||
|
||||
class PythonStateContext
|
||||
{
|
||||
protected:
|
||||
greenlet::refs::OwnedContext _context;
|
||||
public:
|
||||
inline const greenlet::refs::OwnedContext& context() const
|
||||
{
|
||||
return this->_context;
|
||||
}
|
||||
inline greenlet::refs::OwnedContext& context()
|
||||
{
|
||||
return this->_context;
|
||||
}
|
||||
|
||||
inline void tp_clear()
|
||||
{
|
||||
this->_context.CLEAR();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static PyObject* context(T* tstate)
|
||||
{
|
||||
return tstate->context;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static void context(T* tstate, PyObject* new_context)
|
||||
{
|
||||
tstate->context = new_context;
|
||||
tstate->context_ver++;
|
||||
}
|
||||
};
|
||||
class SwitchingArgs;
|
||||
class PythonState : public PythonStateContext
|
||||
{
|
||||
public:
|
||||
typedef greenlet::refs::OwnedReference<struct _frame> OwnedFrame;
|
||||
private:
|
||||
G_NO_COPIES_OF_CLS(PythonState);
|
||||
// We own this if we're suspended (although currently we don't
|
||||
// tp_traverse into it; that's a TODO). If we're running, it's
|
||||
// empty. If we get deallocated and *still* have a frame, it
|
||||
// won't be reachable from the place that normally decref's
|
||||
// it, so we need to do it (hence owning it).
|
||||
OwnedFrame _top_frame;
|
||||
#if GREENLET_USE_CFRAME
|
||||
_PyCFrame* cframe;
|
||||
int use_tracing;
|
||||
#endif
|
||||
#if GREENLET_PY314
|
||||
int py_recursion_depth;
|
||||
// I think this is only used by the JIT. At least,
|
||||
// we only got errors not switching it when the JIT was enabled.
|
||||
// Python/generated_cases.c.h:12469: _PyEval_EvalFrameDefault:
|
||||
// Assertion `tstate->current_executor == NULL' failed.
|
||||
// see https://github.com/python-greenlet/greenlet/issues/460
|
||||
PyObject* current_executor;
|
||||
_PyStackRef* stackpointer;
|
||||
#ifdef Py_GIL_DISABLED
|
||||
_PyCStackRef* c_stack_refs;
|
||||
#endif
|
||||
#elif GREENLET_PY312
|
||||
int py_recursion_depth;
|
||||
int c_recursion_depth;
|
||||
#else
|
||||
int recursion_depth;
|
||||
#endif
|
||||
#if GREENLET_PY313
|
||||
PyObject* delete_later;
|
||||
uintptr_t critical_section;
|
||||
#else
|
||||
int trash_delete_nesting;
|
||||
#endif
|
||||
#if GREENLET_PY311
|
||||
_PyInterpreterFrame* current_frame;
|
||||
_PyStackChunk* datastack_chunk;
|
||||
PyObject** datastack_top;
|
||||
PyObject** datastack_limit;
|
||||
#endif
|
||||
// The PyInterpreterFrame list on 3.12+ contains some entries that are
|
||||
// on the C stack, which can't be directly accessed while a greenlet is
|
||||
// suspended. In order to keep greenlet gr_frame introspection working,
|
||||
// we adjust stack switching to rewrite the interpreter frame list
|
||||
// to skip these C-stack frames; we call this "exposing" the greenlet's
|
||||
// frames because it makes them valid to work with in Python. Then when
|
||||
// the greenlet is resumed we need to remember to reverse the operation
|
||||
// we did. The C-stack frames are "entry frames" which are a low-level
|
||||
// interpreter detail; they're not needed for introspection, but do
|
||||
// need to be present for the eval loop to work.
|
||||
void unexpose_frames();
|
||||
|
||||
public:
|
||||
|
||||
PythonState();
|
||||
// You can use this for testing whether we have a frame
|
||||
// or not. It returns const so they can't modify it.
|
||||
const OwnedFrame& top_frame() const noexcept;
|
||||
|
||||
inline void operator<<(const PyThreadState *const tstate) noexcept;
|
||||
inline void operator>>(PyThreadState* tstate) noexcept;
|
||||
void clear() noexcept;
|
||||
|
||||
int tp_traverse(visitproc visit, void* arg, bool visit_top_frame) noexcept;
|
||||
void tp_clear(bool own_top_frame) noexcept;
|
||||
void set_initial_state(const PyThreadState* const tstate) noexcept;
|
||||
#if GREENLET_USE_CFRAME
|
||||
void set_new_cframe(_PyCFrame& frame) noexcept;
|
||||
#endif
|
||||
|
||||
void may_switch_away() noexcept;
|
||||
inline void will_switch_from(PyThreadState *const origin_tstate) noexcept;
|
||||
void did_finish(PyThreadState* tstate) noexcept;
|
||||
};
|
||||
|
||||
class StackState
|
||||
{
|
||||
// By having only plain C (POD) members, no virtual functions
|
||||
// or bases, we get a trivial assignment operator generated
|
||||
// for us. However, that's not safe since we do manage memory.
|
||||
// So we declare an assignment operator that only works if we
|
||||
// don't have any memory allocated. (We don't use
|
||||
// std::shared_ptr for reference counting just to keep this
|
||||
// object small)
|
||||
private:
|
||||
char* _stack_start;
|
||||
char* stack_stop;
|
||||
char* stack_copy;
|
||||
intptr_t _stack_saved;
|
||||
StackState* stack_prev;
|
||||
inline int copy_stack_to_heap_up_to(const char* const stop) noexcept;
|
||||
inline void free_stack_copy() noexcept;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a started, but inactive, state, using *current*
|
||||
* as the previous.
|
||||
*/
|
||||
StackState(void* mark, StackState& current);
|
||||
/**
|
||||
* Creates an inactive, unstarted, state.
|
||||
*/
|
||||
StackState();
|
||||
~StackState();
|
||||
StackState(const StackState& other);
|
||||
StackState& operator=(const StackState& other);
|
||||
inline void copy_heap_to_stack(const StackState& current) noexcept;
|
||||
inline int copy_stack_to_heap(char* const stackref, const StackState& current) noexcept;
|
||||
inline bool started() const noexcept;
|
||||
inline bool main() const noexcept;
|
||||
inline bool active() const noexcept;
|
||||
inline void set_active() noexcept;
|
||||
inline void set_inactive() noexcept;
|
||||
inline intptr_t stack_saved() const noexcept;
|
||||
inline char* stack_start() const noexcept;
|
||||
static inline StackState make_main() noexcept;
|
||||
#ifdef GREENLET_USE_STDIO
|
||||
friend std::ostream& operator<<(std::ostream& os, const StackState& s);
|
||||
#endif
|
||||
|
||||
// Fill in [dest, dest + n) with the values that would be at
|
||||
// [src, src + n) while this greenlet is running. This is like memcpy
|
||||
// except that if the greenlet is suspended it accounts for the portion
|
||||
// of the greenlet's stack that was spilled to the heap. `src` may
|
||||
// be on this greenlet's stack, or on the heap, but not on a different
|
||||
// greenlet's stack.
|
||||
void copy_from_stack(void* dest, const void* src, size_t n) const;
|
||||
};
|
||||
#ifdef GREENLET_USE_STDIO
|
||||
std::ostream& operator<<(std::ostream& os, const StackState& s);
|
||||
#endif
|
||||
|
||||
class SwitchingArgs
|
||||
{
|
||||
private:
|
||||
G_NO_ASSIGNMENT_OF_CLS(SwitchingArgs);
|
||||
// If args and kwargs are both false (NULL), this is a *throw*, not a
|
||||
// switch. PyErr_... must have been called already.
|
||||
OwnedObject _args;
|
||||
OwnedObject _kwargs;
|
||||
public:
|
||||
|
||||
SwitchingArgs()
|
||||
{}
|
||||
|
||||
SwitchingArgs(const OwnedObject& args, const OwnedObject& kwargs)
|
||||
: _args(args),
|
||||
_kwargs(kwargs)
|
||||
{}
|
||||
|
||||
SwitchingArgs(const SwitchingArgs& other)
|
||||
: _args(other._args),
|
||||
_kwargs(other._kwargs)
|
||||
{}
|
||||
|
||||
const OwnedObject& args()
|
||||
{
|
||||
return this->_args;
|
||||
}
|
||||
|
||||
const OwnedObject& kwargs()
|
||||
{
|
||||
return this->_kwargs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves ownership from the argument to this object.
|
||||
*/
|
||||
SwitchingArgs& operator<<=(SwitchingArgs& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
this->_args = other._args;
|
||||
this->_kwargs = other._kwargs;
|
||||
other.CLEAR();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires ownership of the argument (consumes the reference).
|
||||
*/
|
||||
SwitchingArgs& operator<<=(PyObject* args)
|
||||
{
|
||||
this->_args = OwnedObject::consuming(args);
|
||||
this->_kwargs.CLEAR();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires ownership of the argument.
|
||||
*
|
||||
* Sets the args to be the given value; clears the kwargs.
|
||||
*/
|
||||
SwitchingArgs& operator<<=(OwnedObject& args)
|
||||
{
|
||||
assert(&args != &this->_args);
|
||||
this->_args = args;
|
||||
this->_kwargs.CLEAR();
|
||||
args.CLEAR();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return this->_args || this->_kwargs;
|
||||
}
|
||||
|
||||
inline void CLEAR()
|
||||
{
|
||||
this->_args.CLEAR();
|
||||
this->_kwargs.CLEAR();
|
||||
}
|
||||
|
||||
const std::string as_str() const noexcept
|
||||
{
|
||||
return PyUnicode_AsUTF8(
|
||||
OwnedObject::consuming(
|
||||
PyUnicode_FromFormat(
|
||||
"SwitchingArgs(args=%R, kwargs=%R)",
|
||||
this->_args.borrow(),
|
||||
this->_kwargs.borrow()
|
||||
)
|
||||
).borrow()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadState;
|
||||
|
||||
class UserGreenlet;
|
||||
class MainGreenlet;
|
||||
|
||||
class Greenlet
|
||||
{
|
||||
private:
|
||||
G_NO_COPIES_OF_CLS(Greenlet);
|
||||
PyGreenlet* const _self;
|
||||
private:
|
||||
// XXX: Work to remove these.
|
||||
friend class ThreadState;
|
||||
friend class UserGreenlet;
|
||||
friend class MainGreenlet;
|
||||
protected:
|
||||
ExceptionState exception_state;
|
||||
SwitchingArgs switch_args;
|
||||
StackState stack_state;
|
||||
PythonState python_state;
|
||||
Greenlet(PyGreenlet* p, const StackState& initial_state);
|
||||
public:
|
||||
// This constructor takes ownership of the PyGreenlet, by
|
||||
// setting ``p->pimpl = this;``.
|
||||
Greenlet(PyGreenlet* p);
|
||||
virtual ~Greenlet();
|
||||
|
||||
const OwnedObject context() const;
|
||||
|
||||
// You MUST call this _very_ early in the switching process to
|
||||
// prepare anything that may need prepared. This might perform
|
||||
// garbage collections or otherwise run arbitrary Python code.
|
||||
//
|
||||
// One specific use of it is for Python 3.11+, preventing
|
||||
// running arbitrary code at unsafe times. See
|
||||
// PythonState::may_switch_away().
|
||||
inline void may_switch_away()
|
||||
{
|
||||
this->python_state.may_switch_away();
|
||||
}
|
||||
|
||||
inline void context(refs::BorrowedObject new_context);
|
||||
|
||||
inline SwitchingArgs& args()
|
||||
{
|
||||
return this->switch_args;
|
||||
}
|
||||
|
||||
virtual const refs::BorrowedMainGreenlet main_greenlet() const = 0;
|
||||
|
||||
inline intptr_t stack_saved() const noexcept
|
||||
{
|
||||
return this->stack_state.stack_saved();
|
||||
}
|
||||
|
||||
// This is used by the macro SLP_SAVE_STATE to compute the
|
||||
// difference in stack sizes. It might be nice to handle the
|
||||
// computation ourself, but the type of the result
|
||||
// varies by platform, so doing it in the macro is the
|
||||
// simplest way.
|
||||
inline const char* stack_start() const noexcept
|
||||
{
|
||||
return this->stack_state.stack_start();
|
||||
}
|
||||
|
||||
virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state);
|
||||
|
||||
/**
|
||||
* Depends on the state of this->args() or the current Python
|
||||
* error indicator. Thus, it is not threadsafe or reentrant.
|
||||
* You (you being ``green_switch``, the Python-level
|
||||
* ``greenlet.switch`` method) should call
|
||||
* ``check_switch_allowed`` in free-threaded builds before
|
||||
* calling this method and catch ``PyErrOccurred`` if it isn't
|
||||
* a valid switch. This method should also call that method
|
||||
* because there are places where we can switch internally
|
||||
* without going through the Python method.
|
||||
*/
|
||||
virtual OwnedObject g_switch() = 0;
|
||||
/**
|
||||
* Force the greenlet to appear dead. Used when it's not
|
||||
* possible to throw an exception into a greenlet anymore.
|
||||
*
|
||||
* This losses access to the thread state and the main greenlet.
|
||||
*/
|
||||
virtual void murder_in_place();
|
||||
|
||||
/**
|
||||
* Called when somebody notices we were running in a dead
|
||||
* thread to allow cleaning up resources (because we can't
|
||||
* raise GreenletExit into it anymore).
|
||||
* This is very similar to ``murder_in_place()``, except that
|
||||
* it DOES NOT lose the main greenlet or thread state.
|
||||
*/
|
||||
inline void deactivate_and_free();
|
||||
|
||||
|
||||
// Called when some thread wants to deallocate a greenlet
|
||||
// object.
|
||||
// The thread may or may not be the same thread the greenlet
|
||||
// was running in.
|
||||
// The thread state will be null if the thread the greenlet
|
||||
// was running in was known to have exited.
|
||||
void deallocing_greenlet_in_thread(const ThreadState* current_state);
|
||||
|
||||
// Must be called on 3.12+ before exposing a suspended greenlet's
|
||||
// frames to user code. This rewrites the linked list of interpreter
|
||||
// frames to skip the ones that are being stored on the C stack (which
|
||||
// can't be safely accessed while the greenlet is suspended because
|
||||
// that stack space might be hosting a different greenlet), and
|
||||
// sets PythonState::frames_were_exposed so we remember to restore
|
||||
// the original list before resuming the greenlet. The C-stack frames
|
||||
// are a low-level interpreter implementation detail; while they're
|
||||
// important to the bytecode eval loop, they're superfluous for
|
||||
// introspection purposes.
|
||||
void expose_frames();
|
||||
|
||||
|
||||
// TODO: Figure out how to make these non-public.
|
||||
inline void slp_restore_state() noexcept;
|
||||
inline int slp_save_state(char *const stackref) noexcept;
|
||||
|
||||
inline bool is_currently_running_in_some_thread() const;
|
||||
virtual bool belongs_to_thread(const ThreadState* state) const;
|
||||
|
||||
inline bool started() const
|
||||
{
|
||||
return this->stack_state.started();
|
||||
}
|
||||
inline bool active() const
|
||||
{
|
||||
return this->stack_state.active();
|
||||
}
|
||||
inline bool main() const
|
||||
{
|
||||
return this->stack_state.main();
|
||||
}
|
||||
virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const = 0;
|
||||
|
||||
virtual const OwnedGreenlet parent() const = 0;
|
||||
virtual void parent(const refs::BorrowedObject new_parent) = 0;
|
||||
|
||||
inline const PythonState::OwnedFrame& top_frame()
|
||||
{
|
||||
return this->python_state.top_frame();
|
||||
}
|
||||
|
||||
virtual const OwnedObject& run() const = 0;
|
||||
virtual void run(const refs::BorrowedObject nrun) = 0;
|
||||
|
||||
|
||||
virtual int tp_traverse(visitproc visit, void* arg);
|
||||
virtual int tp_clear();
|
||||
|
||||
|
||||
// Return the thread state that the greenlet is running in, or
|
||||
// null if the greenlet is not running or the thread is known
|
||||
// to have exited.
|
||||
virtual ThreadState* thread_state() const noexcept = 0;
|
||||
|
||||
// Return true if the greenlet is known to have been running
|
||||
// (active) in a thread that has now exited.
|
||||
virtual bool was_running_in_dead_thread() const noexcept = 0;
|
||||
|
||||
// Return a borrowed greenlet that is the Python object
|
||||
// this object represents.
|
||||
inline BorrowedGreenlet self() const noexcept
|
||||
{
|
||||
return BorrowedGreenlet(this->_self);
|
||||
}
|
||||
|
||||
// For testing. If this returns true, we should pretend that
|
||||
// slp_switch() failed.
|
||||
virtual bool force_slp_switch_error() const noexcept;
|
||||
|
||||
// Check the preconditions for switching to this greenlet; if they
|
||||
// aren't met, throws PyErrOccurred. Most callers will want to
|
||||
// catch this and clear the arguments if they've been set.
|
||||
inline void check_switch_allowed() const;
|
||||
|
||||
protected:
|
||||
inline void release_args();
|
||||
|
||||
// The functions that must not be inlined are declared virtual.
|
||||
// We also mark them as protected, not private, so that the
|
||||
// compiler is forced to call them through a function pointer.
|
||||
// (A sufficiently smart compiler could directly call a private
|
||||
// virtual function since it can never be overridden in a
|
||||
// subclass).
|
||||
|
||||
// Also TODO: Switch away from integer error codes and to enums,
|
||||
// or throw exceptions when possible.
|
||||
struct switchstack_result_t
|
||||
{
|
||||
int status;
|
||||
Greenlet* the_new_current_greenlet;
|
||||
OwnedGreenlet origin_greenlet;
|
||||
|
||||
switchstack_result_t()
|
||||
: status(0),
|
||||
the_new_current_greenlet(nullptr)
|
||||
{}
|
||||
|
||||
switchstack_result_t(int err)
|
||||
: status(err),
|
||||
the_new_current_greenlet(nullptr)
|
||||
{}
|
||||
|
||||
switchstack_result_t(int err, Greenlet* state, OwnedGreenlet& origin)
|
||||
: status(err),
|
||||
the_new_current_greenlet(state),
|
||||
origin_greenlet(origin)
|
||||
{
|
||||
}
|
||||
|
||||
switchstack_result_t(int err, Greenlet* state, const BorrowedGreenlet& origin)
|
||||
: status(err),
|
||||
the_new_current_greenlet(state),
|
||||
origin_greenlet(origin)
|
||||
{
|
||||
}
|
||||
|
||||
switchstack_result_t(const switchstack_result_t& other)
|
||||
: status(other.status),
|
||||
the_new_current_greenlet(other.the_new_current_greenlet),
|
||||
origin_greenlet(other.origin_greenlet)
|
||||
{}
|
||||
|
||||
switchstack_result_t& operator=(const switchstack_result_t& other)
|
||||
{
|
||||
this->status = other.status;
|
||||
this->the_new_current_greenlet = other.the_new_current_greenlet;
|
||||
this->origin_greenlet = other.origin_greenlet;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
OwnedObject on_switchstack_or_initialstub_failure(
|
||||
Greenlet* target,
|
||||
const switchstack_result_t& err,
|
||||
const bool target_was_me=false,
|
||||
const bool was_initial_stub=false);
|
||||
|
||||
// Returns the previous greenlet we just switched away from.
|
||||
virtual OwnedGreenlet g_switchstack_success() noexcept;
|
||||
|
||||
class GreenletStartedWhileInPython : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
GreenletStartedWhileInPython() : std::runtime_error("")
|
||||
{}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
/**
|
||||
Perform a stack switch into this greenlet.
|
||||
|
||||
This temporarily sets the global variable
|
||||
``switching_thread_state`` to this greenlet; as soon as the
|
||||
call to ``slp_switch`` completes, this is reset to NULL.
|
||||
Consequently, this depends on the GIL.
|
||||
|
||||
TODO: Adopt the stackman model and pass ``slp_switch`` a
|
||||
callback function and context pointer; this eliminates the
|
||||
need for global variables altogether.
|
||||
|
||||
Because the stack switch happens in this function, this
|
||||
function can't use its own stack (local) variables, set
|
||||
before the switch, and then accessed after the switch.
|
||||
|
||||
Further, you con't even access ``g_thread_state_global``
|
||||
before and after the switch from the global variable.
|
||||
Because it is thread local some compilers cache it in a
|
||||
register/on the stack, notably new versions of MSVC; this
|
||||
breaks with strange crashes sometime later, because writing
|
||||
to anything in ``g_thread_state_global`` after the switch
|
||||
is actually writing to random memory. For this reason, we
|
||||
call a non-inlined function to finish the operation. (XXX:
|
||||
The ``/GT`` MSVC compiler argument probably fixes that.)
|
||||
|
||||
It is very important that stack switch is 'atomic', i.e. no
|
||||
calls into other Python code allowed (except very few that
|
||||
are safe), because global variables are very fragile. (This
|
||||
should no longer be the case with thread-local variables.)
|
||||
|
||||
*/
|
||||
// Made virtual to facilitate subclassing UserGreenlet for testing.
|
||||
virtual switchstack_result_t g_switchstack(void);
|
||||
|
||||
class TracingGuard
|
||||
{
|
||||
private:
|
||||
PyThreadState* tstate;
|
||||
public:
|
||||
TracingGuard()
|
||||
: tstate(PyThreadState_GET())
|
||||
{
|
||||
PyThreadState_EnterTracing(this->tstate);
|
||||
}
|
||||
|
||||
~TracingGuard()
|
||||
{
|
||||
PyThreadState_LeaveTracing(this->tstate);
|
||||
this->tstate = nullptr;
|
||||
}
|
||||
|
||||
inline void CallTraceFunction(const OwnedObject& tracefunc,
|
||||
const greenlet::refs::ImmortalEventName& event,
|
||||
const BorrowedGreenlet& origin,
|
||||
const BorrowedGreenlet& target)
|
||||
{
|
||||
// TODO: This calls tracefunc(event, (origin, target)). Add a shortcut
|
||||
// function for that that's specialized to avoid the Py_BuildValue
|
||||
// string parsing, or start with just using "ON" format with PyTuple_Pack(2,
|
||||
// origin, target). That seems like what the N format is meant
|
||||
// for.
|
||||
// XXX: Why does event not automatically cast back to a PyObject?
|
||||
// It tries to call the "deleted constructor ImmortalEventName
|
||||
// const" instead.
|
||||
assert(tracefunc);
|
||||
assert(event);
|
||||
assert(origin);
|
||||
assert(target);
|
||||
greenlet::refs::NewReference retval(
|
||||
PyObject_CallFunction(
|
||||
tracefunc.borrow(),
|
||||
"O(OO)",
|
||||
event.borrow(),
|
||||
origin.borrow(),
|
||||
target.borrow()
|
||||
));
|
||||
if (!retval) {
|
||||
throw PyErrOccurred::from_current();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
g_calltrace(const OwnedObject& tracefunc,
|
||||
const greenlet::refs::ImmortalEventName& event,
|
||||
const greenlet::refs::BorrowedGreenlet& origin,
|
||||
const BorrowedGreenlet& target);
|
||||
private:
|
||||
OwnedObject g_switch_finish(const switchstack_result_t& err);
|
||||
|
||||
};
|
||||
|
||||
class UserGreenlet : public Greenlet
|
||||
{
|
||||
private:
|
||||
static greenlet::PythonAllocator<UserGreenlet> allocator;
|
||||
OwnedMainGreenlet _main_greenlet;
|
||||
OwnedObject _run_callable;
|
||||
OwnedGreenlet _parent;
|
||||
public:
|
||||
static void* operator new(size_t UNUSED(count));
|
||||
static void operator delete(void* ptr);
|
||||
|
||||
UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent);
|
||||
virtual ~UserGreenlet();
|
||||
|
||||
virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const;
|
||||
virtual bool was_running_in_dead_thread() const noexcept;
|
||||
virtual ThreadState* thread_state() const noexcept;
|
||||
virtual OwnedObject g_switch();
|
||||
virtual const OwnedObject& run() const
|
||||
{
|
||||
if (this->started() || !this->_run_callable) {
|
||||
throw AttributeError("run");
|
||||
}
|
||||
return this->_run_callable;
|
||||
}
|
||||
virtual void run(const refs::BorrowedObject nrun);
|
||||
|
||||
virtual const OwnedGreenlet parent() const;
|
||||
virtual void parent(const refs::BorrowedObject new_parent);
|
||||
|
||||
virtual const refs::BorrowedMainGreenlet main_greenlet() const;
|
||||
|
||||
virtual void murder_in_place();
|
||||
virtual bool belongs_to_thread(const ThreadState* state) const;
|
||||
virtual int tp_traverse(visitproc visit, void* arg);
|
||||
virtual int tp_clear();
|
||||
class ParentIsCurrentGuard
|
||||
{
|
||||
private:
|
||||
OwnedGreenlet oldparent;
|
||||
UserGreenlet* greenlet;
|
||||
G_NO_COPIES_OF_CLS(ParentIsCurrentGuard);
|
||||
public:
|
||||
ParentIsCurrentGuard(UserGreenlet* p, const ThreadState& thread_state);
|
||||
~ParentIsCurrentGuard();
|
||||
};
|
||||
virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state);
|
||||
protected:
|
||||
virtual switchstack_result_t g_initialstub(void* mark);
|
||||
private:
|
||||
// This function isn't meant to return.
|
||||
// This accepts raw pointers and the ownership of them at the
|
||||
// same time. The caller should use ``inner_bootstrap(origin.relinquish_ownership())``.
|
||||
void inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run);
|
||||
};
|
||||
|
||||
class BrokenGreenlet : public UserGreenlet
|
||||
{
|
||||
private:
|
||||
static greenlet::PythonAllocator<BrokenGreenlet> allocator;
|
||||
public:
|
||||
bool _force_switch_error = false;
|
||||
bool _force_slp_switch_error = false;
|
||||
|
||||
static void* operator new(size_t UNUSED(count));
|
||||
static void operator delete(void* ptr);
|
||||
BrokenGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent)
|
||||
: UserGreenlet(p, the_parent)
|
||||
{}
|
||||
virtual ~BrokenGreenlet()
|
||||
{}
|
||||
|
||||
virtual switchstack_result_t g_switchstack(void);
|
||||
virtual bool force_slp_switch_error() const noexcept;
|
||||
|
||||
};
|
||||
|
||||
class MainGreenlet : public Greenlet
|
||||
{
|
||||
private:
|
||||
static greenlet::PythonAllocator<MainGreenlet> allocator;
|
||||
refs::BorrowedMainGreenlet _self;
|
||||
std::atomic<ThreadState*> _thread_state;
|
||||
G_NO_COPIES_OF_CLS(MainGreenlet);
|
||||
public:
|
||||
static void* operator new(size_t UNUSED(count));
|
||||
static void operator delete(void* ptr);
|
||||
|
||||
MainGreenlet(refs::BorrowedMainGreenlet::PyType*, ThreadState*);
|
||||
virtual ~MainGreenlet();
|
||||
|
||||
|
||||
virtual const OwnedObject& run() const;
|
||||
virtual void run(const refs::BorrowedObject nrun);
|
||||
|
||||
virtual const OwnedGreenlet parent() const;
|
||||
virtual void parent(const refs::BorrowedObject new_parent);
|
||||
|
||||
virtual const refs::BorrowedMainGreenlet main_greenlet() const;
|
||||
|
||||
virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const;
|
||||
virtual bool was_running_in_dead_thread() const noexcept;
|
||||
virtual ThreadState* thread_state() const noexcept;
|
||||
void thread_state(ThreadState*) noexcept;
|
||||
virtual OwnedObject g_switch();
|
||||
virtual int tp_traverse(visitproc visit, void* arg);
|
||||
};
|
||||
|
||||
// Instantiate one on the stack to save the GC state,
|
||||
// and then disable GC. When it goes out of scope, GC will be
|
||||
// restored to its original state.
|
||||
class GCDisabledGuard
|
||||
{
|
||||
private:
|
||||
int was_enabled = 0;
|
||||
public:
|
||||
GCDisabledGuard()
|
||||
: was_enabled(PyGC_IsEnabled())
|
||||
{
|
||||
PyGC_Disable();
|
||||
}
|
||||
|
||||
~GCDisabledGuard()
|
||||
{
|
||||
if (this->was_enabled) {
|
||||
PyGC_Enable();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept;
|
||||
|
||||
//TODO: Greenlet::g_switch() should call this automatically on its
|
||||
//return value. As it is, the module code is calling it.
|
||||
static inline OwnedObject
|
||||
single_result(const OwnedObject& results)
|
||||
{
|
||||
if (results
|
||||
&& PyTuple_Check(results.borrow())
|
||||
&& PyTuple_GET_SIZE(results.borrow()) == 1) {
|
||||
PyObject* result = PyTuple_GET_ITEM(results.borrow(), 0);
|
||||
assert(result);
|
||||
return OwnedObject::owning(result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
static OwnedObject
|
||||
g_handle_exit(const OwnedObject& greenlet_result);
|
||||
|
||||
|
||||
template<typename T>
|
||||
void operator<<(const PyThreadState *const lhs, T& rhs)
|
||||
{
|
||||
rhs.operator<<(lhs);
|
||||
}
|
||||
|
||||
} // namespace greenlet ;
|
||||
|
||||
#endif
|
||||
113
venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp
Normal file
113
venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of GreenletGlobals.
|
||||
*
|
||||
* 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"
|
||||
*/
|
||||
#ifndef T_GREENLET_GLOBALS
|
||||
#define T_GREENLET_GLOBALS
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "greenlet_refs.hpp"
|
||||
#include "greenlet_exceptions.hpp"
|
||||
#include "greenlet_thread_support.hpp"
|
||||
#include "greenlet_internal.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
// This encapsulates what were previously module global "constants"
|
||||
// established at init time.
|
||||
// This is a step towards Python3 style module state that allows
|
||||
// reloading.
|
||||
//
|
||||
// In an earlier iteration of this code, we used placement new to be
|
||||
// able to allocate this object statically still, so that references
|
||||
// to its members don't incur an extra pointer indirection.
|
||||
// But under some scenarios, that could result in crashes at
|
||||
// shutdown because apparently the destructor was getting run twice?
|
||||
class GreenletGlobals
|
||||
{
|
||||
|
||||
public:
|
||||
const greenlet::refs::ImmortalEventName event_switch;
|
||||
const greenlet::refs::ImmortalEventName event_throw;
|
||||
const greenlet::refs::ImmortalException PyExc_GreenletError;
|
||||
const greenlet::refs::ImmortalException PyExc_GreenletExit;
|
||||
const greenlet::refs::ImmortalObject empty_tuple;
|
||||
const greenlet::refs::ImmortalObject empty_dict;
|
||||
const greenlet::refs::ImmortalString str_run;
|
||||
Mutex* const thread_states_to_destroy_lock;
|
||||
greenlet::cleanup_queue_t thread_states_to_destroy;
|
||||
|
||||
GreenletGlobals() :
|
||||
event_switch("switch"),
|
||||
event_throw("throw"),
|
||||
PyExc_GreenletError("greenlet.error"),
|
||||
PyExc_GreenletExit("greenlet.GreenletExit", PyExc_BaseException),
|
||||
empty_tuple(Require(PyTuple_New(0))),
|
||||
empty_dict(Require(PyDict_New())),
|
||||
str_run("run"),
|
||||
thread_states_to_destroy_lock(new Mutex())
|
||||
{}
|
||||
|
||||
~GreenletGlobals()
|
||||
{
|
||||
// This object is (currently) effectively immortal, and not
|
||||
// just because of those placement new tricks; if we try to
|
||||
// deallocate the static object we allocated, and overwrote,
|
||||
// we would be doing so at C++ teardown time, which is after
|
||||
// the final Python GIL is released, and we can't use the API
|
||||
// then.
|
||||
// (The members will still be destructed, but they also don't
|
||||
// do any deallocation.)
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be holding the ``thread_states_to_destroy`` lock.
|
||||
*/
|
||||
void queue_to_destroy(ThreadState* ts) const
|
||||
{
|
||||
// we're currently accessed through a static const object,
|
||||
// implicitly marking our members as const, so code can't just
|
||||
// call push_back (or pop_back) without casting away the
|
||||
// const.
|
||||
//
|
||||
// Do that for callers.
|
||||
greenlet::cleanup_queue_t& q = const_cast<greenlet::cleanup_queue_t&>(
|
||||
this->thread_states_to_destroy);
|
||||
// make sure we don't ever try to clean up a state more than
|
||||
// once. Because they're thread-local, and we ultimately call this
|
||||
// method from the destructor of the thread local variable,
|
||||
// we should never find the item already present. This check
|
||||
// is nominally O(n) in the size of the vector.
|
||||
assert(std::find(q.begin(), q.end(), ts) == q.end());
|
||||
q.push_back(ts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be holding the ``thread_states_to_destroy`` lock.
|
||||
*/
|
||||
ThreadState* take_next_to_destroy() const
|
||||
{
|
||||
greenlet::cleanup_queue_t& q = const_cast<greenlet::cleanup_queue_t&>(
|
||||
this->thread_states_to_destroy);
|
||||
if (q.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
ThreadState* result = q.back();
|
||||
q.pop_back();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
static const greenlet::GreenletGlobals* mod_globs;
|
||||
|
||||
#endif // T_GREENLET_GLOBALS
|
||||
163
venv/Lib/site-packages/greenlet/TMainGreenlet.cpp
Normal file
163
venv/Lib/site-packages/greenlet/TMainGreenlet.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of greenlet::MainGreenlet.
|
||||
*
|
||||
* 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"
|
||||
*/
|
||||
#ifndef T_MAIN_GREENLET_CPP
|
||||
#define T_MAIN_GREENLET_CPP
|
||||
|
||||
#include "TGreenlet.hpp"
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
// Incremented when we create a main greenlet, in a new thread, decremented
|
||||
// when it is destroyed.
|
||||
#ifdef Py_GIL_DISABLED
|
||||
static std::atomic<Py_ssize_t> G_TOTAL_MAIN_GREENLETS(0);
|
||||
#else
|
||||
// Protected by the GIL.
|
||||
static Py_ssize_t G_TOTAL_MAIN_GREENLETS;
|
||||
#endif
|
||||
|
||||
namespace greenlet {
|
||||
greenlet::PythonAllocator<MainGreenlet> MainGreenlet::allocator;
|
||||
|
||||
void* MainGreenlet::operator new(size_t UNUSED(count))
|
||||
{
|
||||
return allocator.allocate(1);
|
||||
}
|
||||
|
||||
|
||||
void MainGreenlet::operator delete(void* ptr)
|
||||
{
|
||||
return allocator.deallocate(static_cast<MainGreenlet*>(ptr),
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
MainGreenlet::MainGreenlet(PyGreenlet* p, ThreadState* state)
|
||||
: Greenlet(p, StackState::make_main()),
|
||||
_self(p),
|
||||
_thread_state(state)
|
||||
{
|
||||
G_TOTAL_MAIN_GREENLETS++;
|
||||
}
|
||||
|
||||
MainGreenlet::~MainGreenlet()
|
||||
{
|
||||
G_TOTAL_MAIN_GREENLETS--;
|
||||
this->tp_clear();
|
||||
}
|
||||
|
||||
ThreadState*
|
||||
MainGreenlet::thread_state() const noexcept
|
||||
{
|
||||
return this->_thread_state;
|
||||
}
|
||||
|
||||
void
|
||||
MainGreenlet::thread_state(ThreadState* t) noexcept
|
||||
{
|
||||
// this method is only used during thread tear down, when it is
|
||||
// called with nullptr, signalling the thread is dead.
|
||||
assert(!t);
|
||||
this->_thread_state = t;
|
||||
}
|
||||
|
||||
|
||||
const BorrowedMainGreenlet
|
||||
MainGreenlet::main_greenlet() const
|
||||
{
|
||||
return this->_self;
|
||||
}
|
||||
|
||||
BorrowedMainGreenlet
|
||||
MainGreenlet::find_main_greenlet_in_lineage() const
|
||||
{
|
||||
return BorrowedMainGreenlet(this->_self);
|
||||
}
|
||||
|
||||
bool
|
||||
MainGreenlet::was_running_in_dead_thread() const noexcept
|
||||
{
|
||||
return !this->_thread_state;
|
||||
}
|
||||
|
||||
OwnedObject
|
||||
MainGreenlet::g_switch()
|
||||
{
|
||||
try {
|
||||
this->check_switch_allowed();
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
this->release_args();
|
||||
throw;
|
||||
}
|
||||
|
||||
switchstack_result_t err = this->g_switchstack();
|
||||
if (err.status < 0) {
|
||||
// XXX: This code path is untested, but it is shared
|
||||
// with the UserGreenlet path that is tested.
|
||||
return this->on_switchstack_or_initialstub_failure(
|
||||
this,
|
||||
err,
|
||||
true, // target was me
|
||||
false // was initial stub
|
||||
);
|
||||
}
|
||||
|
||||
return err.the_new_current_greenlet->g_switch_finish(err);
|
||||
}
|
||||
|
||||
int
|
||||
MainGreenlet::tp_traverse(visitproc visit, void* arg)
|
||||
{
|
||||
ThreadState* thread_state = this->_thread_state.load();
|
||||
if (thread_state) {
|
||||
// we've already traversed main, (self), don't do it again.
|
||||
int result = thread_state->tp_traverse(visit, arg, false);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return Greenlet::tp_traverse(visit, arg);
|
||||
}
|
||||
|
||||
const OwnedObject&
|
||||
MainGreenlet::run() const
|
||||
{
|
||||
throw AttributeError("Main greenlets do not have a run attribute.");
|
||||
}
|
||||
|
||||
void
|
||||
MainGreenlet::run(const BorrowedObject UNUSED(nrun))
|
||||
{
|
||||
throw AttributeError("Main greenlets do not have a run attribute.");
|
||||
}
|
||||
|
||||
void
|
||||
MainGreenlet::parent(const BorrowedObject raw_new_parent)
|
||||
{
|
||||
if (!raw_new_parent) {
|
||||
throw AttributeError("can't delete attribute");
|
||||
}
|
||||
throw AttributeError("cannot set the parent of a main greenlet");
|
||||
}
|
||||
|
||||
const OwnedGreenlet
|
||||
MainGreenlet::parent() const
|
||||
{
|
||||
return OwnedGreenlet(); // null becomes None
|
||||
}
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user