U Wg @sFdZddlmZmZmZddlZGdddejZedkrBe dS)a Tests for greenlets interacting with the CPython trash can API. The CPython trash can API is not designed to be re-entered from a single thread. But this can happen using greenlets, if something during the object deallocation process switches greenlets, and this second greenlet then causes the trash can to get entered again. Here, we do this very explicitly, but in other cases (like gevent) it could be arbitrarily more complicated: for example, a weakref callback might try to acquire a lock that's already held by another greenlet; that would allow a greenlet switch to occur. See https://github.com/gevent/gevent/issues/1909 This test is fragile and relies on details of the CPython implementation (like most of the rest of this package): - We enter the trashcan and deferred deallocation after ``_PyTrash_UNWIND_LEVEL`` calls. This constant, defined in CPython's object.c, is generally 50. That's basically how many objects are required to get us into the deferred deallocation situation. - The test fails by hitting an ``assert()`` in object.c; if the build didn't enable assert, then we don't catch this. - If the test fails in that way, the interpreter crashes. )print_functionabsolute_importdivisionNc@seZdZddZddZdS)TestTrashCanReEntercCshzddlm}Wn<tk rLddl}|jdddks>t|dYnXtdD] }|qVdS)Nrget_tstate_trash_delete_nesting) z1get_tstate_trash_delete_nesting is not available. ) greenlet._greenletr ImportErrorsys version_infoAssertionErrorZskipTestrangecheck_it)selfrr_rR/opt/hc_python/lib64/python3.8/site-packages/greenlet/tests/test_greenlet_trash.pytest_it#s zTestTrashCanReEnter.test_itcsddlddlm}|dks*tddGfdddfddfd d }|j|jd~|d|d_|d|| j d| j | j|jj|jj|jd ddl}|dS) Nrr2icsHeZdZdZdZdZdZdZdZdZ fddZ fddZ dS) z-TestTrashCanReEnter.check_it..DeallocaY An object with a ``__del__`` method. When it starts getting deallocated from a deferred trash can run, it switches greenlets, allocates more objects which then also go in the trash can. If we don't save state appropriately, nesting gets out of order and we can crash the interpreter. FNrcs||_jd7_dS)a :param sequence_number: The ordinal of this object during one particular creation run. This is used to detect (guess, really) when we have entered the trash can's deferred deallocation. N)iCREATED)rZsequence_number)Deallocrr__init__`sz6TestTrashCanReEnter.check_it..Dealloc.__init__c s|jkrB|jsB_}_|}|dks>t~n\|jdkrk rd_zdWn0jk r}zt |_~W5d}~XYnXj d7_ k rƈj d7_ dS)N*(Tr) rSPAWNED getcurrentgreenletBG_GLETswitchrBG_RAN GreenletExittype DESTROYED DESTROYED_BG)rotherxexrTRASH_UNWIND_LEVELbackground_greenletr"mainrr__del__is     z5TestTrashCanReEnter.check_it..Dealloc.__del__) __name__ __module__ __qualname____doc__r r%r#rr(r)rr1rr-rrrBs  rcs }~dS)Nr)t) make_somerrr/sz9TestTrashCanReEnter.check_it..background_greenletcs&d}}|r"||f}|d8}q|S)Nrrr)r6r)rOBJECTS_PER_CONTAINERrrr7s   z/TestTrashCanReEnter.check_it..make_somer)r"r rr!rZ assertEqualrr(r#Z assertGreaterr)ZassertIsr Z assertTruer%r&gcZcollect)rrZsomer9r)rr8r.r/r"r0r7rr2s0 F   zTestTrashCanReEnter.check_itN)r2r3r4rrrrrrr!sr__main__) r5 __future__rrrZunittestZTestCaserr2r0rrrrs