context.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. #include "compiler.h"
  2. #include "defaults.h"
  3. #include "event.h"
  4. #include "thread.h"
  5. #include <cstdlib> //for abort()
  6. #if defined(_win_)
  7. #include "winint.h"
  8. #endif
  9. #if defined(_unix_)
  10. #include <cxxabi.h>
  11. #if !defined(Y_CXA_EH_GLOBALS_COMPLETE)
  12. namespace __cxxabiv1 {
  13. struct __cxa_eh_globals {
  14. void* caughtExceptions;
  15. unsigned int uncaughtExceptions;
  16. };
  17. extern "C" __cxa_eh_globals* __cxa_get_globals();
  18. } // namespace __cxxabiv1
  19. #endif
  20. #endif
  21. #include <util/stream/output.h>
  22. #include <util/generic/yexception.h>
  23. #define FROM_CONTEXT_IMPL
  24. #include "context.h"
  25. void ITrampoLine::DoRun() {
  26. }
  27. void ITrampoLine::DoRunNaked() {
  28. try {
  29. DoRun();
  30. } catch (...) {
  31. Cerr << "Uncaught exception in coroutine: " << CurrentExceptionMessage() << "\n";
  32. }
  33. abort();
  34. }
  35. static inline void Run(void* arg) {
  36. ((ITrampoLine*)arg)->DoRunNaked();
  37. }
  38. #if defined(USE_JUMP_CONT)
  39. extern "C" void __mylongjmp(__myjmp_buf env, int val) __attribute__((__noreturn__));
  40. extern "C" int __mysetjmp(__myjmp_buf env) __attribute__((__returns_twice__));
  41. namespace {
  42. class TStackType {
  43. public:
  44. inline TStackType(TArrayRef<char> range) noexcept
  45. #if defined(STACK_GROW_DOWN)
  46. : Data_(range.data() + range.size())
  47. #else
  48. : Data_(range.data() + STACK_ALIGN)
  49. #endif
  50. {
  51. ReAlign();
  52. }
  53. inline ~TStackType() = default;
  54. inline void ReAlign() noexcept {
  55. Data_ = AlignStackPtr(Data_);
  56. }
  57. template <class T>
  58. inline void Push(T t) noexcept {
  59. #if defined(STACK_GROW_DOWN)
  60. Data_ -= sizeof(T);
  61. *((T*)Data_) = t;
  62. #else
  63. *((T*)Data_) = t;
  64. Data_ += sizeof(T);
  65. #endif
  66. }
  67. inline char* StackPtr() noexcept {
  68. return Data_;
  69. }
  70. private:
  71. static inline char* AlignStackPtr(char* ptr) noexcept {
  72. #if defined(STACK_GROW_DOWN)
  73. return AlignDown(ptr, STACK_ALIGN);
  74. #else
  75. return AlignUp(ptr, STACK_ALIGN);
  76. #endif
  77. }
  78. private:
  79. char* Data_;
  80. };
  81. static inline void*& JmpBufReg(__myjmp_buf& buf, size_t n) noexcept {
  82. return (((void**)(void*)(buf))[n]);
  83. }
  84. static inline void*& JmpBufStackReg(__myjmp_buf& buf) noexcept {
  85. return JmpBufReg(buf, STACK_CNT);
  86. }
  87. static inline void*& JmpBufProgrReg(__myjmp_buf& buf) noexcept {
  88. return JmpBufReg(buf, PROGR_CNT);
  89. }
  90. static inline void*& JmpBufFrameReg(__myjmp_buf& buf) noexcept {
  91. return JmpBufReg(buf, FRAME_CNT);
  92. }
  93. #if defined(_x86_64_)
  94. // not sure if Y_NO_SANITIZE is needed
  95. Y_NO_SANITIZE("address")
  96. Y_NO_SANITIZE("memory") extern "C" void
  97. ContextTrampoLine(void*, void*, void*, void*, void*, void*, // register arguments, no defined value
  98. /* first argument passed through the stack */ void* t1,
  99. /* second argument passed through the stack */ void* t2) {
  100. Y_ASSERT(t1 == t2);
  101. Run(t1);
  102. }
  103. #else
  104. Y_NO_SANITIZE("address")
  105. Y_NO_SANITIZE("memory") static void
  106. ContextTrampoLine() {
  107. void** argPtr = (void**)((char*)AlignUp(&argPtr + EXTRA_PUSH_ARGS, STACK_ALIGN) + STACK_ALIGN);
  108. Y_ASSERT(*(argPtr - 1) == *(argPtr - 2));
  109. Run(*(argPtr - 1));
  110. }
  111. #endif
  112. } // namespace
  113. #if defined(USE_SANITIZER_CONTEXT)
  114. TContMachineContext::TSan::TSan() noexcept
  115. : TL(nullptr)
  116. {
  117. }
  118. TContMachineContext::TSan::TSan(const TContClosure& c) noexcept
  119. : NSan::TFiberContext(c.Stack.data(), c.Stack.size(), c.ContName)
  120. , TL(c.TrampoLine)
  121. {
  122. }
  123. void TContMachineContext::TSan::DoRunNaked() {
  124. AfterSwitch();
  125. TL->DoRunNaked();
  126. }
  127. #endif
  128. TContMachineContext::TContMachineContext(const TContClosure& c)
  129. #if defined(USE_SANITIZER_CONTEXT)
  130. : San_(c)
  131. #endif
  132. {
  133. TStackType stack(c.Stack);
  134. /*
  135. * arg, and align data
  136. */
  137. #if defined(USE_SANITIZER_CONTEXT)
  138. auto trampoline = &San_;
  139. #else
  140. auto trampoline = c.TrampoLine;
  141. #endif
  142. #if defined(_x86_64_)
  143. stack.ReAlign();
  144. // push twice to preserve alignment by 16
  145. stack.Push(trampoline); // second stack argument
  146. stack.Push(trampoline); // first stack argument
  147. stack.Push(nullptr); // fake return address
  148. #else
  149. stack.Push(trampoline);
  150. stack.Push(trampoline);
  151. stack.ReAlign();
  152. /*
  153. * fake return address
  154. */
  155. for (size_t i = 0; i < EXTRA_PUSH_ARGS; ++i) {
  156. stack.Push(nullptr);
  157. }
  158. #endif
  159. __mysetjmp(Buf_);
  160. JmpBufProgrReg(Buf_) = reinterpret_cast<void*>(ContextTrampoLine);
  161. JmpBufStackReg(Buf_) = stack.StackPtr();
  162. JmpBufFrameReg(Buf_) = nullptr;
  163. }
  164. void TContMachineContext::SwitchTo(TContMachineContext* next) noexcept {
  165. if (Y_LIKELY(__mysetjmp(Buf_) == 0)) {
  166. #if defined(USE_SANITIZER_CONTEXT)
  167. next->San_.BeforeSwitch(&San_);
  168. #endif
  169. __mylongjmp(next->Buf_, 1);
  170. } else {
  171. #if defined(USE_SANITIZER_CONTEXT)
  172. San_.AfterSwitch();
  173. #endif
  174. }
  175. }
  176. #elif defined(_win_) && defined(_32_)
  177. void __stdcall ContextTrampoLine(void* arg) {
  178. Run(arg);
  179. }
  180. #else
  181. void ContextTrampoLine(void* arg) {
  182. Run(arg);
  183. }
  184. #endif
  185. #if defined(USE_FIBER_CONT)
  186. TContMachineContext::TContMachineContext()
  187. : Fiber_(ConvertThreadToFiber(this))
  188. , MainFiber_(true)
  189. {
  190. Y_ENSURE(Fiber_, TStringBuf("fiber error"));
  191. }
  192. TContMachineContext::TContMachineContext(const TContClosure& c)
  193. : Fiber_(CreateFiber(c.Stack.size(), (LPFIBER_START_ROUTINE)ContextTrampoLine, (LPVOID)c.TrampoLine))
  194. , MainFiber_(false)
  195. {
  196. Y_ENSURE(Fiber_, TStringBuf("fiber error"));
  197. }
  198. TContMachineContext::~TContMachineContext() {
  199. if (MainFiber_) {
  200. ConvertFiberToThread();
  201. } else {
  202. DeleteFiber(Fiber_);
  203. }
  204. }
  205. void TContMachineContext::SwitchTo(TContMachineContext* next) noexcept {
  206. SwitchToFiber(next->Fiber_);
  207. }
  208. #endif
  209. #if defined(USE_GENERIC_CONT)
  210. #include <pthread.h>
  211. struct TContMachineContext::TImpl {
  212. inline TImpl()
  213. : TL(nullptr)
  214. , Finish(false)
  215. {
  216. }
  217. inline TImpl(const TContClosure& c)
  218. : TL(c.TrampoLine)
  219. , Finish(false)
  220. {
  221. Thread.Reset(new TThread(TThread::TParams(Run, this).SetStackSize(c.Stack.size()).SetStackPointer((void*)c.Stack.data())));
  222. Thread->Start();
  223. }
  224. inline ~TImpl() {
  225. if (Thread) {
  226. Finish = true;
  227. Signal();
  228. Thread->Join();
  229. }
  230. }
  231. inline void SwitchTo(TImpl* next) noexcept {
  232. next->Signal();
  233. Wait();
  234. }
  235. static void* Run(void* self) {
  236. ((TImpl*)self)->DoRun();
  237. return nullptr;
  238. }
  239. inline void DoRun() {
  240. Wait();
  241. TL->DoRun();
  242. }
  243. inline void Signal() noexcept {
  244. Event.Signal();
  245. }
  246. inline void Wait() noexcept {
  247. Event.Wait();
  248. if (Finish) {
  249. // TODO - need proper TThread::Exit(), have some troubles in win32 now
  250. pthread_exit(0);
  251. }
  252. }
  253. TAutoEvent Event;
  254. THolder<TThread> Thread;
  255. ITrampoLine* TL;
  256. bool Finish;
  257. };
  258. TContMachineContext::TContMachineContext()
  259. : Impl_(new TImpl())
  260. {
  261. }
  262. TContMachineContext::TContMachineContext(const TContClosure& c)
  263. : Impl_(new TImpl(c))
  264. {
  265. }
  266. TContMachineContext::~TContMachineContext() {
  267. }
  268. void TContMachineContext::SwitchTo(TContMachineContext* next) noexcept {
  269. Impl_->SwitchTo(next->Impl_.Get());
  270. }
  271. #endif
  272. void TExceptionSafeContext::SwitchTo(TExceptionSafeContext* to) noexcept {
  273. #if defined(_unix_)
  274. static_assert(sizeof(__cxxabiv1::__cxa_eh_globals) == sizeof(Buf_), "size mismatch of __cxa_eh_globals structure");
  275. auto* eh = __cxxabiv1::__cxa_get_globals();
  276. ::memcpy(Buf_, eh, sizeof(Buf_));
  277. ::memcpy(eh, to->Buf_, sizeof(Buf_));
  278. #endif
  279. TContMachineContext::SwitchTo(to);
  280. }