Qucs-S S-parameter Viewer & RF Synthesis Tools
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | List of all members
greenlet::ThreadState Class Reference

#include <TThreadState.hpp>

Public Member Functions

void restore_exception_state ()
 
bool has_main_greenlet () const noexcept
 
int tp_traverse (visitproc visit, void *arg, bool traverse_main=true)
 
BorrowedMainGreenlet borrow_main_greenlet () const noexcept
 
OwnedMainGreenlet get_main_greenlet () const noexcept
 
OwnedGreenlet get_current ()
 
BorrowedGreenlet borrow_current ()
 
OwnedGreenlet get_current () const
 
template<typename T , refs::TypeChecker TC>
bool is_current (const refs::PyObjectPointer< T, TC > &obj) const
 
void set_current (const OwnedGreenlet &target)
 
OwnedObject get_tracefunc () const
 
void set_tracefunc (BorrowedObject tracefunc)
 
void delete_when_thread_running (PyGreenlet *to_del)
 

Static Public Member Functions

static void * operator new (size_t UNUSED(count))
 
static void operator delete (void *ptr)
 
static void init ()
 
static std::clock_t clocks_used_doing_gc ()
 
static void set_clocks_used_doing_gc (std::clock_t value)
 
static void add_clocks_used_doing_gc (std::clock_t value)
 

Detailed Description

Thread-local state of greenlets.

Each native thread will get exactly one of these objects, automatically accessed through the best available thread-local mechanism the compiler supports (thread_local for C++11 compilers or __thread/declspec(thread) for older GCC/clang or MSVC, respectively.)

Previously, we kept thread-local state mostly in a bunch of static volatile variables in the main greenlet file.. This had the problem of requiring extra checks, loops, and great care accessing these variables if we potentially invoked any Python code that could release the GIL, because the state could change out from under us. Making the variables thread-local solves this problem.

When we detected that a greenlet API accessing the current greenlet was invoked from a different thread than the greenlet belonged to, we stored a reference to the greenlet in the Python thread dictionary for the thread the greenlet belonged to. This could lead to memory leaks if the thread then exited (because of a reference cycle, as greenlets referred to the thread dictionary, and deleting non-current greenlets leaked their frame plus perhaps arguments on the C stack). If a thread exited while still having running greenlet objects (perhaps that had just switched back to the main greenlet), and did not invoke one of the greenlet APIs in that thread, immediately before it exited, without some other thread then being invoked, such a leak was guaranteed.

This can be partly solved by using compiler thread-local variables instead of the Python thread dictionary, thus avoiding a cycle.

To fully solve this problem, we need a reliable way to know that a thread is done and we should clean up the main greenlet. On POSIX, we can use the destructor function of pthread_key_create, but there's nothing similar on Windows; a C++11 thread local object reliably invokes its destructor when the thread it belongs to exits (non-C++11 compilers offer __thread or declspec(thread) to create thread-local variables, but they can't hold C++ objects that invoke destructors; the C++11 version is the most portable solution I found). When the thread exits, we can drop references and otherwise manipulate greenlets and frames that we know can no longer be switched to.

There are two small wrinkles. The first is that when the thread exits, it is too late to actually invoke Python APIs: the Python thread state is gone, and the GIL is released. To solve this problem, our destructor uses Py_AddPendingCall to transfer the destruction work to the main thread.

The second is that once the thread exits, the thread local object is invalid and we can't even access a pointer to it, so we can't pass it to Py_AddPendingCall. This is handled by actually using a second object that's thread local (ThreadStateCreator) and having it dynamically allocate this object so it can live until the pending call runs.

Member Function Documentation

◆ borrow_current()

BorrowedGreenlet greenlet::ThreadState::borrow_current ( )
inline

As for non-const get_current();

◆ clocks_used_doing_gc()

static std::clock_t greenlet::ThreadState::clocks_used_doing_gc ( )
inlinestatic

Set to std::clock_t(-1) to disable.

◆ delete_when_thread_running()

void greenlet::ThreadState::delete_when_thread_running ( PyGreenlet to_del)
inline

Given a reference to a greenlet that some other thread attempted to delete (has a refcount of 0) store it for later deletion when the thread this state belongs to is current.

◆ get_current() [1/2]

OwnedGreenlet greenlet::ThreadState::get_current ( )
inline

In addition to returning a new reference to the currunt greenlet, this performs any maintenance needed.

◆ get_current() [2/2]

OwnedGreenlet greenlet::ThreadState::get_current ( ) const
inline

Does no maintenance.

◆ get_tracefunc()

OwnedObject greenlet::ThreadState::get_tracefunc ( ) const
inline

Returns a new reference, or a false object.


The documentation for this class was generated from the following file: