|
#ifndef GREENLET_GREENLET_HPP |
|
#define GREENLET_GREENLET_HPP |
|
|
|
|
|
|
|
|
|
#define PY_SSIZE_T_CLEAN |
|
#include <Python.h> |
|
|
|
#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 |
|
# define _PyCFrame CFrame |
|
# define _PyInterpreterFrame _interpreter_frame |
|
#endif |
|
|
|
#if GREENLET_PY312 |
|
# define Py_BUILD_CORE |
|
# include "internal/pycore_frame.h" |
|
#endif |
|
|
|
|
|
|
|
|
|
namespace greenlet |
|
{ |
|
class ExceptionState |
|
{ |
|
private: |
|
G_NO_COPIES_OF_CLS(ExceptionState); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
|
|
OwnedFrame _top_frame; |
|
#if GREENLET_USE_CFRAME |
|
_PyCFrame* cframe; |
|
int use_tracing; |
|
#endif |
|
#if GREENLET_PY312 |
|
int py_recursion_depth; |
|
int c_recursion_depth; |
|
#else |
|
int recursion_depth; |
|
#endif |
|
#if GREENLET_PY313 |
|
PyObject *delete_later; |
|
#else |
|
int trash_delete_nesting; |
|
#endif |
|
#if GREENLET_PY311 |
|
_PyInterpreterFrame* current_frame; |
|
_PyStackChunk* datastack_chunk; |
|
PyObject** datastack_top; |
|
PyObject** datastack_limit; |
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void unexpose_frames(); |
|
|
|
public: |
|
|
|
PythonState(); |
|
|
|
|
|
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 |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
|
|
|
|
StackState(void* mark, StackState& current); |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
SwitchingArgs& operator<<=(SwitchingArgs& other) |
|
{ |
|
if (this != &other) { |
|
this->_args = other._args; |
|
this->_kwargs = other._kwargs; |
|
other.CLEAR(); |
|
} |
|
return *this; |
|
} |
|
|
|
|
|
|
|
|
|
SwitchingArgs& operator<<=(PyObject* args) |
|
{ |
|
this->_args = OwnedObject::consuming(args); |
|
this->_kwargs.CLEAR(); |
|
return *this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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: |
|
|
|
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: |
|
|
|
|
|
Greenlet(PyGreenlet* p); |
|
virtual ~Greenlet(); |
|
|
|
const OwnedObject context() const; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
inline const char* stack_start() const noexcept |
|
{ |
|
return this->stack_state.stack_start(); |
|
} |
|
|
|
virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state); |
|
virtual OwnedObject g_switch() = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual void murder_in_place(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline void deactivate_and_free(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void deallocing_greenlet_in_thread(const ThreadState* current_state); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void expose_frames(); |
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
|
|
|
virtual ThreadState* thread_state() const noexcept = 0; |
|
|
|
|
|
|
|
virtual bool was_running_in_dead_thread() const noexcept = 0; |
|
|
|
|
|
|
|
inline BorrowedGreenlet self() const noexcept |
|
{ |
|
return BorrowedGreenlet(this->_self); |
|
} |
|
|
|
|
|
|
|
virtual bool force_slp_switch_error() const noexcept; |
|
|
|
protected: |
|
inline void release_args(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
virtual OwnedGreenlet g_switchstack_success() noexcept; |
|
|
|
|
|
|
|
|
|
|
|
inline void check_switch_allowed() const; |
|
class GreenletStartedWhileInPython : public std::runtime_error |
|
{ |
|
public: |
|
GreenletStartedWhileInPython() : std::runtime_error("") |
|
{} |
|
}; |
|
|
|
protected: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
|
|
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; |
|
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); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#if GREENLET_PY310 |
|
class GCDisabledGuard |
|
{ |
|
private: |
|
int was_enabled = 0; |
|
public: |
|
GCDisabledGuard() |
|
: was_enabled(PyGC_IsEnabled()) |
|
{ |
|
PyGC_Disable(); |
|
} |
|
|
|
~GCDisabledGuard() |
|
{ |
|
if (this->was_enabled) { |
|
PyGC_Enable(); |
|
} |
|
} |
|
}; |
|
#endif |
|
|
|
OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept; |
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
} |
|
|
|
#endif |
|
|