123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- #ifndef GREENLET_STACK_STATE_CPP
- #define GREENLET_STACK_STATE_CPP
- #include "TGreenlet.hpp"
- namespace greenlet {
- #ifdef GREENLET_USE_STDIO
- #include <iostream>
- using std::cerr;
- using std::endl;
- std::ostream& operator<<(std::ostream& os, const StackState& s)
- {
- os << "StackState(stack_start=" << (void*)s._stack_start
- << ", stack_stop=" << (void*)s.stack_stop
- << ", stack_copy=" << (void*)s.stack_copy
- << ", stack_saved=" << s._stack_saved
- << ", stack_prev=" << s.stack_prev
- << ", addr=" << &s
- << ")";
- return os;
- }
- #endif
- StackState::StackState(void* mark, StackState& current)
- : _stack_start(nullptr),
- stack_stop((char*)mark),
- stack_copy(nullptr),
- _stack_saved(0),
- /* Skip a dying greenlet */
- stack_prev(current._stack_start
- ? ¤t
- : current.stack_prev)
- {
- }
- StackState::StackState()
- : _stack_start(nullptr),
- stack_stop(nullptr),
- stack_copy(nullptr),
- _stack_saved(0),
- stack_prev(nullptr)
- {
- }
- StackState::StackState(const StackState& other)
- // can't use a delegating constructor because of
- // MSVC for Python 2.7
- : _stack_start(nullptr),
- stack_stop(nullptr),
- stack_copy(nullptr),
- _stack_saved(0),
- stack_prev(nullptr)
- {
- this->operator=(other);
- }
- StackState& StackState::operator=(const StackState& other)
- {
- if (&other == this) {
- return *this;
- }
- if (other._stack_saved) {
- throw std::runtime_error("Refusing to steal memory.");
- }
- //If we have memory allocated, dispose of it
- this->free_stack_copy();
- this->_stack_start = other._stack_start;
- this->stack_stop = other.stack_stop;
- this->stack_copy = other.stack_copy;
- this->_stack_saved = other._stack_saved;
- this->stack_prev = other.stack_prev;
- return *this;
- }
- inline void StackState::free_stack_copy() noexcept
- {
- PyMem_Free(this->stack_copy);
- this->stack_copy = nullptr;
- this->_stack_saved = 0;
- }
- inline void StackState::copy_heap_to_stack(const StackState& current) noexcept
- {
- /* Restore the heap copy back into the C stack */
- if (this->_stack_saved != 0) {
- memcpy(this->_stack_start, this->stack_copy, this->_stack_saved);
- this->free_stack_copy();
- }
- StackState* owner = const_cast<StackState*>(¤t);
- if (!owner->_stack_start) {
- owner = owner->stack_prev; /* greenlet is dying, skip it */
- }
- while (owner && owner->stack_stop <= this->stack_stop) {
- // cerr << "\tOwner: " << owner << endl;
- owner = owner->stack_prev; /* find greenlet with more stack */
- }
- this->stack_prev = owner;
- // cerr << "\tFinished with: " << *this << endl;
- }
- inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept
- {
- /* Save more of g's stack into the heap -- at least up to 'stop'
- g->stack_stop |________|
- | |
- | __ stop . . . . .
- | | ==> . .
- |________| _______
- | | | |
- | | | |
- g->stack_start | | |_______| g->stack_copy
- */
- intptr_t sz1 = this->_stack_saved;
- intptr_t sz2 = stop - this->_stack_start;
- assert(this->_stack_start);
- if (sz2 > sz1) {
- char* c = (char*)PyMem_Realloc(this->stack_copy, sz2);
- if (!c) {
- PyErr_NoMemory();
- return -1;
- }
- memcpy(c + sz1, this->_stack_start + sz1, sz2 - sz1);
- this->stack_copy = c;
- this->_stack_saved = sz2;
- }
- return 0;
- }
- inline int StackState::copy_stack_to_heap(char* const stackref,
- const StackState& current) noexcept
- {
- /* must free all the C stack up to target_stop */
- const char* const target_stop = this->stack_stop;
- StackState* owner = const_cast<StackState*>(¤t);
- assert(owner->_stack_saved == 0); // everything is present on the stack
- if (!owner->_stack_start) {
- owner = owner->stack_prev; /* not saved if dying */
- }
- else {
- owner->_stack_start = stackref;
- }
- while (owner->stack_stop < target_stop) {
- /* ts_current is entierely within the area to free */
- if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) {
- return -1; /* XXX */
- }
- owner = owner->stack_prev;
- }
- if (owner != this) {
- if (owner->copy_stack_to_heap_up_to(target_stop)) {
- return -1; /* XXX */
- }
- }
- return 0;
- }
- inline bool StackState::started() const noexcept
- {
- return this->stack_stop != nullptr;
- }
- inline bool StackState::main() const noexcept
- {
- return this->stack_stop == (char*)-1;
- }
- inline bool StackState::active() const noexcept
- {
- return this->_stack_start != nullptr;
- }
- inline void StackState::set_active() noexcept
- {
- assert(this->_stack_start == nullptr);
- this->_stack_start = (char*)1;
- }
- inline void StackState::set_inactive() noexcept
- {
- this->_stack_start = nullptr;
- // XXX: What if we still have memory out there?
- // That case is actually triggered by
- // test_issue251_issue252_explicit_reference_not_collectable (greenlet.tests.test_leaks.TestLeaks)
- // and
- // test_issue251_issue252_need_to_collect_in_background
- // (greenlet.tests.test_leaks.TestLeaks)
- //
- // Those objects never get deallocated, so the destructor never
- // runs.
- // It *seems* safe to clean up the memory here?
- if (this->_stack_saved) {
- this->free_stack_copy();
- }
- }
- inline intptr_t StackState::stack_saved() const noexcept
- {
- return this->_stack_saved;
- }
- inline char* StackState::stack_start() const noexcept
- {
- return this->_stack_start;
- }
- inline StackState StackState::make_main() noexcept
- {
- StackState s;
- s._stack_start = (char*)1;
- s.stack_stop = (char*)-1;
- return s;
- }
- StackState::~StackState()
- {
- if (this->_stack_saved != 0) {
- this->free_stack_copy();
- }
- }
- void StackState::copy_from_stack(void* vdest, const void* vsrc, size_t n) const
- {
- char* dest = static_cast<char*>(vdest);
- const char* src = static_cast<const char*>(vsrc);
- if (src + n <= this->_stack_start
- || src >= this->_stack_start + this->_stack_saved
- || this->_stack_saved == 0) {
- // Nothing we're copying was spilled from the stack
- memcpy(dest, src, n);
- return;
- }
- if (src < this->_stack_start) {
- // Copy the part before the saved stack.
- // We know src + n > _stack_start due to the test above.
- const size_t nbefore = this->_stack_start - src;
- memcpy(dest, src, nbefore);
- dest += nbefore;
- src += nbefore;
- n -= nbefore;
- }
- // We know src >= _stack_start after the before-copy, and
- // src < _stack_start + _stack_saved due to the first if condition
- size_t nspilled = std::min<size_t>(n, this->_stack_start + this->_stack_saved - src);
- memcpy(dest, this->stack_copy + (src - this->_stack_start), nspilled);
- dest += nspilled;
- src += nspilled;
- n -= nspilled;
- if (n > 0) {
- // Copy the part after the saved stack
- memcpy(dest, src, n);
- }
- }
- }; // namespace greenlet
- #endif // GREENLET_STACK_STATE_CPP
|