This repository has been archived on 2023-10-03. You can view files and clone it, but cannot push or open issues or pull requests.

96 lines
2.6 KiB
Python
Raw Normal View History

"""Manages the asyncio loop."""
import asyncio
import traceback
import concurrent.futures
import logging
import bpy
log = logging.getLogger(__name__)
def setup_asyncio_executor():
"""Sets up AsyncIO to run on a single thread.
This ensures that only one Pillar HTTP call is performed at the same time. Other
calls that could be performed in parallel are queued, and thus we can
reliably cancel them.
"""
executor = concurrent.futures.ThreadPoolExecutor()
loop = asyncio.get_event_loop()
loop.set_default_executor(executor)
# loop.set_debug(True)
def kick_async_loop(*args):
loop = asyncio.get_event_loop()
# We always need to do one more 'kick' to handle task-done callbacks.
stop_after_this_kick = False
if loop.is_closed():
log.warning('loop closed, stopping immediately.')
stop_async_loop()
return
all_tasks = asyncio.Task.all_tasks()
if not len(all_tasks):
log.debug('no more scheduled tasks, stopping after this kick.')
stop_after_this_kick = True
elif all(task.done() for task in all_tasks):
log.debug('all %i tasks are done, fetching results and stopping after this kick.',
len(all_tasks))
stop_after_this_kick = True
for task_idx, task in enumerate(all_tasks):
if not task.done():
continue
# noinspection PyBroadException
try:
res = task.result()
log.debug(' task #%i: result=%r', task_idx, res)
except asyncio.CancelledError:
# No problem, we want to stop anyway.
log.debug(' task #%i: cancelled', task_idx)
except Exception:
print('{}: resulted in exception'.format(task))
traceback.print_exc()
loop.stop()
loop.run_forever()
if stop_after_this_kick:
stop_async_loop()
def async_loop_handler() -> callable:
"""Returns the asynchronous loop handler `kick_async_loop`
Only returns the function if it is installed as scene_update_pre handler, otherwise
it returns None.
"""
name = kick_async_loop.__name__
for handler in bpy.app.handlers.scene_update_pre:
if getattr(handler, '__name__', '') == name:
return handler
return None
def ensure_async_loop():
if async_loop_handler() is not None:
return
bpy.app.handlers.scene_update_pre.append(kick_async_loop)
def stop_async_loop():
handler = async_loop_handler()
if handler is None:
return
bpy.app.handlers.scene_update_pre.remove(handler)
log.debug('stopped async loop.')