codekingpro/portable-devtools
114k
1/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
2/**
3 * Implementation of greenlet::UserGreenlet.
4 *
5 * Format with:
6 * clang-format -i --style=file src/greenlet/greenlet.c
7 *
8 *
9 * Fix missing braces with:
10 * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
11*/
12#ifndef T_USER_GREENLET_CPP
13#define T_USER_GREENLET_CPP
14
15#include "greenlet_internal.hpp"
16#include "TGreenlet.hpp"
17
18#include "TThreadStateDestroy.cpp"
19
20
21namespace greenlet {
22using greenlet::refs::BorrowedMainGreenlet;
23greenlet::PythonAllocator<UserGreenlet> UserGreenlet::allocator;
24
25void* UserGreenlet::operator new(size_t UNUSED(count))
26{
27 return allocator.allocate(1);
28}
29
30
31void UserGreenlet::operator delete(void* ptr)
32{
33 return allocator.deallocate(static_cast<UserGreenlet*>(ptr),
34 1);
35}
36
37
38UserGreenlet::UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent)
39 : Greenlet(p), _parent(the_parent)
40{
41}
42
43UserGreenlet::~UserGreenlet()
44{
45 // Python 3.11: If we don't clear out the raw frame datastack
46 // when deleting an unfinished greenlet,
47 // TestLeaks.test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main fails.
48 this->python_state.did_finish(nullptr);
49 this->tp_clear();
50}
51
52
53const BorrowedMainGreenlet
54UserGreenlet::main_greenlet() const
55{
56 return this->_main_greenlet;
57}
58
59
60BorrowedMainGreenlet
61UserGreenlet::find_main_greenlet_in_lineage() const
62{
63 if (this->started()) {
64 assert(this->_main_greenlet);
65 return BorrowedMainGreenlet(this->_main_greenlet);
66 }
67
68 if (!this->_parent) {
69 /* garbage collected greenlet in chain */
70 // XXX: WHAT?
71 return BorrowedMainGreenlet(nullptr);
72 }
73
74 return this->_parent->find_main_greenlet_in_lineage();
75}
76
77
78/**
79 * CAUTION: This will allocate memory and may trigger garbage
80 * collection and arbitrary Python code.
81 */
82OwnedObject
83UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state)
84{
85 /* The dying greenlet cannot be a parent of ts_current
86 because the 'parent' field chain would hold a
87 reference */
88 UserGreenlet::ParentIsCurrentGuard with_current_parent(this, current_thread_state);
89
90 // We don't care about the return value, only whether an
91 // exception happened. Whether or not an exception happens,
92 // we need to restore the parent in case the greenlet gets
93 // resurrected.
94 return Greenlet::throw_GreenletExit_during_dealloc(current_thread_state);
95}
96
97ThreadState*
98UserGreenlet::thread_state() const noexcept
99{
100 // TODO: maybe make this throw, if the thread state isn't there?
101 // if (!this->main_greenlet) {
102 // throw std::runtime_error("No thread state"); // TODO: Better exception
103 // }
104 if (!this->_main_greenlet) {
105 return nullptr;
106 }
107 return this->_main_greenlet->thread_state();
108}
109
110
111bool
112UserGreenlet::was_running_in_dead_thread() const noexcept
113{
114 return this->_main_greenlet && !this->thread_state();
115}
116
117OwnedObject
118UserGreenlet::g_switch()
119{
120 try {
121 if (!this->args() && !PyErr_Occurred()) {
122 // we have nothing to send as the result of switching,
123 // most likely because we've somehow allowed concurrent
124 // uses of switch from multiple threads (which may or may
125 // not be allowed by check_switch_allowed)
126 // ``green_switch`` defends against this by calling
127 // ``check_switch_allowed`` before messing with
128 // ``args()``, but we have at least one internal caller
129 // (``throw_GreenletExit_during_dealloc``) so we keep both
130 // this explicit check and our call to
131 // ``check_switch_allowed``
132 throw PyErrOccurred(mod_globs->PyExc_GreenletError,
133 "cannot switch with no pending arguments or exception");
134 }
135 this->check_switch_allowed();
136 }
137 catch (const PyErrOccurred&) {
138 this->release_args();
139 throw;
140 }
141
142 // Switching greenlets used to attempt to clean out ones that need
143 // deleted *if* we detected a thread switch. Should it still do
144 // that?
145 // An issue is that if we delete a greenlet from another thread,
146 // it gets queued to this thread, and ``kill_greenlet()`` switches
147 // back into the greenlet
148
149 /* find the real target by ignoring dead greenlets,
150 and if necessary starting a greenlet. */
151 switchstack_result_t err;
152 Greenlet* target = this;
153 // TODO: probably cleaner to handle the case where we do
154 // switch to ourself separately from the other cases.
155 // This can probably even further be simplified if we keep
156 // track of the switching_state we're going for and just call
157 // into g_switch() if it's not ourself. The main problem with that
158 // is that we would be using more stack space.
159 bool target_was_me = true;
160 bool was_initial_stub = false;
161 while (target) {
162 if (target->active()) {
163 if (!target_was_me) {
164 target->args() <<= this->args();
165 assert(!this->args());
166 }
167 err = target->g_switchstack();
168 break;
169 }
170 if (!target->started()) {
171 // We never encounter a main greenlet that's not started.
172 assert(!target->main());
173 UserGreenlet* real_target = static_cast<UserGreenlet*>(target);
174 assert(real_target);
175 void* dummymarker;
176 was_initial_stub = true;
177 if (!target_was_me) {
178 target->args() <<= this->args();
179 assert(!this->args());
180 }
181 try {
182 // This can only throw back to us while we're
183 // still in this greenlet. Once the new greenlet
184 // is bootstrapped, it has its own exception state.
185 err = real_target->g_initialstub(&dummymarker);
186 }
187 catch (const PyErrOccurred&) {
188 this->release_args();
189 throw;
190 }
191 catch (const GreenletStartedWhileInPython&) {
192 // The greenlet was started sometime before this
193 // greenlet actually switched to it, i.e.,
194 // "concurrent" calls to switch() or throw().
195 // We need to retry the switch.
196 // Note that the current greenlet has been reset
197 // to this one (or we wouldn't be running!)
198 continue;
199 }
200 break;
201 }
202
203 target = target->parent();
204 target_was_me = false;
205 }
206 // The ``this`` pointer and all other stack or register based
207 // variables are invalid now, at least where things succeed
208 // above.
209 // But this one, probably not so much? It's not clear if it's
210 // safe to throw an exception at this point.
211
212 if (err.status < 0) {
213 // If we get here, either g_initialstub()
214 // failed, or g_switchstack() failed. Either one of those
215 // cases SHOULD leave us in the original greenlet with a valid
216 // stack.
217 return this->on_switchstack_or_initialstub_failure(target, err, target_was_me, was_initial_stub);
218 }
219
220 // err.the_new_current_greenlet would be the same as ``target``,
221 // if target wasn't probably corrupt.
222 return err.the_new_current_greenlet->g_switch_finish(err);
223}
224
225
226
227Greenlet::switchstack_result_t
228UserGreenlet::g_initialstub(void* mark)
229{
230 OwnedObject run;
231
232 // We need to grab a reference to the current switch arguments
233 // in case we're entered concurrently during the call to
234 // GetAttr() and have to try again.
235 // We'll restore them when we return in that case.
236 // Scope them tightly to avoid ref leaks.
237 {
238 SwitchingArgs args(this->args());
239
240 /* save exception in case getattr clears it */
241 PyErrPieces saved;
242
243 /*
244 self.run is the object to call in the new greenlet.
245 This could run arbitrary python code and switch greenlets!
246 */
247 run = this->self().PyRequireAttr(mod_globs->str_run);
248 /* restore saved exception */
249 saved.PyErrRestore();
250
251
252 /* recheck that it's safe to switch in case greenlet reparented anywhere above */
253 this->check_switch_allowed();
254
255 /* by the time we got here another start could happen elsewhere,
256 * that means it should now be a regular switch.
257 * This can happen if the Python code is a subclass that implements
258 * __getattribute__ or __getattr__, or makes ``run`` a descriptor;
259 * all of those can run arbitrary code that switches back into
260 * this greenlet.
261 */
262 if (this->stack_state.started()) {
263 // the successful switch cleared these out, we need to
264 // restore our version. They will be copied on up to the
265 // next target.
266 assert(!this->args());
267 this->args() <<= args;
268 throw GreenletStartedWhileInPython();
269 }
270 }
271
272 // Sweet, if we got here, we have the go-ahead and will switch
273 // greenlets.
274 // Nothing we do from here on out should allow for a thread or
275 // greenlet switch: No arbitrary calls to Python, including
276 // decref'ing
277
278#if GREENLET_USE_CFRAME
279 /* OK, we need it, we're about to switch greenlets, save the state. */
280 /*
281 See green_new(). This is a stack-allocated variable used
282 while *self* is in PyObject_Call().
283 We want to defer copying the state info until we're sure
284 we need it and are in a stable place to do so.
285 */
286 _PyCFrame trace_info;
287
288 this->python_state.set_new_cframe(trace_info);
289#endif
290 /* start the greenlet */
291 ThreadState& thread_state = GET_THREAD_STATE().state();
292 this->stack_state = StackState(mark,
293 thread_state.borrow_current()->stack_state);
294 this->python_state.set_initial_state(PyThreadState_GET());
295 this->exception_state.clear();
296 this->_main_greenlet = thread_state.get_main_greenlet();
297
298 /* perform the initial switch */
299 switchstack_result_t err = this->g_switchstack();
300 /* returns twice!
301 The 1st time with ``err == 1``: we are in the new greenlet.
302 This one owns a greenlet that used to be current.
303 The 2nd time with ``err <= 0``: back in the caller's
304 greenlet; this happens if the child finishes or switches
305 explicitly to us. Either way, the ``err`` variable is
306 created twice at the same memory location, but possibly
307 having different ``origin`` values. Note that it's not
308 constructed for the second time until the switch actually happens.
309 */
310 if (err.status == 1) {
311 // In the new greenlet.
312
313 // This never returns! Calling inner_bootstrap steals
314 // the contents of our run object within this stack frame, so
315 // it is not valid to do anything with it.
316 try {
317 this->inner_bootstrap(err.origin_greenlet.relinquish_ownership(),
318 run.relinquish_ownership());
319 }
320 // Getting a C++ exception here isn't good. It's probably a
321 // bug in the underlying greenlet, meaning it's probably a
322 // C++ extension. We're going to abort anyway, but try to
323 // display some nice information *if* possible. Some obscure
324 // platforms don't properly support this (old 32-bit Arm, see see
325 // https://github.com/python-greenlet/greenlet/issues/385); that's not
326 // great, but should usually be OK because, as mentioned above, we're
327 // terminating anyway.
328 //
329 // The catching is tested by
330 // ``test_cpp.CPPTests.test_unhandled_exception_in_greenlet_aborts``.
331 //
332 // PyErrOccurred can theoretically be thrown by
333 // inner_bootstrap() -> g_switch_finish(), but that should
334 // never make it back to here. It is a std::exception and
335 // would be caught if it is.
336 catch (const std::exception& e) {
337 std::string base = "greenlet: Unhandled C++ exception: ";
338 base += e.what();
339 Py_FatalError(base.c_str());
340 }
341 catch (...) {
342 // Some compilers/runtimes use exceptions internally.
343 // It appears that GCC on Linux with libstdc++ throws an
344 // exception internally at process shutdown time to unwind
345 // stacks and clean up resources. Depending on exactly
346 // where we are when the process exits, that could result
347 // in an unknown exception getting here. If we
348 // Py_FatalError() or abort() here, we interfere with
349 // orderly process shutdown. Throwing the exception on up
350 // is the right thing to do.
351 //
352 // gevent's ``examples/dns_mass_resolve.py`` demonstrates this.
353#ifndef NDEBUG
354 fprintf(stderr,
355 "greenlet: inner_bootstrap threw unknown exception; "
356 "is the process terminating?\n");
357#endif
358 throw;
359 }
360 Py_FatalError("greenlet: inner_bootstrap returned with no exception.\n");
361 }
362
363
364 // In contrast, notice that we're keeping the origin greenlet
365 // around as an owned reference; we need it to call the trace
366 // function for the switch back into the parent. It was only
367 // captured at the time the switch actually happened, though,
368 // so we haven't been keeping an extra reference around this
369 // whole time.
370
371 /* back in the parent */
372 if (err.status < 0) {
373 /* start failed badly, restore greenlet state */
374 this->stack_state = StackState();
375 this->_main_greenlet.CLEAR();
376 // CAUTION: This may run arbitrary Python code.
377 run.CLEAR(); // inner_bootstrap didn't run, we own the reference.
378 }
379
380 // In the success case, the spawned code (inner_bootstrap) will
381 // take care of decrefing this, so we relinquish ownership so as
382 // to not double-decref.
383
384 run.relinquish_ownership();
385
386 return err;
387}
388
389
390void
391UserGreenlet::inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run)
392{
393 // The arguments here would be another great place for move.
394 // As it is, we take them as a reference so that when we clear
395 // them we clear what's on the stack above us. Do that NOW, and
396 // without using a C++ RAII object,
397 // so there's no way that exiting the parent frame can clear it,
398 // or we clear it unexpectedly. This arises in the context of the
399 // interpreter shutting down. See https://github.com/python-greenlet/greenlet/issues/325
400 //PyObject* run = _run.relinquish_ownership();
401
402 /* in the new greenlet */
403 assert(this->thread_state()->borrow_current() == BorrowedGreenlet(this->_self));
404 // C++ exceptions cannot propagate to the parent greenlet from
405 // here. (TODO: Do we need a catch(...) clause, perhaps on the
406 // function itself? ALl we could do is terminate the program.)
407 // NOTE: On 32-bit Windows, the call chain is extremely
408 // important here in ways that are subtle, having to do with
409 // the depth of the SEH list. The call to restore it MUST NOT
410 // add a new SEH handler to the list, or we'll restore it to
411 // the wrong thing.
412 this->thread_state()->restore_exception_state();
413 /* stack variables from above are no good and also will not unwind! */
414 // EXCEPT: That can't be true, we access run, among others, here.
415
416 this->stack_state.set_active(); /* running */
417
418 // We're about to possibly run Python code again, which
419 // could switch back/away to/from us, so we need to grab the
420 // arguments locally.
421 SwitchingArgs args;
422 args <<= this->args();
423 assert(!this->args());
424
425 // XXX: We could clear this much earlier, right?
426 // Or would that introduce the possibility of running Python
427 // code when we don't want to?
428 // CAUTION: This may run arbitrary Python code.
429 this->_run_callable.CLEAR();
430
431
432 // The first switch we need to manually call the trace
433 // function here instead of in g_switch_finish, because we
434 // never return there.
435 if (OwnedObject tracefunc = this->thread_state()->get_tracefunc()) {
436 OwnedGreenlet trace_origin;
437 trace_origin = origin_greenlet;
438 try {
439 g_calltrace(tracefunc,
440 args ? mod_globs->event_switch : mod_globs->event_throw,
441 trace_origin,
442 this->_self);
443 }
444 catch (const PyErrOccurred&) {
445 /* Turn trace errors into switch throws */
446 args.CLEAR();
447 }
448 }
449
450 // We no longer need the origin, it was only here for
451 // tracing.
452 // We may never actually exit this stack frame so we need
453 // to explicitly clear it.
454 // This could run Python code and switch.
455 Py_CLEAR(origin_greenlet);
456
457 OwnedObject result;
458 if (!args) {
459 /* pending exception */
460 result = NULL;
461 }
462 else {
463 /* call g.run(*args, **kwargs) */
464 // This could result in further switches
465 try {
466 //result = run.PyCall(args.args(), args.kwargs());
467 // CAUTION: Just invoking this, before the function even
468 // runs, may cause memory allocations, which may trigger
469 // GC, which may run arbitrary Python code.
470 result = OwnedObject::consuming(PyObject_Call(run, args.args().borrow(), args.kwargs().borrow()));
471 }
472 catch (...) {
473 // Unhandled C++ exception!
474
475 // If we declare ourselves as noexcept, if we don't catch
476 // this here, most platforms will just abort() the
477 // process. But on 64-bit Windows with older versions of
478 // the C runtime, this can actually corrupt memory and
479 // just return. We see this when compiling with the
480 // Windows 7.0 SDK targeting Windows Server 2008, but not
481 // when using the Appveyor Visual Studio 2019 image. So
482 // this currently only affects Python 2.7 on Windows 64.
483 // That is, the tests pass and the runtime aborts
484 // everywhere else.
485 //
486 // However, if we catch it and try to continue with a
487 // Python error, then all Windows 64 bit platforms corrupt
488 // memory. So all we can do is manually abort, hopefully
489 // with a good error message. (Note that the above was
490 // tested WITHOUT the `/EHr` switch being used at compile
491 // time, so MSVC may have "optimized" out important
492 // checking. Using that switch, we may be in a better
493 // place in terms of memory corruption.) But sometimes it
494 // can't be caught here at all, which is confusing but not
495 // terribly surprising; so again, the G_NOEXCEPT_WIN32
496 // plus "/EHr".
497 //
498 // Hopefully the basic C stdlib is still functional enough
499 // for us to at least print an error.
500 //
501 // It gets more complicated than that, though, on some
502 // platforms, specifically at least Linux/gcc/libstdc++. They use
503 // an exception to unwind the stack when a background
504 // thread exits. (See comments about noexcept.) So this
505 // may not actually represent anything untoward. On those
506 // platforms we allow throws of this to propagate, or
507 // attempt to anyway.
508# if defined(WIN32) || defined(_WIN32)
509 Py_FatalError(
510 "greenlet: Unhandled C++ exception from a greenlet run function. "
511 "Because memory is likely corrupted, terminating process.");
512 std::abort();
513#else
514 throw;
515#endif
516 }
517 }
518 // These lines may run arbitrary code
519 args.CLEAR();
520 Py_CLEAR(run);
521
522 if (!result
523 && mod_globs->PyExc_GreenletExit.PyExceptionMatches()
524 && (this->args())) {
525 // This can happen, for example, if our only reference
526 // goes away after we switch back to the parent.
527 // See test_dealloc_switch_args_not_lost
528 PyErrPieces clear_error;
529 result <<= this->args();
530 result = single_result(result);
531 }
532 this->release_args();
533 this->python_state.did_finish(PyThreadState_GET());
534
535 result = g_handle_exit(result);
536 assert(this->thread_state()->borrow_current() == this->_self);
537
538 /* jump back to parent */
539 this->stack_state.set_inactive(); /* dead */
540
541
542 // TODO: Can we decref some things here? Release our main greenlet
543 // and maybe parent?
544 for (Greenlet* parent = this->_parent;
545 parent;
546 parent = parent->parent()) {
547 // We need to somewhere consume a reference to
548 // the result; in most cases we'll never have control
549 // back in this stack frame again. Calling
550 // green_switch actually adds another reference!
551 // This would probably be clearer with a specific API
552 // to hand results to the parent.
553 parent->args() <<= result;
554 assert(!result);
555 // The parent greenlet now owns the result; in the
556 // typical case we'll never get back here to assign to
557 // result and thus release the reference.
558 try {
559 result = parent->g_switch();
560 }
561 catch (const PyErrOccurred&) {
562 // Ignore, keep passing the error on up.
563 }
564
565 /* Return here means switch to parent failed,
566 * in which case we throw *current* exception
567 * to the next parent in chain.
568 */
569 assert(!result);
570 }
571 /* We ran out of parents, cannot continue */
572 PyErr_WriteUnraisable(this->self().borrow_o());
573 Py_FatalError("greenlet: ran out of parent greenlets while propagating exception; "
574 "cannot continue");
575 std::abort();
576}
577
578void
579UserGreenlet::run(const BorrowedObject nrun)
580{
581 if (this->started()) {
582 throw AttributeError(
583 "run cannot be set "
584 "after the start of the greenlet");
585 }
586 this->_run_callable = nrun;
587}
588
589const OwnedGreenlet
590UserGreenlet::parent() const
591{
592 return this->_parent;
593}
594
595void
596UserGreenlet::parent(const BorrowedObject raw_new_parent)
597{
598 if (!raw_new_parent) {
599 throw AttributeError("can't delete attribute");
600 }
601
602 BorrowedMainGreenlet main_greenlet_of_new_parent;
603 BorrowedGreenlet new_parent(raw_new_parent.borrow()); // could
604 // throw
605 // TypeError!
606 for (BorrowedGreenlet p = new_parent; p; p = p->parent()) {
607 if (p == this->self()) {
608 throw ValueError("cyclic parent chain");
609 }
610 main_greenlet_of_new_parent = p->main_greenlet();
611 }
612
613 if (!main_greenlet_of_new_parent) {
614 throw ValueError("parent must not be garbage collected");
615 }
616
617 if (this->started()
618 && this->_main_greenlet != main_greenlet_of_new_parent) {
619 throw ValueError("parent cannot be on a different thread");
620 }
621
622 this->_parent = new_parent;
623}
624
625void
626UserGreenlet::murder_in_place()
627{
628 this->_main_greenlet.CLEAR();
629 Greenlet::murder_in_place();
630}
631
632bool
633UserGreenlet::belongs_to_thread(const ThreadState* thread_state) const
634{
635 return Greenlet::belongs_to_thread(thread_state) && this->_main_greenlet == thread_state->borrow_main_greenlet();
636}
637
638
639int
640UserGreenlet::tp_traverse(visitproc visit, void* arg)
641{
642 Py_VISIT(this->_parent.borrow_o());
643 Py_VISIT(this->_main_greenlet.borrow_o());
644 Py_VISIT(this->_run_callable.borrow_o());
645
646 return Greenlet::tp_traverse(visit, arg);
647}
648
649int
650UserGreenlet::tp_clear()
651{
652 Greenlet::tp_clear();
653 this->_parent.CLEAR();
654 this->_main_greenlet.CLEAR();
655 this->_run_callable.CLEAR();
656 return 0;
657}
658
659UserGreenlet::ParentIsCurrentGuard::ParentIsCurrentGuard(UserGreenlet* p,
660 const ThreadState& thread_state)
661 : oldparent(p->_parent),
662 greenlet(p)
663{
664 p->_parent = thread_state.get_current();
665}
666
667UserGreenlet::ParentIsCurrentGuard::~ParentIsCurrentGuard()
668{
669 this->greenlet->_parent = oldparent;
670 oldparent.CLEAR();
671}
672
673}; //namespace greenlet
674#endif
675 