greenlet_slp_switch.hpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #ifndef GREENLET_SLP_SWITCH_HPP
  2. #define GREENLET_SLP_SWITCH_HPP
  3. #include "greenlet_compiler_compat.hpp"
  4. #include "greenlet_refs.hpp"
  5. /*
  6. * the following macros are spliced into the OS/compiler
  7. * specific code, in order to simplify maintenance.
  8. */
  9. // We can save about 10% of the time it takes to switch greenlets if
  10. // we thread the thread state through the slp_save_state() and the
  11. // following slp_restore_state() calls from
  12. // slp_switch()->g_switchstack() (which already needs to access it).
  13. //
  14. // However:
  15. //
  16. // that requires changing the prototypes and implementations of the
  17. // switching functions. If we just change the prototype of
  18. // slp_switch() to accept the argument and update the macros, without
  19. // changing the implementation of slp_switch(), we get crashes on
  20. // 64-bit Linux and 32-bit x86 (for reasons that aren't 100% clear);
  21. // on the other hand, 64-bit macOS seems to be fine. Also, 64-bit
  22. // windows is an issue because slp_switch is written fully in assembly
  23. // and currently ignores its argument so some code would have to be
  24. // adjusted there to pass the argument on to the
  25. // ``slp_save_state_asm()`` function (but interestingly, because of
  26. // the calling convention, the extra argument is just ignored and
  27. // things function fine, albeit slower, if we just modify
  28. // ``slp_save_state_asm`()` to fetch the pointer to pass to the
  29. // macro.)
  30. //
  31. // Our compromise is to use a *glabal*, untracked, weak, pointer
  32. // to the necessary thread state during the process of switching only.
  33. // This is safe because we're protected by the GIL, and if we're
  34. // running this code, the thread isn't exiting. This also nets us a
  35. // 10-12% speed improvement.
  36. static greenlet::Greenlet* volatile switching_thread_state = nullptr;
  37. #ifdef GREENLET_NOINLINE_SUPPORTED
  38. extern "C" {
  39. static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref);
  40. static void GREENLET_NOINLINE(slp_restore_state_trampoline)();
  41. }
  42. #define GREENLET_NOINLINE_INIT() \
  43. do { \
  44. } while (0)
  45. #else
  46. /* force compiler to call functions via pointers */
  47. /* XXX: Do we even want/need to support such compilers? This code path
  48. is untested on CI. */
  49. extern "C" {
  50. static int (slp_save_state_trampoline)(char* stackref);
  51. static void (slp_restore_state_trampoline)();
  52. }
  53. #define GREENLET_NOINLINE(name) cannot_inline_##name
  54. #define GREENLET_NOINLINE_INIT() \
  55. do { \
  56. slp_save_state_trampoline = GREENLET_NOINLINE(slp_save_state_trampoline); \
  57. slp_restore_state_trampoline = GREENLET_NOINLINE(slp_restore_state_trampoline); \
  58. } while (0)
  59. #endif
  60. #define SLP_SAVE_STATE(stackref, stsizediff) \
  61. do { \
  62. assert(switching_thread_state); \
  63. stackref += STACK_MAGIC; \
  64. if (slp_save_state_trampoline((char*)stackref)) \
  65. return -1; \
  66. if (!switching_thread_state->active()) \
  67. return 1; \
  68. stsizediff = switching_thread_state->stack_start() - (char*)stackref; \
  69. } while (0)
  70. #define SLP_RESTORE_STATE() slp_restore_state_trampoline()
  71. #define SLP_EVAL
  72. extern "C" {
  73. #define slp_switch GREENLET_NOINLINE(slp_switch)
  74. #include "slp_platformselect.h"
  75. }
  76. #undef slp_switch
  77. #ifndef STACK_MAGIC
  78. # error \
  79. "greenlet needs to be ported to this platform, or taught how to detect your compiler properly."
  80. #endif /* !STACK_MAGIC */
  81. #ifdef EXTERNAL_ASM
  82. /* CCP addition: Make these functions, to be called from assembler.
  83. * The token include file for the given platform should enable the
  84. * EXTERNAL_ASM define so that this is included.
  85. */
  86. extern "C" {
  87. intptr_t
  88. slp_save_state_asm(intptr_t* ref)
  89. {
  90. intptr_t diff;
  91. SLP_SAVE_STATE(ref, diff);
  92. return diff;
  93. }
  94. void
  95. slp_restore_state_asm(void)
  96. {
  97. SLP_RESTORE_STATE();
  98. }
  99. extern int slp_switch(void);
  100. };
  101. #endif
  102. #endif