| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | '''
 | 
					
						
							|  |  |  | Complex edge case where during real-time streaming the IPC tranport | 
					
						
							|  |  |  | channels are wiped out (purposely in this example though it could have | 
					
						
							|  |  |  | been an outage) and we want to ensure that despite being in debug mode | 
					
						
							|  |  |  | (or not) the user can sent SIGINT once they notice the hang and the | 
					
						
							|  |  |  | actor tree will eventually be cancelled without leaving any zombies. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | '''
 | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | import trio | 
					
						
							|  |  |  | from tractor import ( | 
					
						
							|  |  |  |     open_nursery, | 
					
						
							|  |  |  |     context, | 
					
						
							|  |  |  |     Context, | 
					
						
							|  |  |  |     MsgStream, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | async def break_channel_silently_then_error( | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |     stream: MsgStream, | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |     async for msg in stream: | 
					
						
							|  |  |  |         await stream.send(msg) | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |         # XXX: close the channel right after an error is raised | 
					
						
							|  |  |  |         # purposely breaking the IPC transport to make sure the parent | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |         # doesn't get stuck in debug or hang on the connection join. | 
					
						
							|  |  |  |         # this more or less simulates an infinite msg-receive hang on | 
					
						
							|  |  |  |         # the other end. | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |         await stream._ctx.chan.send(None) | 
					
						
							|  |  |  |         assert 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | async def close_stream_and_error( | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |     stream: MsgStream, | 
					
						
							|  |  |  | ): | 
					
						
							|  |  |  |     async for msg in stream: | 
					
						
							|  |  |  |         await stream.send(msg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |         # wipe out channel right before raising | 
					
						
							|  |  |  |         await stream._ctx.chan.send(None) | 
					
						
							|  |  |  |         await stream.aclose() | 
					
						
							|  |  |  |         assert 0 | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @context | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | async def recv_and_spawn_net_killers( | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ctx: Context, | 
					
						
							|  |  |  |     **kwargs, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ) -> None: | 
					
						
							|  |  |  |     '''
 | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |     Receive stream msgs and spawn some IPC killers mid-stream. | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     '''
 | 
					
						
							|  |  |  |     await ctx.started() | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |     async with ( | 
					
						
							|  |  |  |         ctx.open_stream() as stream, | 
					
						
							|  |  |  |         trio.open_nursery() as n, | 
					
						
							|  |  |  |     ): | 
					
						
							|  |  |  |         for i in range(100): | 
					
						
							|  |  |  |             await stream.send(i) | 
					
						
							|  |  |  |             if i > 80: | 
					
						
							|  |  |  |                 n.start_soon(break_channel_silently_then_error, stream) | 
					
						
							|  |  |  |                 n.start_soon(close_stream_and_error, stream) | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | async def main( | 
					
						
							| 
									
										
										
										
											2023-01-27 22:02:36 +00:00
										 |  |  |     debug_mode: bool = False, | 
					
						
							|  |  |  |     start_method: str = 'trio', | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | ) -> None: | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     async with open_nursery( | 
					
						
							| 
									
										
										
										
											2023-01-27 22:02:36 +00:00
										 |  |  |         start_method=start_method, | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # NOTE: even debugger is used we shouldn't get | 
					
						
							|  |  |  |         # a hang since it never engages due to broken IPC | 
					
						
							|  |  |  |         debug_mode=debug_mode, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |     ) as n: | 
					
						
							|  |  |  |         portal = await n.start_actor( | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |             'chitty_hijo', | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |             enable_modules=[__name__], | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         async with portal.open_context( | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |             recv_and_spawn_net_killers, | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  |         ) as (ctx, sent): | 
					
						
							|  |  |  |             async with ctx.open_stream() as stream: | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |                 for i in range(100): | 
					
						
							| 
									
										
										
										
											2023-01-27 22:02:36 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-27 22:17:17 +00:00
										 |  |  |                     # it actually breaks right here in the | 
					
						
							|  |  |  |                     # mp_spawn/forkserver backends and thus the zombie | 
					
						
							|  |  |  |                     # reaper never even kicks in? | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |                     await stream.send(i) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     with trio.move_on_after(2) as cs: | 
					
						
							|  |  |  |                         rx = await stream.receive() | 
					
						
							|  |  |  |                         print(f'I a mad user and here is what i got {rx}') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if cs.cancelled_caught: | 
					
						
							|  |  |  |                         # pretend to be a user seeing no streaming action | 
					
						
							|  |  |  |                         # thinking it's a hang, and then hitting ctl-c.. | 
					
						
							| 
									
										
										
										
											2023-01-27 22:17:17 +00:00
										 |  |  |                         print("YOO i'm a user anddd thingz hangin.. CTRL-C..") | 
					
						
							| 
									
										
										
										
											2023-01-27 21:27:25 +00:00
										 |  |  |                         raise KeyboardInterrupt | 
					
						
							| 
									
										
										
										
											2023-01-26 22:48:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     trio.run(main) |