greenlet_thread_support.hpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #ifndef GREENLET_THREAD_SUPPORT_HPP
  2. #define GREENLET_THREAD_SUPPORT_HPP
  3. /**
  4. * Defines various utility functions to help greenlet integrate well
  5. * with threads. When possible, we use portable C++ 11 threading; when
  6. * not possible, we will use platform specific APIs if needed and
  7. * available. (Currently, this is only for Python 2.7 on Windows.)
  8. */
  9. #include <stdexcept>
  10. #include "greenlet_compiler_compat.hpp"
  11. // Allow setting this to 0 on the command line so that we
  12. // can test these code paths on compilers that otherwise support
  13. // standard threads.
  14. #ifndef G_USE_STANDARD_THREADING
  15. #if __cplusplus >= 201103
  16. // Cool. We should have standard support
  17. # define G_USE_STANDARD_THREADING 1
  18. #elif defined(_MSC_VER)
  19. // MSVC doesn't use a modern version of __cplusplus automatically, you
  20. // have to opt-in to update it with /Zc:__cplusplus, but that's not
  21. // available on our old version of visual studio for Python 2.7
  22. # if _MSC_VER <= 1500
  23. // Python 2.7 on Windows. Use the Python thread state and native Win32 APIs.
  24. # define G_USE_STANDARD_THREADING 0
  25. # else
  26. // Assume we have a compiler that supports it. The Appveyor compilers
  27. // we use all do have standard support
  28. # define G_USE_STANDARD_THREADING 1
  29. # endif
  30. #elif defined(__GNUC__) || defined(__clang__)
  31. // All tested versions either do, or can with the right --std argument, support what we need
  32. # define G_USE_STANDARD_THREADING 1
  33. #else
  34. # define G_USE_STANDARD_THREADING 0
  35. #endif
  36. #endif /* G_USE_STANDARD_THREADING */
  37. namespace greenlet {
  38. class LockInitError : public std::runtime_error
  39. {
  40. public:
  41. LockInitError(const char* what) : std::runtime_error(what)
  42. {};
  43. };
  44. };
  45. #if G_USE_STANDARD_THREADING == 1
  46. # define G_THREAD_LOCAL_SUPPORTS_DESTRUCTOR 1
  47. # include <thread>
  48. # include <mutex>
  49. # define G_THREAD_LOCAL_VAR thread_local
  50. namespace greenlet {
  51. typedef std::mutex Mutex;
  52. typedef std::lock_guard<Mutex> LockGuard;
  53. };
  54. #else
  55. // NOTE: At this writing, the mutex isn't currently required;
  56. // we don't use a shared cleanup queue or Py_AddPendingCall in this
  57. // model, we rely on the thread state dictionary for cleanup.
  58. # if defined(_MSC_VER)
  59. // We should only hit this case for Python 2.7 on Windows.
  60. # define G_THREAD_LOCAL_VAR __declspec(thread)
  61. # include <windows.h>
  62. namespace greenlet {
  63. class Mutex
  64. {
  65. CRITICAL_SECTION _mutex;
  66. G_NO_COPIES_OF_CLS(Mutex);
  67. public:
  68. Mutex()
  69. {
  70. InitializeCriticalSection(&this->_mutex);
  71. };
  72. void Lock()
  73. {
  74. EnterCriticalSection(&this->_mutex);
  75. };
  76. void UnLock()
  77. {
  78. LeaveCriticalSection(&this->_mutex);
  79. };
  80. };
  81. };
  82. # elif (defined(__GNUC__) || defined(__clang__)) || (defined(__SUNPRO_C))
  83. // GCC, clang, SunStudio all use __thread for thread-local variables.
  84. // For locks, we can use PyThread APIs, officially added in 3.2, but
  85. // present back to 2.7
  86. # define G_THREAD_LOCAL_VAR __thread
  87. # include "pythread.h"
  88. namespace greenlet {
  89. class Mutex
  90. {
  91. PyThread_type_lock _mutex;
  92. G_NO_COPIES_OF_CLS(Mutex);
  93. public:
  94. Mutex()
  95. {
  96. this->_mutex = PyThread_allocate_lock();
  97. if (!this->_mutex) {
  98. throw LockInitError("Failed to initialize mutex.");
  99. }
  100. };
  101. void Lock()
  102. {
  103. PyThread_acquire_lock(this->_mutex, WAIT_LOCK);
  104. };
  105. void UnLock()
  106. {
  107. PyThread_release_lock(this->_mutex);
  108. };
  109. };
  110. };
  111. # else
  112. # error Unable to declare thread-local variables.
  113. # endif
  114. // the RAII lock keeper for all non-standard threading platforms.
  115. namespace greenlet {
  116. class LockGuard
  117. {
  118. Mutex& _mutex;
  119. G_NO_COPIES_OF_CLS(LockGuard);
  120. public:
  121. LockGuard(Mutex& m) : _mutex(m)
  122. {
  123. this->_mutex.Lock();
  124. };
  125. ~LockGuard()
  126. {
  127. this->_mutex.UnLock();
  128. };
  129. };
  130. };
  131. #endif /* G_USE_STANDARD_THREADING == 1 */
  132. #endif /* GREENLET_THREAD_SUPPORT_HPP */