codekingpro/portable-devtools
115k
1/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
2/* Format with:
3 * clang-format -i --style=file src/greenlet/greenlet.c
4 *
5 *
6 * Fix missing braces with:
7 * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
8*/
9#include <cstdlib>
10#include <string>
11#include <algorithm>
12#include <exception>
13
14
15#define PY_SSIZE_T_CLEAN
16#include <Python.h>
17#include "structmember.h" // PyMemberDef
18
19#include "greenlet_internal.hpp"
20// Code after this point can assume access to things declared in stdint.h,
21// including the fixed-width types. This goes for the platform-specific switch functions
22// as well.
23#include "greenlet_refs.hpp"
24#include "greenlet_slp_switch.hpp"
25
26#include "greenlet_thread_support.hpp"
27#include "TGreenlet.hpp"
28
29#include "TGreenletGlobals.cpp"
30
31#include "TGreenlet.cpp"
32#include "TMainGreenlet.cpp"
33#include "TUserGreenlet.cpp"
34#include "TBrokenGreenlet.cpp"
35#include "TExceptionState.cpp"
36#include "TPythonState.cpp"
37#include "TStackState.cpp"
38
39#include "TThreadState.hpp"
40#include "TThreadStateCreator.hpp"
41#include "TThreadStateDestroy.cpp"
42
43#include "PyGreenlet.cpp"
44#include "PyGreenletUnswitchable.cpp"
45#include "CObjects.cpp"
46
47using greenlet::LockGuard;
48using greenlet::LockInitError;
49using greenlet::PyErrOccurred;
50using greenlet::Require;
51
52using greenlet::g_handle_exit;
53using greenlet::single_result;
54
55using greenlet::Greenlet;
56using greenlet::UserGreenlet;
57using greenlet::MainGreenlet;
58using greenlet::BrokenGreenlet;
59using greenlet::ThreadState;
60using greenlet::PythonState;
61
62
63
64// ******* Implementation of things from included files
65template<typename T, greenlet::refs::TypeChecker TC>
66greenlet::refs::_BorrowedGreenlet<T, TC>& greenlet::refs::_BorrowedGreenlet<T, TC>::operator=(const greenlet::refs::BorrowedObject& other)
67{
68 this->_set_raw_pointer(static_cast<PyObject*>(other));
69 return *this;
70}
71
72template <typename T, greenlet::refs::TypeChecker TC>
73inline greenlet::refs::_BorrowedGreenlet<T, TC>::operator Greenlet*() const noexcept
74{
75 if (!this->p) {
76 return nullptr;
77 }
78 return reinterpret_cast<PyGreenlet*>(this->p)->pimpl;
79}
80
81template<typename T, greenlet::refs::TypeChecker TC>
82greenlet::refs::_BorrowedGreenlet<T, TC>::_BorrowedGreenlet(const BorrowedObject& p)
83 : BorrowedReference<T, TC>(nullptr)
84{
85
86 this->_set_raw_pointer(p.borrow());
87}
88
89template <typename T, greenlet::refs::TypeChecker TC>
90inline greenlet::refs::_OwnedGreenlet<T, TC>::operator Greenlet*() const noexcept
91{
92 if (!this->p) {
93 return nullptr;
94 }
95 return reinterpret_cast<PyGreenlet*>(this->p)->pimpl;
96}
97
98
99
100#ifdef __clang__
101# pragma clang diagnostic push
102# pragma clang diagnostic ignored "-Wmissing-field-initializers"
103# pragma clang diagnostic ignored "-Wwritable-strings"
104#elif defined(__GNUC__)
105# pragma GCC diagnostic push
106// warning: ISO C++ forbids converting a string constant to ‘char*’
107// (The python APIs aren't const correct and accept writable char*)
108# pragma GCC diagnostic ignored "-Wwrite-strings"
109#endif
110
111
112/***********************************************************
113
114A PyGreenlet is a range of C stack addresses that must be
115saved and restored in such a way that the full range of the
116stack contains valid data when we switch to it.
117
118Stack layout for a greenlet:
119
120 | ^^^ |
121 | older data |
122 | |
123 stack_stop . |_______________|
124 . | |
125 . | greenlet data |
126 . | in stack |
127 . * |_______________| . . _____________ stack_copy + stack_saved
128 . | | | |
129 . | data | |greenlet data|
130 . | unrelated | | saved |
131 . | to | | in heap |
132 stack_start . | this | . . |_____________| stack_copy
133 | greenlet |
134 | |
135 | newer data |
136 | vvv |
137
138
139Note that a greenlet's stack data is typically partly at its correct
140place in the stack, and partly saved away in the heap, but always in
141the above configuration: two blocks, the more recent one in the heap
142and the older one still in the stack (either block may be empty).
143
144Greenlets are chained: each points to the previous greenlet, which is
145the one that owns the data currently in the C stack above my
146stack_stop. The currently running greenlet is the first element of
147this chain. The main (initial) greenlet is the last one. Greenlets
148whose stack is entirely in the heap can be skipped from the chain.
149
150The chain is not related to execution order, but only to the order
151in which bits of C stack happen to belong to greenlets at a particular
152point in time.
153
154The main greenlet doesn't have a stack_stop: it is responsible for the
155complete rest of the C stack, and we don't know where it begins. We
156use (char*) -1, the largest possible address.
157
158States:
159 stack_stop == NULL && stack_start == NULL: did not start yet
160 stack_stop != NULL && stack_start == NULL: already finished
161 stack_stop != NULL && stack_start != NULL: active
162
163The running greenlet's stack_start is undefined but not NULL.
164
165 ***********************************************************/
166
167
168
169
170/***********************************************************/
171
172/* Some functions must not be inlined:
173 * slp_restore_state, when inlined into slp_switch might cause
174 it to restore stack over its own local variables
175 * slp_save_state, when inlined would add its own local
176 variables to the saved stack, wasting space
177 * slp_switch, cannot be inlined for obvious reasons
178 * g_initialstub, when inlined would receive a pointer into its
179 own stack frame, leading to incomplete stack save/restore
180
181g_initialstub is a member function and declared virtual so that the
182compiler always calls it through a vtable.
183
184slp_save_state and slp_restore_state are also member functions. They
185are called from trampoline functions that themselves are declared as
186not eligible for inlining.
187*/
188
189extern "C" {
190static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref)
191{
192 return switching_thread_state->slp_save_state(stackref);
193}
194static void GREENLET_NOINLINE(slp_restore_state_trampoline)()
195{
196 switching_thread_state->slp_restore_state();
197}
198}
199
200
201/***********************************************************/
202
203
204#include "PyModule.cpp"
205
206
207
208static PyObject*
209greenlet_internal_mod_init() noexcept
210{
211 static void* _PyGreenlet_API[PyGreenlet_API_pointers];
212
213 try {
214 CreatedModule m(greenlet_module_def);
215
216 Require(PyType_Ready(&PyGreenlet_Type));
217 Require(PyType_Ready(&PyGreenletUnswitchable_Type));
218
219 mod_globs = new greenlet::GreenletGlobals;
220 ThreadState::init();
221
222 m.PyAddObject("greenlet", PyGreenlet_Type);
223 m.PyAddObject("UnswitchableGreenlet", PyGreenletUnswitchable_Type);
224 m.PyAddObject("error", mod_globs->PyExc_GreenletError);
225 m.PyAddObject("GreenletExit", mod_globs->PyExc_GreenletExit);
226
227 m.PyAddObject("GREENLET_USE_GC", 1);
228 m.PyAddObject("GREENLET_USE_TRACING", 1);
229 m.PyAddObject("GREENLET_USE_CONTEXT_VARS", 1L);
230 m.PyAddObject("GREENLET_USE_STANDARD_THREADING", 1L);
231
232 NewReference clocks_per_sec(Require(PyLong_FromSsize_t(CLOCKS_PER_SEC)));
233 m.PyAddObject("CLOCKS_PER_SEC", clocks_per_sec);
234
235 /* also publish module-level data as attributes of the greentype. */
236 // XXX: This is weird, and enables a strange pattern of
237 // confusing the class greenlet with the module greenlet; with
238 // the exception of (possibly) ``getcurrent()``, this
239 // shouldn't be encouraged so don't add new items here.
240 for (const char* const* p = copy_on_greentype; *p; p++) {
241 OwnedObject o = m.PyRequireAttr(*p);
242 Require(PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o.borrow()));
243 }
244
245 /*
246 * Expose C API
247 */
248
249 /* types */
250 _PyGreenlet_API[PyGreenlet_Type_NUM] = (void*)&PyGreenlet_Type;
251
252 /* exceptions */
253 _PyGreenlet_API[PyExc_GreenletError_NUM] = (void*)mod_globs->PyExc_GreenletError;
254 _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void*)mod_globs->PyExc_GreenletExit;
255
256 /* methods */
257 _PyGreenlet_API[PyGreenlet_New_NUM] = (void*)PyGreenlet_New;
258 _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void*)PyGreenlet_GetCurrent;
259 _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void*)PyGreenlet_Throw;
260 _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void*)PyGreenlet_Switch;
261 _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void*)PyGreenlet_SetParent;
262
263 /* Previously macros, but now need to be functions externally. */
264 _PyGreenlet_API[PyGreenlet_MAIN_NUM] = (void*)Extern_PyGreenlet_MAIN;
265 _PyGreenlet_API[PyGreenlet_STARTED_NUM] = (void*)Extern_PyGreenlet_STARTED;
266 _PyGreenlet_API[PyGreenlet_ACTIVE_NUM] = (void*)Extern_PyGreenlet_ACTIVE;
267 _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM] = (void*)Extern_PyGreenlet_GET_PARENT;
268
269 /* XXX: Note that our module name is ``greenlet._greenlet``, but for
270 backwards compatibility with existing C code, we need the _C_API to
271 be directly in greenlet.
272 */
273 const NewReference c_api_object(Require(
274 PyCapsule_New(
275 (void*)_PyGreenlet_API,
276 "greenlet._C_API",
277 NULL)));
278 m.PyAddObject("_C_API", c_api_object);
279 assert(c_api_object.REFCNT() == 2);
280
281 // cerr << "Sizes:"
282 // << "\n\tGreenlet : " << sizeof(Greenlet)
283 // << "\n\tUserGreenlet : " << sizeof(UserGreenlet)
284 // << "\n\tMainGreenlet : " << sizeof(MainGreenlet)
285 // << "\n\tExceptionState : " << sizeof(greenlet::ExceptionState)
286 // << "\n\tPythonState : " << sizeof(greenlet::PythonState)
287 // << "\n\tStackState : " << sizeof(greenlet::StackState)
288 // << "\n\tSwitchingArgs : " << sizeof(greenlet::SwitchingArgs)
289 // << "\n\tOwnedObject : " << sizeof(greenlet::refs::OwnedObject)
290 // << "\n\tBorrowedObject : " << sizeof(greenlet::refs::BorrowedObject)
291 // << "\n\tPyGreenlet : " << sizeof(PyGreenlet)
292 // << endl;
293
294#ifdef Py_GIL_DISABLED
295 PyUnstable_Module_SetGIL(m.borrow(), Py_MOD_GIL_NOT_USED);
296#endif
297
298 return m.borrow(); // But really it's the main reference.
299 }
300 catch (const LockInitError& e) {
301 PyErr_SetString(PyExc_MemoryError, e.what());
302 return NULL;
303 }
304 catch (const PyErrOccurred&) {
305 return NULL;
306 }
307
308}
309
310extern "C" {
311
312PyMODINIT_FUNC
313PyInit__greenlet(void)
314{
315 return greenlet_internal_mod_init();
316}
317
318}; // extern C
319
320#ifdef __clang__
321# pragma clang diagnostic pop
322#elif defined(__GNUC__)
323# pragma GCC diagnostic pop
324#endif
325 