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 / _release
slime/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_rebuffers
slime/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) 进入后台线程。输入是调用方要执行的协程,输出是它的结果或异常。

  1. 构造函数用 asyncio.new_event_loop() 创建对象,此时 loop 尚未运行。
  2. 新线程中的 asyncio.set_event_loop(self.loop) 把它登记为该线程的当前 loop;登记也不等于启动。
  3. loop.run_forever() 驱动 Task、I/O 和回调,持续运行到收到 loop.stop()。
  4. 调用方用 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();目标线程一旦被堵住,要等待的工作也失去推进机会。

展开固定源码:创建、启动和跨线程等待

AsyncLoopThread
slime/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._run
slime/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.start
slime/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.stop
slime/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 / _close
slime/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_turn
slime/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_session
slime/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_diagnostic
examples/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
        pass

AsyncRolloutWorker._loop completion harvesting
slime/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 registration
slime/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_cb
slime/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-000032examples/coding_agent_rl/generate.py:290
_log_timeout_diagnostic
pending = [t for t in asyncio.all_tasks() if not t.done()]
occ-000374slime/agent/adapters/common.py:241
BaseAdapter.shutdown_session
await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(_drain(), loop))
occ-000375slime/agent/adapters/common.py:241
BaseAdapter.shutdown_session
await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(_drain(), loop))
occ-000385slime/agent/adapters/common.py:337
BaseAdapter._run_turn
task = asyncio.current_task()
occ-000470slime/agent/aiohttp_threaded.py:30
AppHandle
loop: asyncio.AbstractEventLoop
occ-000473slime/agent/aiohttp_threaded.py:38
AppHandle.stop
fut = asyncio.run_coroutine_threadsafe(_shutdown(), self.loop)
occ-000478slime/agent/aiohttp_threaded.py:66
run_app_in_thread._run
loop = asyncio.new_event_loop()
occ-000479slime/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-000613slime/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-000701slime/backends/sglang_utils/server_control.py:176
abort_server_until_idle
loop = asyncio.get_running_loop()
occ-000810slime/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-000835slime/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-000936slime/rollout/fully_async_distributed.py:280
_GenerationActor.start
future = asyncio.run_coroutine_threadsafe(self._start(rollout_id), get_async_loop().loop)
occ-000938slime/rollout/fully_async_distributed.py:281
_GenerationActor.start
await asyncio.wrap_future(future)
occ-000945slime/rollout/fully_async_distributed.py:295
_GenerationActor.pause
future = asyncio.run_coroutine_threadsafe(self._pause(), get_async_loop().loop)
occ-000947slime/rollout/fully_async_distributed.py:296
_GenerationActor.pause
await asyncio.wrap_future(future)
occ-000950slime/rollout/fully_async_distributed.py:302
_GenerationActor.next
future = asyncio.run_coroutine_threadsafe(self._next(), get_async_loop().loop)
occ-000952slime/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-000993slime/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-001008slime/rollout/fully_async_distributed.py:435
_GenerationActor.close
future = asyncio.run_coroutine_threadsafe(self._close(), get_async_loop().loop)
occ-001010slime/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-001017slime/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-001125slime/rollout/sglang_rollout.py:263
_run_request_abortable_generate
task = asyncio.current_task()
occ-001199slime/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-001205slime/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-001207slime/utils/async_utils.py:48
AsyncLoopThread.__init__
self.loop = asyncio.new_event_loop()
occ-001210slime/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-001212slime/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-001670tests/test_distributed_rollout.py:647
scheduler_factory.remote.<lambda>
return SimpleNamespace(remote=lambda *args: asyncio.run_coroutine_threadsafe(method(*args), loop))
occ-001688tests/test_distributed_rollout.py:737
scheduler_factory
asyncio.run_coroutine_threadsafe(cleanup(), loop).result(timeout=10)
occ-001956tests/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-002026tests/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-002192tests/test_streaming_rollout.py:541
test_generation_pacing_preserves_concurrency_cancellation_and_reuse.exercise
loop = asyncio.get_running_loop()

专题首页 · 上一节 · 下一节:14 · 回到 Slime:串起创建、等待与清理 · 源码与教程对照 · 全部用法