OpenCode 源码解析
第 16 讲:Effect 运行时系统——Fiber、Runner 与服务编排
基于 dev 分支源码 · 2026-07-24
一、Effect 运行时:OpenCode 的并发引擎
前十五讲覆盖了 OpenCode 从 CLI 到数据库的核心能力。这一讲进入Effect 运行时系统——OpenCode 基于 Effect TS 构建的并发引擎,管理 Fiber、Runner 和服务编排。Effect 模块横跨 11 个文件、约 767 行代码,是整个系统的并发基础设施。
📦 本讲核心文件
packages/opencode/src/effect/runner.ts(217 行)— Runner 并发控制器
packages/opencode/src/effect/app-runtime.ts(135 行)— 应用运行时
packages/opencode/src/effect/instance-state.ts(69 行)— 实例状态管理
packages/opencode/src/effect/run-service.ts(47 行)— 服务运行器
packages/opencode/src/effect/bridge.ts(84 行)— Effect/Promise 桥接
二、Runner:并发控制器
runner.ts(217 行)是 Effect 运行时的核心,实现了 Runner 模式——管理 Effect 任务的生命周期、并发控制和取消。
1. Runner 接口
📄 effect/runner.ts (第 3-9 行)
export interface Runner<A, E = never> {
readonly state: State<A, E>
readonly busy: boolean
readonly ensureRunning: (work: Effect.Effect<A, E>)
=> Effect.Effect<A, E>
readonly startShell: (work: Effect.Effect<A, E>,
ready?: Latch.Latch) => Effect.Effect<A, E | Busy>
readonly cancel: Effect.Effect<void>
}
设计亮点:Runner 封装了 Effect 任务的生命周期管理,提供 5 个核心方法:状态查询、忙碌检查、确保运行、启动 Shell 和取消任务。
2. 四种状态
📄 effect/runner.ts (第 33-37 行)
export type State<A, E> =
| { readonly _tag: "Idle" }
| { readonly _tag: "Running"; readonly run: RunHandle<A, E> }
| { readonly _tag: "Shell"; readonly shell: ShellHandle<A, E> }
| { readonly _tag: "ShellThenRun";
readonly shell: ShellHandle<A, E>;
readonly run: PendingHandle<A, E> }
状态机:Idle(空闲)→ Running(运行中)→ Shell(Shell 模式)→ ShellThenRun(Shell 后运行)。每个状态都有对应的处理逻辑。
三、Fiber 并发模型
Runner 基于 Effect 的 Fiber 模型实现并发控制,支持任务取消、超时和错误处理。
1. Fiber 生命周期
📄 effect/runner.ts (第 83-91 行)
const startRun = (work: Effect.Effect<A, E>,
done: Deferred.Deferred<A, E | Cancelled>) =>
Effect.gen(function* () {
const id = next()
const fiber = yield* work.pipe(
Effect.onExit((exit) => finishRun(id, done, exit)),
Effect.forkIn(scope),
)
return { id, done, fiber } satisfies RunHandle<A, E>
})
Fiber 设计:每个任务 fork 到 Scope 中运行,通过 Deferred 跟踪完成状态。onExit 回调确保任务结束时正确更新状态。
2. 任务取消
📄 effect/runner.ts (第 171-202 行)
const cancel = SynchronizedRef.modify(ref, (st) => {
switch (st._tag) {
case "Idle":
return [Effect.void, st] as const
case "Running":
return [
Effect.gen(function* () {
yield* Fiber.interrupt(st.run.fiber)
yield* Deferred.fail(st.run.done,
new Cancelled()).pipe(Effect.asVoid)
yield* idleIfCurrent()
}),
{ _tag: "Idle" } as const,
] as const
case "Shell":
return [
Effect.gen(function* () {
yield* stopShell(st.shell)
yield* idleIfCurrent()
}),
{ _tag: "Idle" } as const,
] as const
// ... ShellThenRun 同理
}
}).pipe(Effect.flatten)
取消逻辑:根据当前状态中断对应的 Fiber,更新 Deferred 为 Cancelled 状态,然后回到 Idle 状态。Shell 模式需要等待 ready 信号。
四、实例状态管理
instance-state.ts(69 行)实现了基于目录的实例状态管理,每个项目目录有独立的状态。
1. InstanceState 接口
📄 effect/instance-state.ts (第 15-40 行)
export type InstanceState<S> = {
readonly make: (effect: Effect.Effect<S>)
=> Effect.Effect<void>
readonly use: (f: (state: S) => A)
=> Effect.Effect<A>
readonly useEffect: (f: (state: S)
=> Effect.Effect<A, E>)
=> Effect.Effect<A, E>
readonly directory: string
}
设计亮点:InstanceState 基于 ScopedCache 实现,每个项目目录有独立状态。make 创建状态,use/useEffect 读取状态。状态在实例销毁时自动清理。
五、总结
🔹 Runner 模式管理 Effect 任务生命周期
🔹 四种状态:Idle/Running/Shell/ShellThenRun
🔹 Fiber 并发模型支持取消、超时和错误处理
🔹 InstanceState 基于目录的实例状态管理
🔹 EffectBridge 桥接 Promise 和 Effect
🔹 SynchronizedRef 保证状态修改的原子性
← 系列导航 →
← 第 15 讲:Env 与运行时标志 | 第 17 讲:Event 事件总线 →
关注公众号获取更多 OpenCode 源码解析干货
源码:https://github.com/opencode-ai/opencode
夜雨聆风