[3.6] bpo-34769: Thread safety for _asyncgen_finalizer_hook(). (GH-97… · python/cpython@a14dda5 (original) (raw)

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

`

915

915

`self.loop.run_forever()

`

916

916

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

`

917

917

``

``

918

`+

async def leave_unfinalized_asyncgen(self):

`

``

919

`+

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

`

``

920

`+

to be garbage collected.

`

``

921

`+

Used in async generator finalization tests.

`

``

922

`+

Depends on implementation details of garbage collector. Changes

`

``

923

`+

in gc may break this function.

`

``

924

`+

status = {'started': False,

`

``

925

`+

'stopped': False,

`

``

926

`+

'finalized': False}

`

``

927

+

``

928

`+

async def agen():

`

``

929

`+

status['started'] = True

`

``

930

`+

try:

`

``

931

`+

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

`

``

932

`+

yield item

`

``

933

`+

finally:

`

``

934

`+

status['finalized'] = True

`

``

935

+

``

936

`+

ag = agen()

`

``

937

`+

ai = ag.aiter()

`

``

938

+

``

939

`+

async def iter_one():

`

``

940

`+

try:

`

``

941

`+

item = await ai.anext()

`

``

942

`+

except StopAsyncIteration:

`

``

943

`+

return

`

``

944

`+

if item == 'THREE':

`

``

945

`+

status['stopped'] = True

`

``

946

`+

return

`

``

947

`+

self.loop.create_task(iter_one())

`

``

948

+

``

949

`+

self.loop.create_task(iter_one())

`

``

950

`+

return status

`

``

951

+

``

952

`+

def test_asyncgen_finalization_by_gc(self):

`

``

953

`+

Async generators should be finalized when garbage collected.

`

``

954

`+

self.loop._process_events = mock.Mock()

`

``

955

`+

self.loop._write_to_self = mock.Mock()

`

``

956

`+

with support.disable_gc():

`

``

957

`+

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

`

``

958

`+

while not status['stopped']:

`

``

959

`+

test_utils.run_briefly(self.loop)

`

``

960

`+

self.assertTrue(status['started'])

`

``

961

`+

self.assertTrue(status['stopped'])

`

``

962

`+

self.assertFalse(status['finalized'])

`

``

963

`+

support.gc_collect()

`

``

964

`+

test_utils.run_briefly(self.loop)

`

``

965

`+

self.assertTrue(status['finalized'])

`

``

966

+

``

967

`+

def test_asyncgen_finalization_by_gc_in_other_thread(self):

`

``

968

`+

Python issue 34769: If garbage collector runs in another

`

``

969

`+

thread, async generators will not finalize in debug

`

``

970

`+

mode.

`

``

971

`+

self.loop._process_events = mock.Mock()

`

``

972

`+

self.loop._write_to_self = mock.Mock()

`

``

973

`+

self.loop.set_debug(True)

`

``

974

`+

with support.disable_gc():

`

``

975

`+

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

`

``

976

`+

while not status['stopped']:

`

``

977

`+

test_utils.run_briefly(self.loop)

`

``

978

`+

self.assertTrue(status['started'])

`

``

979

`+

self.assertTrue(status['stopped'])

`

``

980

`+

self.assertFalse(status['finalized'])

`

``

981

`+

self.loop.run_until_complete(

`

``

982

`+

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

`

``

983

`+

test_utils.run_briefly(self.loop)

`

``

984

`+

self.assertTrue(status['finalized'])

`

``

985

+

918

986

``

919

987

`class MyProto(asyncio.Protocol):

`

920

988

`done = None

`