Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
TUserGreenlet.cpp675 linesDownload Raw Back to greenlet
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 
codekingpro/portable-devtools · Team Ai