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