PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/translator/c/src/stacklet/stacklet.c

https://bitbucket.org/pypy/pypy/
C | 341 lines | 216 code | 45 blank | 80 comment | 37 complexity | ed1f275fb9937e06da0249f2aac72fb3 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. /********** A really minimal coroutine package for C **********
  2. * By Armin Rigo
  3. */
  4. #include "stacklet.h"
  5. #include <stddef.h>
  6. #include <assert.h>
  7. #include <string.h>
  8. /************************************************************
  9. * platform specific code
  10. */
  11. /* The default stack direction is downwards, 0, but platforms
  12. * can redefine it to upwards growing, 1.
  13. */
  14. #define STACK_DIRECTION 0
  15. #include "slp_platformselect.h"
  16. #if STACK_DIRECTION != 0
  17. # error "review this whole code, which depends on STACK_DIRECTION==0 so far"
  18. #endif
  19. /************************************************************/
  20. /* #define DEBUG_DUMP */
  21. #ifdef DEBUG_DUMP
  22. #include <stdio.h>
  23. #endif
  24. /************************************************************/
  25. struct stacklet_s {
  26. /* The portion of the real stack claimed by this paused tealet. */
  27. char *stack_start; /* the "near" end of the stack */
  28. char *stack_stop; /* the "far" end of the stack */
  29. /* The amount that has been saved away so far, just after this struct.
  30. * There is enough allocated space for 'stack_stop - stack_start'
  31. * bytes.
  32. */
  33. ptrdiff_t stack_saved; /* the amount saved */
  34. /* Internally, some stacklets are arranged in a list, to handle lazy
  35. * saving of stacks: if the stacklet has a partially unsaved stack,
  36. * this points to the next stacklet with a partially unsaved stack,
  37. * creating a linked list with each stacklet's stack_stop higher
  38. * than the previous one. The last entry in the list is always the
  39. * main stack.
  40. */
  41. struct stacklet_s *stack_prev;
  42. };
  43. void *(*_stacklet_switchstack)(void*(*)(void*, void*),
  44. void*(*)(void*, void*), void*) = NULL;
  45. void (*_stacklet_initialstub)(struct stacklet_thread_s *,
  46. stacklet_run_fn, void *) = NULL;
  47. struct stacklet_thread_s {
  48. struct stacklet_s *g_stack_chain_head; /* NULL <=> running main */
  49. char *g_current_stack_stop;
  50. char *g_current_stack_marker;
  51. struct stacklet_s *g_source;
  52. struct stacklet_s *g_target;
  53. };
  54. /***************************************************************/
  55. static void g_save(struct stacklet_s* g, char* stop
  56. #ifdef DEBUG_DUMP
  57. , int overwrite_stack_for_debug
  58. #endif
  59. )
  60. {
  61. /* Save more of g's stack into the heap -- at least up to 'stop'
  62. In the picture below, the C stack is on the left, growing down,
  63. and the C heap on the right. The area marked with xxx is the logical
  64. stack of the stacklet 'g'. It can be half in the C stack (its older
  65. part), and half in the heap (its newer part).
  66. g->stack_stop |________|
  67. |xxxxxxxx|
  68. |xxx __ stop .........
  69. |xxxxxxxx| ==> : :
  70. |________| :_______:
  71. | | |xxxxxxx|
  72. | | |xxxxxxx|
  73. g->stack_start | | |_______| g+1
  74. */
  75. ptrdiff_t sz1 = g->stack_saved;
  76. ptrdiff_t sz2 = stop - g->stack_start;
  77. assert(stop <= g->stack_stop);
  78. if (sz2 > sz1) {
  79. char *c = (char *)(g + 1);
  80. #if STACK_DIRECTION == 0
  81. memcpy(c+sz1, g->stack_start+sz1, sz2-sz1);
  82. # ifdef DEBUG_DUMP
  83. if (overwrite_stack_for_debug)
  84. memset(g->stack_start+sz1, 0xdb, sz2-sz1);
  85. # endif
  86. #else
  87. xxx;
  88. #endif
  89. g->stack_saved = sz2;
  90. }
  91. }
  92. /* Allocate and store in 'g_source' a new stacklet, which has the C
  93. * stack from 'old_stack_pointer' to 'g_current_stack_stop'. It is
  94. * initially completely unsaved, so it is attached to the head of the
  95. * chained list of 'stack_prev'.
  96. */
  97. static int g_allocate_source_stacklet(void *old_stack_pointer,
  98. struct stacklet_thread_s *thrd)
  99. {
  100. struct stacklet_s *stacklet;
  101. ptrdiff_t stack_size = (thrd->g_current_stack_stop -
  102. (char *)old_stack_pointer);
  103. thrd->g_source = malloc(sizeof(struct stacklet_s) + stack_size);
  104. if (thrd->g_source == NULL)
  105. return -1;
  106. stacklet = thrd->g_source;
  107. stacklet->stack_start = old_stack_pointer;
  108. stacklet->stack_stop = thrd->g_current_stack_stop;
  109. stacklet->stack_saved = 0;
  110. stacklet->stack_prev = thrd->g_stack_chain_head;
  111. thrd->g_stack_chain_head = stacklet;
  112. return 0;
  113. }
  114. /* Save more of the C stack away, up to 'target_stop'.
  115. */
  116. static void g_clear_stack(struct stacklet_s *g_target,
  117. struct stacklet_thread_s *thrd)
  118. {
  119. struct stacklet_s *current = thrd->g_stack_chain_head;
  120. char *target_stop = g_target->stack_stop;
  121. /* save and unlink tealets that are completely within
  122. the area to clear. */
  123. while (current != NULL && current->stack_stop <= target_stop) {
  124. struct stacklet_s *prev = current->stack_prev;
  125. current->stack_prev = NULL;
  126. if (current != g_target) {
  127. /* don't bother saving away g_target, because
  128. it would be immediately restored */
  129. g_save(current, current->stack_stop
  130. #ifdef DEBUG_DUMP
  131. , 1
  132. #endif
  133. );
  134. }
  135. current = prev;
  136. }
  137. /* save a partial stack */
  138. if (current != NULL && current->stack_start < target_stop)
  139. g_save(current, target_stop
  140. #ifdef DEBUG_DUMP
  141. , 1
  142. #endif
  143. );
  144. thrd->g_stack_chain_head = current;
  145. }
  146. /* This saves the current state in a new stacklet that gets stored in
  147. * 'g_source', and save away enough of the stack to allow a jump to
  148. * 'g_target'.
  149. */
  150. static void *g_save_state(void *old_stack_pointer, void *rawthrd)
  151. {
  152. struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
  153. if (g_allocate_source_stacklet(old_stack_pointer, thrd) < 0)
  154. return NULL;
  155. g_clear_stack(thrd->g_target, thrd);
  156. return thrd->g_target->stack_start;
  157. }
  158. /* This saves the current state in a new stacklet that gets stored in
  159. * 'g_source', but returns NULL, to not do any restoring yet.
  160. */
  161. static void *g_initial_save_state(void *old_stack_pointer, void *rawthrd)
  162. {
  163. struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
  164. if (g_allocate_source_stacklet(old_stack_pointer, thrd) == 0)
  165. g_save(thrd->g_source, thrd->g_current_stack_marker
  166. #ifdef DEBUG_DUMP
  167. , 0
  168. #endif
  169. );
  170. return NULL;
  171. }
  172. /* Save away enough of the stack to allow a jump to 'g_target'.
  173. */
  174. static void *g_destroy_state(void *old_stack_pointer, void *rawthrd)
  175. {
  176. struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
  177. thrd->g_source = EMPTY_STACKLET_HANDLE;
  178. g_clear_stack(thrd->g_target, thrd);
  179. return thrd->g_target->stack_start;
  180. }
  181. /* Restore the C stack by copying back from the heap in 'g_target',
  182. * and free 'g_target'.
  183. */
  184. static void *g_restore_state(void *new_stack_pointer, void *rawthrd)
  185. {
  186. /* Restore the heap copy back into the C stack */
  187. struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
  188. struct stacklet_s *g = thrd->g_target;
  189. ptrdiff_t stack_saved = g->stack_saved;
  190. assert(new_stack_pointer == g->stack_start);
  191. #if STACK_DIRECTION == 0
  192. memcpy(g->stack_start, g+1, stack_saved);
  193. #else
  194. memcpy(g->stack_start - stack_saved, g+1, stack_saved);
  195. #endif
  196. thrd->g_current_stack_stop = g->stack_stop;
  197. free(g);
  198. return EMPTY_STACKLET_HANDLE;
  199. }
  200. static void g_initialstub(struct stacklet_thread_s *thrd,
  201. stacklet_run_fn run, void *run_arg)
  202. {
  203. struct stacklet_s *result;
  204. /* The following call returns twice! */
  205. result = (struct stacklet_s *) _stacklet_switchstack(g_initial_save_state,
  206. g_restore_state,
  207. thrd);
  208. if (result == NULL && thrd->g_source != NULL) {
  209. /* First time it returns. Only g_initial_save_state() has run
  210. and has created 'g_source'. Call run(). */
  211. thrd->g_current_stack_stop = thrd->g_current_stack_marker;
  212. result = run(thrd->g_source, run_arg);
  213. /* Then switch to 'result'. */
  214. thrd->g_target = result;
  215. _stacklet_switchstack(g_destroy_state, g_restore_state, thrd);
  216. assert(!"stacklet: we should not return here");
  217. abort();
  218. }
  219. /* The second time it returns. */
  220. }
  221. /************************************************************/
  222. stacklet_thread_handle stacklet_newthread(void)
  223. {
  224. struct stacklet_thread_s *thrd;
  225. if (_stacklet_switchstack == NULL) {
  226. /* set up the following global with an indirection, which is needed
  227. to prevent any inlining */
  228. _stacklet_initialstub = g_initialstub;
  229. _stacklet_switchstack = slp_switch;
  230. }
  231. thrd = malloc(sizeof(struct stacklet_thread_s));
  232. if (thrd != NULL)
  233. memset(thrd, 0, sizeof(struct stacklet_thread_s));
  234. return thrd;
  235. }
  236. void stacklet_deletethread(stacklet_thread_handle thrd)
  237. {
  238. free(thrd);
  239. }
  240. stacklet_handle stacklet_new(stacklet_thread_handle thrd,
  241. stacklet_run_fn run, void *run_arg)
  242. {
  243. long stackmarker;
  244. assert((char *)NULL < (char *)&stackmarker);
  245. if (thrd->g_current_stack_stop <= (char *)&stackmarker)
  246. thrd->g_current_stack_stop = ((char *)&stackmarker) + 1;
  247. thrd->g_current_stack_marker = (char *)&stackmarker;
  248. _stacklet_initialstub(thrd, run, run_arg);
  249. return thrd->g_source;
  250. }
  251. stacklet_handle stacklet_switch(stacklet_thread_handle thrd,
  252. stacklet_handle target)
  253. {
  254. long stackmarker;
  255. if (thrd->g_current_stack_stop <= (char *)&stackmarker)
  256. thrd->g_current_stack_stop = ((char *)&stackmarker) + 1;
  257. thrd->g_target = target;
  258. _stacklet_switchstack(g_save_state, g_restore_state, thrd);
  259. return thrd->g_source;
  260. }
  261. void stacklet_destroy(stacklet_thread_handle thrd, stacklet_handle target)
  262. {
  263. /* remove 'target' from the chained list 'unsaved_stack', if it is there */
  264. struct stacklet_s **pp = &thrd->g_stack_chain_head;
  265. for (; *pp != NULL; pp = &(*pp)->stack_prev)
  266. if (*pp == target) {
  267. *pp = target->stack_prev;
  268. break;
  269. }
  270. free(target);
  271. }
  272. char **_stacklet_translate_pointer(stacklet_handle context, char **ptr)
  273. {
  274. char *p = (char *)ptr;
  275. long delta;
  276. if (context == NULL)
  277. return ptr;
  278. delta = p - context->stack_start;
  279. if (((unsigned long)delta) < ((unsigned long)context->stack_saved)) {
  280. /* a pointer to a saved away word */
  281. char *c = (char *)(context + 1);
  282. return (char **)(c + delta);
  283. }
  284. if (((unsigned long)delta) >=
  285. (unsigned long)(context->stack_stop - context->stack_start)) {
  286. /* out-of-stack pointer! it's only ok if we are the main stacklet
  287. and we are reading past the end, because the main stacklet's
  288. stack stop is not exactly known. */
  289. assert(delta >= 0);
  290. assert(((long)context->stack_stop) & 1);
  291. }
  292. return ptr;
  293. }