1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
U
¸ý°dã@sNdZddlmZmZmZddlZddlZGdd„dejƒZe    dkrJe 
¡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_functionÚabsolute_importÚdivisionNc@s2eZdZe ejddkd¡dd„ƒZdd„ZdS)    ÚTestTrashCanReEnterrézVPython 2 tracks this slightly differently, so our test doesn't catch a problem there. cCstdƒD] }| ¡qdS)Né
)ÚrangeÚcheck_it)ÚselfÚ_©r úYd:\z\workplace\vscode\pyvenv\venv\Lib\site-packages\greenlet/tests/test_greenlet_trash.pyÚtest_it%s zTestTrashCanReEnter.test_itcsddl‰ddlm}ˆ ¡‰|ƒdks*t‚d‰d‰G‡‡‡‡‡fdd„dƒ‰‡fdd„‰‡‡fd    d
„‰ˆƒ}| ˆjˆ¡| ˆjd¡~| d|ƒ¡dˆ_| d|ƒ¡|     ˆj
d¡|  ˆj ˆ¡|  ˆj¡| ˆjˆj¡| ˆjˆj¡| ˆjˆd ¡ddl}| ¡dS) Nr)Úget_tstate_trash_delete_nestingé2iôcsHeZdZdZdZdZdZdZdZdZ    ‡fdd„Z
‡‡‡‡‡fdd„Z dS)    z-TestTrashCanReEnter.check_it.<locals>.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)ÚiÚCREATED)r
Zsequence_number)ÚDeallocr r Ú__init__^sz6TestTrashCanReEnter.check_it.<locals>.Dealloc.__init__c
sÊ|jˆkrB|jsBˆ ¡ˆ_ˆ ˆ¡}ˆ_| ¡}|dks>t‚~n\|jdkržˆ ¡ˆk    rždˆ_zˆ d¡Wn0ˆjk
rœ}zt    |ƒˆ_~W5d}~XYnXˆj
d7_
ˆ ¡ˆk    rƈj d7_ dS)Né*é(Tr) rÚSPAWNEDÚ
getcurrentÚgreenletÚBG_GLETÚswitchÚAssertionErrorÚBG_RANÚ GreenletExitÚtypeÚ    DESTROYEDÚ DESTROYED_BG)r
ÚotherÚxÚex©rÚTRASH_UNWIND_LEVELÚbackground_greenletrÚmainr r Ú__del__gs 
 
 z5TestTrashCanReEnter.check_it.<locals>.Dealloc.__del__) Ú__name__Ú
__module__Ú __qualname__Ú__doc__rrrrr!r"rr*r r&r r r@s
     rcs ˆƒ}~dS)Nr )Út)Ú    make_somer r r(†sz9TestTrashCanReEnter.check_it.<locals>.background_greenletcs&d}ˆ}|r"ˆ|ƒ|f}|d8}q|S)Nr rr )r/r)rÚOBJECTS_PER_CONTAINERr r r0Žs  
z/TestTrashCanReEnter.check_it.<locals>.make_somer)rZgreenlet._greenletrrrÚ assertEqualrr!rZ assertGreaterr"ZassertIsrÚ
assertTruerrÚgcZcollect)r
rZsomer4r )rr1r'r(rr)r0r r    /s0 F   zTestTrashCanReEnter.check_itN)    r+r,r-ÚunittestZ
skipUnlessÚsysÚ version_inforr    r r r r r#s  þ
rÚ__main__) r.Ú
__future__rrrr6r5ZTestCaserr+r)r r r r Ú<module>s