|
#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), |
|
|
|
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) |
|
|
|
|
|
: _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."); |
|
} |
|
|
|
|
|
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 |
|
{ |
|
|
|
|
|
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; |
|
} |
|
while (owner && owner->stack_stop <= this->stack_stop) { |
|
|
|
owner = owner->stack_prev; |
|
} |
|
this->stack_prev = owner; |
|
|
|
} |
|
|
|
inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
{ |
|
|
|
const char* const target_stop = this->stack_stop; |
|
|
|
StackState* owner = const_cast<StackState*>(¤t); |
|
assert(owner->_stack_saved == 0); |
|
if (!owner->_stack_start) { |
|
owner = owner->stack_prev; |
|
} |
|
else { |
|
owner->_stack_start = stackref; |
|
} |
|
|
|
while (owner->stack_stop < target_stop) { |
|
|
|
if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) { |
|
return -1; |
|
} |
|
owner = owner->stack_prev; |
|
} |
|
if (owner != this) { |
|
if (owner->copy_stack_to_heap_up_to(target_stop)) { |
|
return -1; |
|
} |
|
} |
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
memcpy(dest, src, n); |
|
return; |
|
} |
|
|
|
if (src < this->_stack_start) { |
|
|
|
|
|
const size_t nbefore = this->_stack_start - src; |
|
memcpy(dest, src, nbefore); |
|
dest += nbefore; |
|
src += nbefore; |
|
n -= nbefore; |
|
} |
|
|
|
|
|
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) { |
|
|
|
memcpy(dest, src, n); |
|
} |
|
} |
|
|
|
}; |
|
|
|
#endif |
|
|