bpo-34769: Thread safety for _asyncgen_finalizer_hook(). (GH-9716) · python/cpython@41e5ec3 (original) (raw)

`@@ -907,6 +907,74 @@ def test_run_forever_pre_stopped(self):

`

907

907

`self.loop.run_forever()

`

908

908

`self.loop._selector.select.assert_called_once_with(0)

`

909

909

``

``

910

`+

async def leave_unfinalized_asyncgen(self):

`

``

911

`+

Create an async generator, iterate it partially, and leave it

`

``

912

`+

to be garbage collected.

`

``

913

`+

Used in async generator finalization tests.

`

``

914

`+

Depends on implementation details of garbage collector. Changes

`

``

915

`+

in gc may break this function.

`

``

916

`+

status = {'started': False,

`

``

917

`+

'stopped': False,

`

``

918

`+

'finalized': False}

`

``

919

+

``

920

`+

async def agen():

`

``

921

`+

status['started'] = True

`

``

922

`+

try:

`

``

923

`+

for item in ['ZERO', 'ONE', 'TWO', 'THREE', 'FOUR']:

`

``

924

`+

yield item

`

``

925

`+

finally:

`

``

926

`+

status['finalized'] = True

`

``

927

+

``

928

`+

ag = agen()

`

``

929

`+

ai = ag.aiter()

`

``

930

+

``

931

`+

async def iter_one():

`

``

932

`+

try:

`

``

933

`+

item = await ai.anext()

`

``

934

`+

except StopAsyncIteration:

`

``

935

`+

return

`

``

936

`+

if item == 'THREE':

`

``

937

`+

status['stopped'] = True

`

``

938

`+

return

`

``

939

`+

asyncio.create_task(iter_one())

`

``

940

+

``

941

`+

asyncio.create_task(iter_one())

`

``

942

`+

return status

`

``

943

+

``

944

`+

def test_asyncgen_finalization_by_gc(self):

`

``

945

`+

Async generators should be finalized when garbage collected.

`

``

946

`+

self.loop._process_events = mock.Mock()

`

``

947

`+

self.loop._write_to_self = mock.Mock()

`

``

948

`+

with support.disable_gc():

`

``

949

`+

status = self.loop.run_until_complete(self.leave_unfinalized_asyncgen())

`

``

950

`+

while not status['stopped']:

`

``

951

`+

test_utils.run_briefly(self.loop)

`

``

952

`+

self.assertTrue(status['started'])

`

``

953

`+

self.assertTrue(status['stopped'])

`

``

954

`+

self.assertFalse(status['finalized'])

`

``

955

`+

support.gc_collect()

`

``

956

`+

test_utils.run_briefly(self.loop)

`

``

957

`+

self.assertTrue(status['finalized'])

`

``

958

+

``

959

`+

def test_asyncgen_finalization_by_gc_in_other_thread(self):

`

``

960

`+

Python issue 34769: If garbage collector runs in another

`

``

961

`+

thread, async generators will not finalize in debug

`

``

962

`+

mode.

`

``

963

`+

self.loop._process_events = mock.Mock()

`

``

964

`+

self.loop._write_to_self = mock.Mock()

`

``

965

`+

self.loop.set_debug(True)

`

``

966

`+

with support.disable_gc():

`

``

967

`+

status = self.loop.run_until_complete(self.leave_unfinalized_asyncgen())

`

``

968

`+

while not status['stopped']:

`

``

969

`+

test_utils.run_briefly(self.loop)

`

``

970

`+

self.assertTrue(status['started'])

`

``

971

`+

self.assertTrue(status['stopped'])

`

``

972

`+

self.assertFalse(status['finalized'])

`

``

973

`+

self.loop.run_until_complete(

`

``

974

`+

self.loop.run_in_executor(None, support.gc_collect))

`

``

975

`+

test_utils.run_briefly(self.loop)

`

``

976

`+

self.assertTrue(status['finalized'])

`

``

977

+

910

978

``

911

979

`class MyProto(asyncio.Protocol):

`

912

980

`done = None

`