fix issues with Ctrl-C tasks cancellation

This commit is contained in:
Aleix Conchillo Flaqué
2024-05-17 10:43:52 -07:00
parent f432e2b17e
commit f62fe059b1
3 changed files with 37 additions and 26 deletions

View File

@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added initial interruptions support.
### Fixed
- Fixed issues with Ctrl-C program termination.
## [0.0.16] - 2024-05-16
### Fixed

View File

@@ -38,7 +38,7 @@ class PipelineTask:
self._pipeline = pipeline
self._allow_interruptions = allow_interruptions
self._task_queue = asyncio.Queue()
self._down_queue = asyncio.Queue()
self._up_queue = asyncio.Queue()
self._source = Source(self._up_queue)
@@ -50,15 +50,22 @@ class PipelineTask:
async def cancel(self):
logger.debug(f"Canceling pipeline task {self}")
await self.queue_frame(CancelFrame())
# Make sure everything is cleaned up downstream. This is sent
# out-of-band from the main streaming task which is what we want since
# we want to cancel right away.
await self._source.process_frame(CancelFrame(), FrameDirection.DOWNSTREAM)
self._process_down_task.cancel()
self._process_up_task.cancel()
async def run(self):
await asyncio.gather(self._process_task_queue(), self._process_up_queue())
self._process_up_task = asyncio.create_task(self._process_up_queue())
self._process_down_task = asyncio.create_task(self._process_down_queue())
await asyncio.gather(self._process_up_task, self._process_down_task)
await self._source.cleanup()
await self._pipeline.cleanup()
async def queue_frame(self, frame: Frame):
await self._task_queue.put(frame)
await self._down_queue.put(frame)
async def queue_frames(self, frames: Iterable[Frame] | AsyncIterable[Frame]):
if isinstance(frames, AsyncIterable):
@@ -70,30 +77,31 @@ class PipelineTask:
else:
raise Exception("Frames must be an iterable or async iterable")
async def _process_task_queue(self):
async def _process_down_queue(self):
await self._source.process_frame(
StartFrame(allow_interruptions=self._allow_interruptions), FrameDirection.DOWNSTREAM)
running = True
while running:
frame = await self._task_queue.get()
await self._source.process_frame(frame, FrameDirection.DOWNSTREAM)
self._task_queue.task_done()
running = not (isinstance(frame, StopTaskFrame) or
isinstance(frame, CancelFrame) or
isinstance(frame, EndFrame))
# We just enqueue None to terminate the task.
await self._up_queue.put(None)
try:
frame = await self._down_queue.get()
await self._source.process_frame(frame, FrameDirection.DOWNSTREAM)
self._down_queue.task_done()
running = not (isinstance(frame, StopTaskFrame) or isinstance(frame, EndFrame))
except asyncio.CancelledError:
break
# We just enqueue None to terminate the task gracefully.
self._process_up_task.cancel()
async def _process_up_queue(self):
running = True
while running:
frame = await self._up_queue.get()
if frame:
while True:
try:
frame = await self._up_queue.get()
if isinstance(frame, ErrorFrame):
logger.error(f"Error running app: {frame.error}")
await self.queue_frame(CancelFrame())
self._up_queue.task_done()
running = frame is not None
self._up_queue.task_done()
except asyncio.CancelledError:
break
def __str__(self):
return self.name

View File

@@ -65,8 +65,7 @@ class BaseInputTransport(FrameProcessor):
await self._audio_in_thread
await self._audio_out_thread
await self._internal_push_frame(None, None)
await self._push_frame_task
self._push_frame_task.cancel()
def vad_analyze(self, audio_frames: bytes) -> VADState:
pass
@@ -112,12 +111,12 @@ class BaseInputTransport(FrameProcessor):
await self._push_queue.put((frame, direction))
async def _push_frame_task_handler(self):
running = True
while running:
(frame, direction) = await self._push_queue.get()
if frame:
while True:
try:
(frame, direction) = await self._push_queue.get()
await self.push_frame(frame, direction)
running = frame is not None
except asyncio.CancelledError:
break
#
# Handle interruptions