PyModule.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #ifndef PY_MODULE_CPP
  2. #define PY_MODULE_CPP
  3. #include "greenlet_internal.hpp"
  4. #include "TGreenletGlobals.cpp"
  5. #include "TMainGreenlet.cpp"
  6. #include "TThreadStateDestroy.cpp"
  7. using greenlet::LockGuard;
  8. using greenlet::ThreadState;
  9. #ifdef __clang__
  10. # pragma clang diagnostic push
  11. # pragma clang diagnostic ignored "-Wunused-function"
  12. # pragma clang diagnostic ignored "-Wunused-variable"
  13. #endif
  14. PyDoc_STRVAR(mod_getcurrent_doc,
  15. "getcurrent() -> greenlet\n"
  16. "\n"
  17. "Returns the current greenlet (i.e. the one which called this "
  18. "function).\n");
  19. static PyObject*
  20. mod_getcurrent(PyObject* UNUSED(module))
  21. {
  22. return GET_THREAD_STATE().state().get_current().relinquish_ownership_o();
  23. }
  24. PyDoc_STRVAR(mod_settrace_doc,
  25. "settrace(callback) -> object\n"
  26. "\n"
  27. "Sets a new tracing function and returns the previous one.\n");
  28. static PyObject*
  29. mod_settrace(PyObject* UNUSED(module), PyObject* args)
  30. {
  31. PyArgParseParam tracefunc;
  32. if (!PyArg_ParseTuple(args, "O", &tracefunc)) {
  33. return NULL;
  34. }
  35. ThreadState& state = GET_THREAD_STATE();
  36. OwnedObject previous = state.get_tracefunc();
  37. if (!previous) {
  38. previous = Py_None;
  39. }
  40. state.set_tracefunc(tracefunc);
  41. return previous.relinquish_ownership();
  42. }
  43. PyDoc_STRVAR(mod_gettrace_doc,
  44. "gettrace() -> object\n"
  45. "\n"
  46. "Returns the currently set tracing function, or None.\n");
  47. static PyObject*
  48. mod_gettrace(PyObject* UNUSED(module))
  49. {
  50. OwnedObject tracefunc = GET_THREAD_STATE().state().get_tracefunc();
  51. if (!tracefunc) {
  52. tracefunc = Py_None;
  53. }
  54. return tracefunc.relinquish_ownership();
  55. }
  56. PyDoc_STRVAR(mod_set_thread_local_doc,
  57. "set_thread_local(key, value) -> None\n"
  58. "\n"
  59. "Set a value in the current thread-local dictionary. Debugging only.\n");
  60. static PyObject*
  61. mod_set_thread_local(PyObject* UNUSED(module), PyObject* args)
  62. {
  63. PyArgParseParam key;
  64. PyArgParseParam value;
  65. PyObject* result = NULL;
  66. if (PyArg_UnpackTuple(args, "set_thread_local", 2, 2, &key, &value)) {
  67. if(PyDict_SetItem(
  68. PyThreadState_GetDict(), // borrow
  69. key,
  70. value) == 0 ) {
  71. // success
  72. Py_INCREF(Py_None);
  73. result = Py_None;
  74. }
  75. }
  76. return result;
  77. }
  78. PyDoc_STRVAR(mod_get_pending_cleanup_count_doc,
  79. "get_pending_cleanup_count() -> Integer\n"
  80. "\n"
  81. "Get the number of greenlet cleanup operations pending. Testing only.\n");
  82. static PyObject*
  83. mod_get_pending_cleanup_count(PyObject* UNUSED(module))
  84. {
  85. LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
  86. return PyLong_FromSize_t(mod_globs->thread_states_to_destroy.size());
  87. }
  88. PyDoc_STRVAR(mod_get_total_main_greenlets_doc,
  89. "get_total_main_greenlets() -> Integer\n"
  90. "\n"
  91. "Quickly return the number of main greenlets that exist. Testing only.\n");
  92. static PyObject*
  93. mod_get_total_main_greenlets(PyObject* UNUSED(module))
  94. {
  95. return PyLong_FromSize_t(G_TOTAL_MAIN_GREENLETS);
  96. }
  97. PyDoc_STRVAR(mod_get_clocks_used_doing_optional_cleanup_doc,
  98. "get_clocks_used_doing_optional_cleanup() -> Integer\n"
  99. "\n"
  100. "Get the number of clock ticks the program has used doing optional "
  101. "greenlet cleanup.\n"
  102. "Beginning in greenlet 2.0, greenlet tries to find and dispose of greenlets\n"
  103. "that leaked after a thread exited. This requires invoking Python's garbage collector,\n"
  104. "which may have a performance cost proportional to the number of live objects.\n"
  105. "This function returns the amount of processor time\n"
  106. "greenlet has used to do this. In programs that run with very large amounts of live\n"
  107. "objects, this metric can be used to decide whether the cost of doing this cleanup\n"
  108. "is worth the memory leak being corrected. If not, you can disable the cleanup\n"
  109. "using ``enable_optional_cleanup(False)``.\n"
  110. "The units are arbitrary and can only be compared to themselves (similarly to ``time.clock()``);\n"
  111. "for example, to see how it scales with your heap. You can attempt to convert them into seconds\n"
  112. "by dividing by the value of CLOCKS_PER_SEC."
  113. "If cleanup has been disabled, returns None."
  114. "\n"
  115. "This is an implementation specific, provisional API. It may be changed or removed\n"
  116. "in the future.\n"
  117. ".. versionadded:: 2.0"
  118. );
  119. static PyObject*
  120. mod_get_clocks_used_doing_optional_cleanup(PyObject* UNUSED(module))
  121. {
  122. std::clock_t& clocks = ThreadState::clocks_used_doing_gc();
  123. if (clocks == std::clock_t(-1)) {
  124. Py_RETURN_NONE;
  125. }
  126. // This might not actually work on some implementations; clock_t
  127. // is an opaque type.
  128. return PyLong_FromSsize_t(clocks);
  129. }
  130. PyDoc_STRVAR(mod_enable_optional_cleanup_doc,
  131. "mod_enable_optional_cleanup(bool) -> None\n"
  132. "\n"
  133. "Enable or disable optional cleanup operations.\n"
  134. "See ``get_clocks_used_doing_optional_cleanup()`` for details.\n"
  135. );
  136. static PyObject*
  137. mod_enable_optional_cleanup(PyObject* UNUSED(module), PyObject* flag)
  138. {
  139. int is_true = PyObject_IsTrue(flag);
  140. if (is_true == -1) {
  141. return nullptr;
  142. }
  143. std::clock_t& clocks = ThreadState::clocks_used_doing_gc();
  144. if (is_true) {
  145. // If we already have a value, we don't want to lose it.
  146. if (clocks == std::clock_t(-1)) {
  147. clocks = 0;
  148. }
  149. }
  150. else {
  151. clocks = std::clock_t(-1);
  152. }
  153. Py_RETURN_NONE;
  154. }
  155. #if !GREENLET_PY313
  156. PyDoc_STRVAR(mod_get_tstate_trash_delete_nesting_doc,
  157. "get_tstate_trash_delete_nesting() -> Integer\n"
  158. "\n"
  159. "Return the 'trash can' nesting level. Testing only.\n");
  160. static PyObject*
  161. mod_get_tstate_trash_delete_nesting(PyObject* UNUSED(module))
  162. {
  163. PyThreadState* tstate = PyThreadState_GET();
  164. #if GREENLET_PY312
  165. return PyLong_FromLong(tstate->trash.delete_nesting);
  166. #else
  167. return PyLong_FromLong(tstate->trash_delete_nesting);
  168. #endif
  169. }
  170. #endif
  171. static PyMethodDef GreenMethods[] = {
  172. {
  173. .ml_name="getcurrent",
  174. .ml_meth=(PyCFunction)mod_getcurrent,
  175. .ml_flags=METH_NOARGS,
  176. .ml_doc=mod_getcurrent_doc
  177. },
  178. {
  179. .ml_name="settrace",
  180. .ml_meth=(PyCFunction)mod_settrace,
  181. .ml_flags=METH_VARARGS,
  182. .ml_doc=mod_settrace_doc
  183. },
  184. {
  185. .ml_name="gettrace",
  186. .ml_meth=(PyCFunction)mod_gettrace,
  187. .ml_flags=METH_NOARGS,
  188. .ml_doc=mod_gettrace_doc
  189. },
  190. {
  191. .ml_name="set_thread_local",
  192. .ml_meth=(PyCFunction)mod_set_thread_local,
  193. .ml_flags=METH_VARARGS,
  194. .ml_doc=mod_set_thread_local_doc
  195. },
  196. {
  197. .ml_name="get_pending_cleanup_count",
  198. .ml_meth=(PyCFunction)mod_get_pending_cleanup_count,
  199. .ml_flags=METH_NOARGS,
  200. .ml_doc=mod_get_pending_cleanup_count_doc
  201. },
  202. {
  203. .ml_name="get_total_main_greenlets",
  204. .ml_meth=(PyCFunction)mod_get_total_main_greenlets,
  205. .ml_flags=METH_NOARGS,
  206. .ml_doc=mod_get_total_main_greenlets_doc
  207. },
  208. {
  209. .ml_name="get_clocks_used_doing_optional_cleanup",
  210. .ml_meth=(PyCFunction)mod_get_clocks_used_doing_optional_cleanup,
  211. .ml_flags=METH_NOARGS,
  212. .ml_doc=mod_get_clocks_used_doing_optional_cleanup_doc
  213. },
  214. {
  215. .ml_name="enable_optional_cleanup",
  216. .ml_meth=(PyCFunction)mod_enable_optional_cleanup,
  217. .ml_flags=METH_O,
  218. .ml_doc=mod_enable_optional_cleanup_doc
  219. },
  220. #if !GREENLET_PY313
  221. {
  222. .ml_name="get_tstate_trash_delete_nesting",
  223. .ml_meth=(PyCFunction)mod_get_tstate_trash_delete_nesting,
  224. .ml_flags=METH_NOARGS,
  225. .ml_doc=mod_get_tstate_trash_delete_nesting_doc
  226. },
  227. #endif
  228. {.ml_name=NULL, .ml_meth=NULL} /* Sentinel */
  229. };
  230. static const char* const copy_on_greentype[] = {
  231. "getcurrent",
  232. "error",
  233. "GreenletExit",
  234. "settrace",
  235. "gettrace",
  236. NULL
  237. };
  238. static struct PyModuleDef greenlet_module_def = {
  239. .m_base=PyModuleDef_HEAD_INIT,
  240. .m_name="greenlet._greenlet",
  241. .m_doc=NULL,
  242. .m_size=-1,
  243. .m_methods=GreenMethods,
  244. };
  245. #endif
  246. #ifdef __clang__
  247. # pragma clang diagnostic pop
  248. #elif defined(__GNUC__)
  249. # pragma GCC diagnostic pop
  250. #endif