ComfyUI-Ranking-API / profile_io.py
ZHIWEI666's picture
Upload 91 files
b9d11ff verified
Raw History Blame Contribute Delete
3.83 kB
"""资料文件I/O在后台线程执行;准入在排队前,已开始工作取消不提前释放容量。"""
import asyncio
import threading
from fastapi import HTTPException
MAX_PROFILE_IO = 8
class ProfileIO:
def __init__(self, capacity=MAX_PROFILE_IO):
self.capacity = threading.BoundedSemaphore(capacity)
self._gate = threading.Lock()
self._accepting = True
self._pending = 0
self._closers = 0
self._drained = threading.Event()
self._drained.set()
def start(self):
"""同进程重新启动仅在上一代所属工作全部终态后开放准入。"""
with self._gate:
if self._pending or self._closers:
raise RuntimeError("profile work is still pending")
self._accepting = True
async def close(self):
"""关闭准入并等真实工作终态;取消关闭等待也不提前宣称排空。"""
with self._gate:
self._accepting = False
self._closers += 1
try:
cancelled = False
while not self._drained.is_set():
try:
# 不向共用线程池提交等待任务,避免池饱和时相互等待。
await asyncio.sleep(0.01)
except asyncio.CancelledError:
cancelled = True
if cancelled:
raise asyncio.CancelledError
finally:
with self._gate:
self._closers -= 1
def _finish(self):
with self._gate:
self.capacity.release()
self._pending -= 1
if not self._pending:
self._drained.set()
async def run(self, function, *args, **kwargs):
with self._gate:
if not self._accepting or not self.capacity.acquire(blocking=False):
raise HTTPException(status_code=503, detail="用户数据暂不可用")
self._pending += 1
self._drained.clear()
# 内部任务也可能在宿主关闭时被取消。锁区分未开始与已开始的工作,
# 保证取消和线程启动竞争时只释放一次,且取消后不再开始业务写入。
ownership = threading.Lock()
started = False
released = False
def release_unstarted():
nonlocal released
with ownership:
if not started and not released:
released = True
self._finish()
def execute():
nonlocal started, released
with ownership:
if released:
return None
started = True
try:
return function(*args, **kwargs)
finally:
with ownership:
released = True
self._finish()
coroutine = asyncio.to_thread(execute)
try:
task = asyncio.create_task(coroutine)
except BaseException:
coroutine.close(); release_unstarted()
raise
def consume(completed):
if completed.cancelled():
release_unstarted()
else:
# 提交默认线程池本身也可能失败,尚未开始的准入必须收回。
release_unstarted()
completed.exception()
task.add_done_callback(consume)
# 外层请求取消不取消所属任务;已开始的工作只在线程终态释放容量。
return await asyncio.shield(task)
_profile_io = ProfileIO()
run_profile_io = _profile_io.run
start_profile_io = _profile_io.start
close_profile_io = _profile_io.close