TStackState.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. #ifndef GREENLET_STACK_STATE_CPP
  2. #define GREENLET_STACK_STATE_CPP
  3. #include "TGreenlet.hpp"
  4. namespace greenlet {
  5. #ifdef GREENLET_USE_STDIO
  6. #include <iostream>
  7. using std::cerr;
  8. using std::endl;
  9. std::ostream& operator<<(std::ostream& os, const StackState& s)
  10. {
  11. os << "StackState(stack_start=" << (void*)s._stack_start
  12. << ", stack_stop=" << (void*)s.stack_stop
  13. << ", stack_copy=" << (void*)s.stack_copy
  14. << ", stack_saved=" << s._stack_saved
  15. << ", stack_prev=" << s.stack_prev
  16. << ", addr=" << &s
  17. << ")";
  18. return os;
  19. }
  20. #endif
  21. StackState::StackState(void* mark, StackState& current)
  22. : _stack_start(nullptr),
  23. stack_stop((char*)mark),
  24. stack_copy(nullptr),
  25. _stack_saved(0),
  26. /* Skip a dying greenlet */
  27. stack_prev(current._stack_start
  28. ? &current
  29. : current.stack_prev)
  30. {
  31. }
  32. StackState::StackState()
  33. : _stack_start(nullptr),
  34. stack_stop(nullptr),
  35. stack_copy(nullptr),
  36. _stack_saved(0),
  37. stack_prev(nullptr)
  38. {
  39. }
  40. StackState::StackState(const StackState& other)
  41. // can't use a delegating constructor because of
  42. // MSVC for Python 2.7
  43. : _stack_start(nullptr),
  44. stack_stop(nullptr),
  45. stack_copy(nullptr),
  46. _stack_saved(0),
  47. stack_prev(nullptr)
  48. {
  49. this->operator=(other);
  50. }
  51. StackState& StackState::operator=(const StackState& other)
  52. {
  53. if (&other == this) {
  54. return *this;
  55. }
  56. if (other._stack_saved) {
  57. throw std::runtime_error("Refusing to steal memory.");
  58. }
  59. //If we have memory allocated, dispose of it
  60. this->free_stack_copy();
  61. this->_stack_start = other._stack_start;
  62. this->stack_stop = other.stack_stop;
  63. this->stack_copy = other.stack_copy;
  64. this->_stack_saved = other._stack_saved;
  65. this->stack_prev = other.stack_prev;
  66. return *this;
  67. }
  68. inline void StackState::free_stack_copy() noexcept
  69. {
  70. PyMem_Free(this->stack_copy);
  71. this->stack_copy = nullptr;
  72. this->_stack_saved = 0;
  73. }
  74. inline void StackState::copy_heap_to_stack(const StackState& current) noexcept
  75. {
  76. /* Restore the heap copy back into the C stack */
  77. if (this->_stack_saved != 0) {
  78. memcpy(this->_stack_start, this->stack_copy, this->_stack_saved);
  79. this->free_stack_copy();
  80. }
  81. StackState* owner = const_cast<StackState*>(&current);
  82. if (!owner->_stack_start) {
  83. owner = owner->stack_prev; /* greenlet is dying, skip it */
  84. }
  85. while (owner && owner->stack_stop <= this->stack_stop) {
  86. // cerr << "\tOwner: " << owner << endl;
  87. owner = owner->stack_prev; /* find greenlet with more stack */
  88. }
  89. this->stack_prev = owner;
  90. // cerr << "\tFinished with: " << *this << endl;
  91. }
  92. inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept
  93. {
  94. /* Save more of g's stack into the heap -- at least up to 'stop'
  95. g->stack_stop |________|
  96. | |
  97. | __ stop . . . . .
  98. | | ==> . .
  99. |________| _______
  100. | | | |
  101. | | | |
  102. g->stack_start | | |_______| g->stack_copy
  103. */
  104. intptr_t sz1 = this->_stack_saved;
  105. intptr_t sz2 = stop - this->_stack_start;
  106. assert(this->_stack_start);
  107. if (sz2 > sz1) {
  108. char* c = (char*)PyMem_Realloc(this->stack_copy, sz2);
  109. if (!c) {
  110. PyErr_NoMemory();
  111. return -1;
  112. }
  113. memcpy(c + sz1, this->_stack_start + sz1, sz2 - sz1);
  114. this->stack_copy = c;
  115. this->_stack_saved = sz2;
  116. }
  117. return 0;
  118. }
  119. inline int StackState::copy_stack_to_heap(char* const stackref,
  120. const StackState& current) noexcept
  121. {
  122. /* must free all the C stack up to target_stop */
  123. const char* const target_stop = this->stack_stop;
  124. StackState* owner = const_cast<StackState*>(&current);
  125. assert(owner->_stack_saved == 0); // everything is present on the stack
  126. if (!owner->_stack_start) {
  127. owner = owner->stack_prev; /* not saved if dying */
  128. }
  129. else {
  130. owner->_stack_start = stackref;
  131. }
  132. while (owner->stack_stop < target_stop) {
  133. /* ts_current is entierely within the area to free */
  134. if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) {
  135. return -1; /* XXX */
  136. }
  137. owner = owner->stack_prev;
  138. }
  139. if (owner != this) {
  140. if (owner->copy_stack_to_heap_up_to(target_stop)) {
  141. return -1; /* XXX */
  142. }
  143. }
  144. return 0;
  145. }
  146. inline bool StackState::started() const noexcept
  147. {
  148. return this->stack_stop != nullptr;
  149. }
  150. inline bool StackState::main() const noexcept
  151. {
  152. return this->stack_stop == (char*)-1;
  153. }
  154. inline bool StackState::active() const noexcept
  155. {
  156. return this->_stack_start != nullptr;
  157. }
  158. inline void StackState::set_active() noexcept
  159. {
  160. assert(this->_stack_start == nullptr);
  161. this->_stack_start = (char*)1;
  162. }
  163. inline void StackState::set_inactive() noexcept
  164. {
  165. this->_stack_start = nullptr;
  166. // XXX: What if we still have memory out there?
  167. // That case is actually triggered by
  168. // test_issue251_issue252_explicit_reference_not_collectable (greenlet.tests.test_leaks.TestLeaks)
  169. // and
  170. // test_issue251_issue252_need_to_collect_in_background
  171. // (greenlet.tests.test_leaks.TestLeaks)
  172. //
  173. // Those objects never get deallocated, so the destructor never
  174. // runs.
  175. // It *seems* safe to clean up the memory here?
  176. if (this->_stack_saved) {
  177. this->free_stack_copy();
  178. }
  179. }
  180. inline intptr_t StackState::stack_saved() const noexcept
  181. {
  182. return this->_stack_saved;
  183. }
  184. inline char* StackState::stack_start() const noexcept
  185. {
  186. return this->_stack_start;
  187. }
  188. inline StackState StackState::make_main() noexcept
  189. {
  190. StackState s;
  191. s._stack_start = (char*)1;
  192. s.stack_stop = (char*)-1;
  193. return s;
  194. }
  195. StackState::~StackState()
  196. {
  197. if (this->_stack_saved != 0) {
  198. this->free_stack_copy();
  199. }
  200. }
  201. void StackState::copy_from_stack(void* vdest, const void* vsrc, size_t n) const
  202. {
  203. char* dest = static_cast<char*>(vdest);
  204. const char* src = static_cast<const char*>(vsrc);
  205. if (src + n <= this->_stack_start
  206. || src >= this->_stack_start + this->_stack_saved
  207. || this->_stack_saved == 0) {
  208. // Nothing we're copying was spilled from the stack
  209. memcpy(dest, src, n);
  210. return;
  211. }
  212. if (src < this->_stack_start) {
  213. // Copy the part before the saved stack.
  214. // We know src + n > _stack_start due to the test above.
  215. const size_t nbefore = this->_stack_start - src;
  216. memcpy(dest, src, nbefore);
  217. dest += nbefore;
  218. src += nbefore;
  219. n -= nbefore;
  220. }
  221. // We know src >= _stack_start after the before-copy, and
  222. // src < _stack_start + _stack_saved due to the first if condition
  223. size_t nspilled = std::min<size_t>(n, this->_stack_start + this->_stack_saved - src);
  224. memcpy(dest, this->stack_copy + (src - this->_stack_start), nspilled);
  225. dest += nspilled;
  226. src += nspilled;
  227. n -= nspilled;
  228. if (n > 0) {
  229. // Copy the part after the saved stack
  230. memcpy(dest, src, n);
  231. }
  232. }
  233. }; // namespace greenlet
  234. #endif // GREENLET_STACK_STATE_CPP