await future 到底在等谁
前面已经用 Task 管理协程;这一节向下一层看 Future。Future 保存一个尚未完成的结果,回调或其他生产者把结果写进去,等待者才能继续。它本身不会启动一个 worker。
保留这个最小实验:
import asyncio
async def wait():
loop = asyncio.get_running_loop()
future = loop.create_future()
loop.call_soon(future.set_result, 7)
return await future
async def exercise():
print("value", await wait())
if __name__ == "__main__":
asyncio.run(exercise())
下载 example.py,执行 python3 -W error example.py,输出仍是 value 7。本章使用 Python 标准库;推荐 Python 3.12,最低 Python 3.11。
这里先取当前线程正在运行的 loop,再由 loop.create_future() 创建属于它的 Future。call_soon 登记普通回调;当当前执行让出控制权,loop 才有机会调用 future.set_result(7),随后等待者从 await future 取到 7。
回到 Slime:分批完成 Future
阅读主线是“请求进入 AsyncPacer.wait() → 排队等待 → _release() 完成结果 → 请求继续”。这里第一次完整展开 AsyncPacer,不依赖第 2 节存在它的示例。
wait() 把 Future 加入 pending;第一次入队时用 loop.call_soon(self._release) 安排释放。_release() 最多取出 64 个,检查 future.done(),对尚未结束的 Future 调用 set_result(None)。剩余队列通过 loop.call_later(0.001, self._release) 等下一批;这个延迟给 socket 和其他计时回调留下运行机会。它没有等待上一批业务结束,因此控制的是唤醒节奏,并非第 6 节 Semaphore 的在途任务上限。
| 操作 | 输入、结果与约束 |
|---|---|
loop.call_soon(callback, *args) | 在后续一轮执行普通回调;当前 loop 线程内使用,不接收一个需要自动 await 的协程函数 |
loop.call_later(delay, callback, *args) | 延迟至少接近指定秒数再安排回调;依赖单调时钟,不承诺精确执行时间 |
Future.done() | 成功、失败或取消都算完成,不能据此断言成功 |
Future.set_result(value) | 成功完成;已完成的 Future 再写会抛 asyncio.InvalidStateError |
Future.set_exception(error) | 失败完成;等待者在 await 处收到异常,需要读取和处理 |
Future.cancel() | 把尚未完成的 Future 标成取消,唤醒等待者;已结束时不能重新取消 |
Future.add_done_callback(fn) | 完成后由 loop 调用 fn(future);已完成也会排入回调,不在注册语句中直接内联执行 |
_flush_rebuffers() 给出失败分支:后台 add_samples 成功就 future.set_result(None),异常就 future.set_exception(error)。Future 连接了“批处理生产者”与“各请求等待者”;它没有替生产者执行写入。
展开固定源码:排队、分批唤醒与失败完成
AsyncPacer.wait / _releaseslime/utils/async_utils.py · L24–L42
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
async def wait(self):
loop = asyncio.get_running_loop()
future = loop.create_future()
self.pending.append(future)
if not self.scheduled:
self.scheduled = True
loop.call_soon(self._release)
await future
def _release(self):
for _ in range(min(64, len(self.pending))):
future = self.pending.popleft()
if not future.done():
future.set_result(None)
if self.pending:
# Let socket callbacks and timers run before waking more tasks.
asyncio.get_running_loop().call_later(0.001, self._release)
else:
self.scheduled = False_rebuffer / _flush_rebuffersslime/rollout/fully_async_distributed.py · L397–L422
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
async def _rebuffer(self, group):
if not hasattr(self, "_pending_rebuffers"):
self._pending_rebuffers = deque()
self._rebuffer_task = None
future = asyncio.get_running_loop().create_future()
self._pending_rebuffers.append((group, future))
if self._rebuffer_task is None:
self._rebuffer_task = asyncio.create_task(self._flush_rebuffers())
await asyncio.shield(future)
async def _flush_rebuffers(self):
try:
await asyncio.sleep(0.01)
while self._pending_rebuffers:
batch = [self._pending_rebuffers.popleft() for _ in range(min(64, len(self._pending_rebuffers)))]
try:
await asyncio.to_thread(self.data_source.add_samples, [group for group, _ in batch])
except Exception as error:
for _, future in [*batch, *self._pending_rebuffers]:
future.set_exception(error)
self._pending_rebuffers.clear()
return
for _, future in batch:
future.set_result(None)
finally:
self._rebuffer_task = None谁创建 loop,谁让它运行
现在沿 get_async_loop() → AsyncLoopThread → _start_loop() → run(coro) 进入后台线程。输入是调用方要执行的协程,输出是它的结果或异常。
- 构造函数用
asyncio.new_event_loop()创建对象,此时 loop 尚未运行。 - 新线程中的
asyncio.set_event_loop(self.loop)把它登记为该线程的当前 loop;登记也不等于启动。 loop.run_forever()驱动 Task、I/O 和回调,持续运行到收到loop.stop()。- 调用方用
asyncio.run_coroutine_threadsafe(coro, loop)提交,得到concurrent.futures.Future。这里的同步.result()阻塞调用方线程;目标 loop 必须还能运行。
run_app_in_thread._run() 是另一种真实顺序:先两次 loop.run_until_complete(...) 完成 runner setup 和监听启动,再进入 run_forever()。前者运行到指定等待对象完成并返回结果,后者没有预定业务完成点。不能在已经运行的同一个 loop 中嵌套调用这些运行入口。
_GenerationActor.start() 已经在异步调用方内,于是用 await asyncio.wrap_future(future) 等待跨线程提交结果。不要在目标 loop 的线程里同步 .result();目标线程一旦被堵住,要等待的工作也失去推进机会。
展开固定源码:创建、启动和跨线程等待
AsyncLoopThreadslime/utils/async_utils.py · L46–L58
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
class AsyncLoopThread:
def __init__(self):
self.loop = asyncio.new_event_loop()
self._thread = threading.Thread(target=self._start_loop, daemon=True)
self._thread.start()
def _start_loop(self):
asyncio.set_event_loop(self.loop)
self.loop.run_forever()
def run(self, coro):
# Schedule a coroutine onto the loop and block until it's done
return asyncio.run_coroutine_threadsafe(coro, self.loop).result()run_app_in_thread._runslime/agent/aiohttp_threaded.py · L65–L85
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
def _run() -> None:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
runner = web.AppRunner(app, **(runner_kwargs or {}))
loop.run_until_complete(runner.setup())
site = web.TCPSite(runner, host, port)
loop.run_until_complete(site.start())
actual_port = port
for sock in site._server.sockets: # type: ignore[attr-defined]
actual_port = sock.getsockname()[1]
break
box["loop"] = loop
box["runner"] = runner
box["port"] = actual_port
started.set()
loop.run_forever()
except BaseException as e: # pragma: no cover
err_box.append(e)
started.set()
raise_GenerationActor.startslime/rollout/fully_async_distributed.py · L279–L281
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
async def start(self, rollout_id):
future = asyncio.run_coroutine_threadsafe(self._start(rollout_id), get_async_loop().loop)
await asyncio.wrap_future(future)先完成业务收尾,再 stop 和 close
AppHandle.stop() 先提交 runner.cleanup() 并等待,再用 loop.call_soon_threadsafe(loop.stop) 让目标线程执行停止。call_soon_threadsafe 接收普通回调,供其他线程安全入队;不要用非线程安全的 call_soon 代替。已关闭的 loop 不能继续接受提交。
| 操作 | 负责什么 | 不负责什么 |
|---|---|---|
loop.is_running() | 观察 loop 是否正在运行 | 不证明任意业务已完成,也不是跨线程关闭协议 |
loop.stop() | 当前批次回调跑完后退出运行入口 | 不取消所有 Task、不关闭业务连接;当前回调新增的回调可能留待再次运行 |
await loop.shutdown_default_executor() | 等默认 executor 的线程工作结束 | 不等于关闭整个 loop;关闭后不能继续使用它的默认 executor |
loop.close() | 释放 loop 持有的资源,丢弃剩余回调 | 必须先停止运行;不能代替等待任务完成,关闭不可逆 |
_GenerationActor._close() 先取消并等待 producer,再等 _rebuffer_task,最后 await ...shutdown_default_executor(),随后才关闭 data_source。理由就在源码注释:第 8 节见过,取消 to_thread 的等待者不代表同步写入线程已经结束;先关闭文件可能撞上仍在执行的写入。Python 3.12 给 shutdown_default_executor 增加了 timeout 参数;示例不依赖此参数,因此仍能在 3.11 运行。asyncio.run() 自己管理的 loop 会自动关闭默认 executor,不应重复调用这段手动流程。
原版 AsyncLoopThread 只有创建和运行,AppHandle.stop() 也没有 loop.close();不能把下面教学版的完整关闭写成 Slime 已实现的行为。
展开固定源码:跨线程停止和关闭前的 executor 排空
AppHandle.stopslime/agent/aiohttp_threaded.py · L33–L43
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
def stop(self) -> None:
async def _shutdown() -> None:
await self.runner.cleanup()
try:
fut = asyncio.run_coroutine_threadsafe(_shutdown(), self.loop)
fut.result(timeout=10)
except Exception:
pass
self.loop.call_soon_threadsafe(self.loop.stop)
self.thread.join(timeout=5)_GenerationActor.close / _closeslime/rollout/fully_async_distributed.py · L434–L448
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
async def close(self):
future = asyncio.run_coroutine_threadsafe(self._close(), get_async_loop().loop)
await asyncio.wrap_future(future)
async def _close(self):
self.running = False
if self.producer is not None:
self.producer.cancel()
await asyncio.gather(self.producer, return_exceptions=True)
if getattr(self, "_rebuffer_task", None) is not None:
await self._rebuffer_task
# to_thread writes can outlive cancelled awaiters. Drain them before
# closing their files or killing this process.
await asyncio.get_running_loop().shutdown_default_executor()
self.data_source.close()任务身份、所属 loop 与完成后的读取
再回到业务主线:BaseAdapter._run_turn() 通过 asyncio.current_task() 取得承载本次请求的 Task,并登记进 session 的在途集合。清理 session 时先筛掉 done() 的任务,再从 tasks[0].get_loop() 取回它们实际所属的 loop,把 _drain() 提交过去。这是执行归属问题,不是全局寻找“任意可用 loop”。
asyncio.current_task() 默认查询当前运行 loop;普通 loop 回调未必处于 Task 内,因此可能返回 None。asyncio.all_tasks() 默认返回当前 loop 的尚未完成任务集合,包含调用它的当前任务;它不是全进程、全线程任务清单。Slime 的 _log_timeout_diagnostic() 使用它输出超时现场,没有用诊断操作来取消所有任务。源码再筛 not t.done() 是防御式检查。
asyncio.Task 方法 | 何时用、会发生什么 |
|---|---|
done() | 判断协程已返回、抛错或结束取消;它本身不会等待 |
result() | 仅在完成后读取;成功返回值,失败重抛异常,已取消抛 CancelledError,未完成抛 InvalidStateError |
exception() | 完成后读取失败对象;成功为 None,已取消仍抛 CancelledError,未完成仍抛 InvalidStateError |
cancel() | 请求在协程下一次恢复时注入取消;返回并不保证任务已结束,随后仍须等待清理 |
cancelled() | 判断 Task 是否最终以取消结束;协程若抑制取消可能为假 |
get_loop() | 取所属 loop;不能借此把 Task 任意移动到另一个 loop |
add_done_callback(fn) | 注册 fn(task);供框架完成收集和记账,回调内可安全读取完成结果,也仍须处理失败与取消 |
Task 从 Future 继承许多读取接口,但不允许外部 set_result / set_exception 改写它的执行结果。与 concurrent.futures.Future.result(timeout=...) 不同,asyncio.Task.result() 和 asyncio.Future.result() 不会阻塞等到完成。
AsyncRolloutWorker._loop() 创建任务后注册 _make_done_cb(gid);回调读 done_task.result(),分别处理取消、异常和正常结果。主循环另用 done() 收割已结束任务。先前第 5 节的“取消后等待”在这里变成了有明确归属和错误分支的收尾代码。
展开固定源码:身份登记、跨 loop 排空、诊断与回调
BaseAdapter._run_turnslime/agent/adapters/common.py · L335–L344
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
tok = self.tokenizer
s = self.store.setdefault(sid, Session())
task = asyncio.current_task()
self.inflight.setdefault(sid, set()).add(task)
t0 = time.monotonic()
try:
translated, tools_schema = self._translate(body)
prompt_ids = _render_token_ids(translated, tok, tools=tools_schema, add_generation_prompt=True)
turn = await call_sglang_generate(prompt_ids, s, body, adapter=self, session_id=sid)BaseAdapter.shutdown_sessionslime/agent/adapters/common.py · L225–L243
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
async def shutdown_session(self, sid: str, *, wait_timeout: float = 5.0) -> None:
"""Mark a sid closed and drain its in-flight turn tasks."""
self.closed.add(sid)
tasks = [t for t in self.inflight.pop(sid, ()) if not t.done()]
if not tasks:
return
async def _drain() -> None:
_, pending = await asyncio.wait(tasks, timeout=wait_timeout)
for task in pending:
task.cancel()
if pending:
await asyncio.gather(*pending, return_exceptions=True)
loop = tasks[0].get_loop()
try:
await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(_drain(), loop))
except Exception:
self.logger.exception("[%s] sid=%s shutdown drain failed", self.log_prefix, sid)_log_timeout_diagnosticexamples/coding_agent_rl/generate.py · L286–L305
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
def _log_timeout_diagnostic(t0: float, instance_id: str) -> None:
# Dump pending-task names when the wall-clock guard fires. Must not crash.
try:
elapsed = time.time() - t0
pending = [t for t in asyncio.all_tasks() if not t.done()]
stuck = []
for t in pending[:5]: # cap to avoid log spam
coro = getattr(t, "_coro", None)
stuck.append(getattr(coro, "__qualname__", repr(coro)))
logger.warning(
"[coding_agent_rl] %s: wall_clock_timeout after %.1fs "
"(guard=%ds); %d tasks pending; sample of stuck: %s",
instance_id,
elapsed,
CONFIG.rollout_guard_sec,
len(pending),
stuck,
)
except Exception: # pragma: no cover - diag must never crash
passAsyncRolloutWorker._loop completion harvestingslime/rollout/fully_async_rollout.py · L173–L192
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
async def _loop(self) -> None:
active_tasks: set[asyncio.Task] = set()
max_concurrent = self.concurrency
gid_counter = 0
while self.running:
try:
# Reap done tasks
if active_tasks:
done = {t for t in active_tasks if t.done()}
for t in done:
try:
t.result() # results already handled in callback
except Exception as e: # noqa: BLE001
logger.warning("fully-async task crashed: %r", e)
active_tasks -= done
if done:
# Done callbacks requeue ABORTED groups. Let them run
# before asking the data source for replacement work.
await asyncio.sleep(0)AsyncRolloutWorker._loop callback registrationslime/rollout/fully_async_rollout.py · L203–L218
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
groups = self.data_buffer.get_samples(1)
if not groups:
break
for group in groups:
gid = gid_counter
gid_counter += 1
task = asyncio.create_task(
generate_and_rm_group(
self.args,
group,
sampling_params=self.state.sampling_params.copy(),
evaluation=False,
)
)
task.add_done_callback(self._make_done_cb(gid))
active_tasks.add(task)AsyncRolloutWorker._make_done_cbslime/rollout/fully_async_rollout.py · L241–L265
快照 2f2318653f6f794dddd321eff7c9d4b7b174643f· 查看固定版本源码
def _make_done_cb(self, gid: int):
def _cb(done_task: asyncio.Task) -> None:
try:
result = done_task.result()
except asyncio.CancelledError:
return
except Exception as error:
self.error = error
return
if not isinstance(result, list):
logger.warning(
"fully-async: generate_and_rm_group returned %r, expected list[Sample]; dropping",
type(result).__name__,
)
return
# Aborted group → requeue, don't ship to training.
if any(getattr(s, "status", None) == Sample.Status.ABORTED for s in result):
try:
self.data_buffer.add_samples([result])
except Exception: # noqa: BLE001
logger.exception("fully-async: failed to requeue aborted group")
return
self.output_queue.put((gid, result))
return _cb第二个实验:观察完整生命周期
下载 lifecycle.py,执行 python3 -W error lifecycle.py。它保留真实类名、入口和跨线程提交;教学扩展只增加显式关闭,在 loop 所在线程的 finally 中用 run_until_complete 排空异步生成器与默认 executor,再 close。shutdown_asyncgens() 是手动 loop 的兜底收尾;业务仍应自己及时关闭生成器。
展开完整实验:lifecycle.py
"""Teaching extension of Slime's AsyncLoopThread; stdlib only."""
import asyncio
import threading
class AsyncLoopThread:
def __init__(self):
self.loop = asyncio.new_event_loop()
self._thread = threading.Thread(target=self._start_loop, daemon=True)
self._thread.start()
def _start_loop(self):
asyncio.set_event_loop(self.loop)
try:
self.loop.run_forever()
finally:
# Teaching addition: Slime's original class has no close method.
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
self.loop.run_until_complete(self.loop.shutdown_default_executor())
self.loop.close()
asyncio.set_event_loop(None)
def run(self, coro):
return asyncio.run_coroutine_threadsafe(coro, self.loop).result()
def close(self):
self.loop.call_soon_threadsafe(self.loop.stop)
self._thread.join()
async def exercise():
loop = asyncio.get_running_loop()
current = asyncio.current_task()
print('loop running', loop.is_running())
print('current registered', current in asyncio.all_tasks())
completed = []
future = loop.create_future()
future.add_done_callback(lambda done: completed.append(done.result()))
loop.call_soon(future.set_result, 7)
print('future result', await future)
print('future callback', completed)
failed = loop.create_future()
loop.call_later(0, failed.set_exception, ValueError('bad value'))
try:
await failed
except ValueError as error:
print('future error', str(error))
cancelled = loop.create_future()
cancelled.cancel()
print('future cancelled', cancelled.cancelled(), cancelled.done())
task = asyncio.create_task(asyncio.sleep(0, result=9))
await task
print('task state', task.done(), task.result(), task.exception())
print('task loop', task.get_loop() is loop)
pending = asyncio.create_task(asyncio.sleep(60))
pending.cancel()
try:
await pending
except asyncio.CancelledError:
print('task cancelled', pending.cancelled(), pending.done())
if __name__ == '__main__':
worker = AsyncLoopThread()
try:
worker.run(exercise())
finally:
worker.close()
print('thread stopped', not worker._thread.is_alive())
loop running True
current registered True
future result 7
future callback [7]
future error bad value
future cancelled True True
task state True 9 None
task loop True
task cancelled True True
thread stopped True
阅读实验时按三段定位:exercise() 完成 Future/Task 演示 → 主线程请求 stop → 目标线程完成清理并退出。真实服务如果仍有业务任务,必须先取消或等完它们,再走最后两步;这个实验在 run(exercise()) 返回时已没有遗留业务任务。
语言依据与版本边界
书上怎么说:await 的对象必须提供可等待行为;Future 属于低层等待对象。
我的判断(本书未覆盖):这里的生产者/等待者交接与 Slime 对照来自上述固定源码;loop 关闭、Task 方法和 Python 3.12 的细节按官方文档核对。
依据 A:《Fluent Python, 2nd Edition》(2022),第 21 章 New Concept: Awaitable。英文 corpus/en/ch21.md:203–228,中文 corpus/zh/ch21.md:166–187,同一锚点 new-concept-awaitable。本书基于 Python 3.10;本次验证解释器 Python 3.12.3。
原文:“The for keyword works with iterables. The await keyword works with awaitables.”
对照:“for 关键字处理可迭代对象,await 关键字处理可异步调用对象。”
语义核对:事件循环生命周期与回调、Future、Task 与跨线程提交。
本节对应的全部源码位置
下面把同类用法在 Slime 中的每个扫描位置接回本节讲解;点击文件名可查看完整源码快照。重复出现的 API 共用解释,具体输入与清理由所在函数决定。
展开本节 59 条源码记录
| 记录 / 讲解 | 源码位置 | 原代码 |
|---|---|---|
| occ-000032 | examples/coding_agent_rl/generate.py:290 _log_timeout_diagnostic | pending = [t for t in asyncio.all_tasks() if not t.done()] |
| occ-000374 | slime/agent/adapters/common.py:241 BaseAdapter.shutdown_session | await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(_drain(), loop)) |
| occ-000375 | slime/agent/adapters/common.py:241 BaseAdapter.shutdown_session | await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(_drain(), loop)) |
| occ-000385 | slime/agent/adapters/common.py:337 BaseAdapter._run_turn | task = asyncio.current_task() |
| occ-000470 | slime/agent/aiohttp_threaded.py:30 AppHandle | loop: asyncio.AbstractEventLoop |
| occ-000473 | slime/agent/aiohttp_threaded.py:38 AppHandle.stop | fut = asyncio.run_coroutine_threadsafe(_shutdown(), self.loop) |
| occ-000478 | slime/agent/aiohttp_threaded.py:66 run_app_in_thread._run | loop = asyncio.new_event_loop() |
| occ-000479 | slime/agent/aiohttp_threaded.py:67 run_app_in_thread._run | asyncio.set_event_loop(loop) |
| occ-000480 有接收者线索 | slime/agent/aiohttp_threaded.py:70 run_app_in_thread._run | loop.run_until_complete(runner.setup()) |
| occ-000481 有接收者线索 | slime/agent/aiohttp_threaded.py:72 run_app_in_thread._run | loop.run_until_complete(site.start()) |
| occ-000484 有接收者线索 | slime/agent/aiohttp_threaded.py:81 run_app_in_thread._run | loop.run_forever() |
| occ-000613 | slime/backends/megatron_utils/server/megatron_server.py:464 _build_http_app.update_from_disk | update_future = asyncio.get_running_loop().create_future() |
| occ-000614 有接收者线索 | slime/backends/megatron_utils/server/megatron_server.py:464 _build_http_app.update_from_disk | update_future = asyncio.get_running_loop().create_future() |
| occ-000625 有接收者线索 | slime/backends/megatron_utils/server/megatron_server.py:506 _build_http_app.update_from_disk | update_future.set_result(result) |
| occ-000626 有接收者线索 | slime/backends/megatron_utils/server/megatron_server.py:508 _build_http_app.update_from_disk | update_future.set_result(error) |
| occ-000701 | slime/backends/sglang_utils/server_control.py:176 abort_server_until_idle | loop = asyncio.get_running_loop() |
| occ-000810 | slime/data/queue_data_source.py:1115 QueueReader.get_samples_async | return await asyncio.get_running_loop().run_in_executor(self._reader, self.get_samples, num_samples) |
| occ-000835 | slime/data/transport.py:372 run_rollout_io | loop = asyncio.get_running_loop() |
| occ-000844 有接收者线索 | slime/data/transport.py:383 run_rollout_io | while not pending.done(): |
| occ-000848 有接收者线索 | slime/data/transport.py:388 run_rollout_io | pending.result() |
| occ-000936 | slime/rollout/fully_async_distributed.py:280 _GenerationActor.start | future = asyncio.run_coroutine_threadsafe(self._start(rollout_id), get_async_loop().loop) |
| occ-000938 | slime/rollout/fully_async_distributed.py:281 _GenerationActor.start | await asyncio.wrap_future(future) |
| occ-000945 | slime/rollout/fully_async_distributed.py:295 _GenerationActor.pause | future = asyncio.run_coroutine_threadsafe(self._pause(), get_async_loop().loop) |
| occ-000947 | slime/rollout/fully_async_distributed.py:296 _GenerationActor.pause | await asyncio.wrap_future(future) |
| occ-000950 | slime/rollout/fully_async_distributed.py:302 _GenerationActor.next | future = asyncio.run_coroutine_threadsafe(self._next(), get_async_loop().loop) |
| occ-000952 | slime/rollout/fully_async_distributed.py:303 _GenerationActor.next | return await asyncio.wrap_future(future) |
| occ-000959 有接收者线索 | slime/rollout/fully_async_distributed.py:309 _GenerationActor._next | if take.done(): |
| occ-000960 有接收者线索 | slime/rollout/fully_async_distributed.py:310 _GenerationActor._next | return take.result() |
| occ-000961 有接收者线索 | slime/rollout/fully_async_distributed.py:311 _GenerationActor._next | self.producer.result() # Propagate custom generation errors. |
| occ-000964 有接收者线索 | slime/rollout/fully_async_distributed.py:316 _GenerationActor._next | take.cancel() |
| occ-000993 | slime/rollout/fully_async_distributed.py:401 _GenerationActor._rebuffer | future = asyncio.get_running_loop().create_future() |
| occ-000994 有接收者线索 | slime/rollout/fully_async_distributed.py:401 _GenerationActor._rebuffer | future = asyncio.get_running_loop().create_future() |
| occ-001008 | slime/rollout/fully_async_distributed.py:435 _GenerationActor.close | future = asyncio.run_coroutine_threadsafe(self._close(), get_async_loop().loop) |
| occ-001010 | slime/rollout/fully_async_distributed.py:436 _GenerationActor.close | await asyncio.wrap_future(future) |
| occ-001012 有接收者线索 | slime/rollout/fully_async_distributed.py:441 _GenerationActor._close | self.producer.cancel() |
| occ-001017 | slime/rollout/fully_async_distributed.py:447 _GenerationActor._close | await asyncio.get_running_loop().shutdown_default_executor() |
| occ-001018 有接收者线索 | slime/rollout/fully_async_distributed.py:447 _GenerationActor._close | await asyncio.get_running_loop().shutdown_default_executor() |
| occ-001051 有接收者线索 | slime/rollout/fully_async_rollout.py:217 AsyncRolloutWorker._loop | task.add_done_callback(self._make_done_cb(gid)) |
| occ-001125 | slime/rollout/sglang_rollout.py:263 _run_request_abortable_generate | task = asyncio.current_task() |
| occ-001199 | slime/utils/async_utils.py:25 AsyncPacer.wait | loop = asyncio.get_running_loop() |
| occ-001200 有接收者线索 | slime/utils/async_utils.py:26 AsyncPacer.wait | future = loop.create_future() |
| occ-001201 有接收者线索 | slime/utils/async_utils.py:30 AsyncPacer.wait | loop.call_soon(self._release) |
| occ-001205 | slime/utils/async_utils.py:40 AsyncPacer._release | asyncio.get_running_loop().call_later(0.001, self._release) |
| occ-001206 有接收者线索 | slime/utils/async_utils.py:40 AsyncPacer._release | asyncio.get_running_loop().call_later(0.001, self._release) |
| occ-001207 | slime/utils/async_utils.py:48 AsyncLoopThread.__init__ | self.loop = asyncio.new_event_loop() |
| occ-001210 | slime/utils/async_utils.py:53 AsyncLoopThread._start_loop | asyncio.set_event_loop(self.loop) |
| occ-001211 有接收者线索 | slime/utils/async_utils.py:54 AsyncLoopThread._start_loop | self.loop.run_forever() |
| occ-001212 | slime/utils/async_utils.py:58 AsyncLoopThread.run | return asyncio.run_coroutine_threadsafe(coro, self.loop).result() |
| occ-001666 有接收者线索 | tests/test_distributed_rollout.py:620 test_slow_task_acquire_does_not_block_async_generation.exercise | assert not task.done() |
| occ-001670 | tests/test_distributed_rollout.py:647 scheduler_factory.remote.<lambda> | return SimpleNamespace(remote=lambda *args: asyncio.run_coroutine_threadsafe(method(*args), loop)) |
| occ-001688 | tests/test_distributed_rollout.py:737 scheduler_factory | asyncio.run_coroutine_threadsafe(cleanup(), loop).result(timeout=10) |
| occ-001956 | tests/test_rollout_straw_transport.py:377 test_synchronous_rollout_stores_during_generation_and_preserves_legacy_hook.exercise | loop = asyncio.get_running_loop() |
| occ-001961 有接收者线索 | tests/test_rollout_straw_transport.py:392 test_synchronous_rollout_stores_during_generation_and_preserves_legacy_hook.exercise.pack | loop.call_soon_threadsafe(second.set) |
| occ-002026 | tests/test_server_control.py:232 test_http_transport_preserves_streams_timeouts_and_connection_budget.check.serve | task = asyncio.current_task() |
| occ-002103 有接收者线索 | tests/test_straw_r3.py:164 test_cancelled_straw_publication_drains_before_releasing_capacity.run | first.cancel() |
| occ-002107 有接收者线索 | tests/test_straw_r3.py:168 test_cancelled_straw_publication_drains_before_releasing_capacity.run | first.cancel() # A second shutdown signal must not release the writer early. |
| occ-002110 有接收者线索 | tests/test_straw_r3.py:170 test_cancelled_straw_publication_drains_before_releasing_capacity.run | assert not first.done() |
| occ-002174 有接收者线索 | tests/test_streaming_rollout.py:437 test_unrelated_stream_cancellation_propagates.exercise | task.cancel() |
| occ-002192 | tests/test_streaming_rollout.py:541 test_generation_pacing_preserves_concurrency_cancellation_and_reuse.exercise | loop = asyncio.get_running_loop() |
专题首页 · 上一节 · 下一节:14 · 回到 Slime:串起创建、等待与清理 · 源码与教程对照 · 全部用法