Skip to content

Async Model

github-actions[bot] edited this page Jul 16, 2026 · 8 revisions

异步模型

异步 API 把同步 Send* / WebSocket 连接搬到固定 4 线程工作池执行;调用方持有 opaque AsyncOp,用等待/轮询/取消收尾。同步路径不受异步运行时影响。

运行时

工作线程 固定 4AsyncWorkerCount
队列 FIFO,最大深度 256
队满 STATUS_INSUFFICIENT_RESOURCES
初始化 CAS 惰性启动

种类:HttpSendWebSocketConnect。状态:Pending → Running → Completed

生命周期

  1. 创建AsyncGet / AsyncPost / AsyncSendwknet::websocket::ConnectAsyncAsyncOp*,初始 STATUS_PENDING,引用计数 1。
  2. 入队:worker 执行;若入队前已取消,立即以 STATUS_CANCELLED 完成。
  3. 等待AsyncWait(op, timeoutMs),或轮询 AsyncGetStatus / AsyncIsCompleted
  4. 取结果:HTTP 用 AsyncGetResponse;WebSocket 用 wknet::websocket::AsyncGetWebSocket
  5. 释放AsyncRelease(引用归零时清理)。
wknet::http::AsyncOp* op = nullptr;
NTSTATUS s = wknet::http::AsyncGetEx(session, url, urlLen, nullptr, nullptr, &op);
if (NT_SUCCESS(s) && wknet::http::AsyncWait(op, 30000) == STATUS_SUCCESS) {
    wknet::http::Response* r = nullptr;
    if (NT_SUCCESS(wknet::http::AsyncGetResponse(op, &r))) {
        // 使用 r
        wknet::http::ResponseRelease(r);
    }
}
wknet::http::AsyncRelease(op);

引用计数与取消

  • 用户句柄持 1 个引用;worker 另持 1 个,保证用户标记关闭后仍可观察取消标志。
  • AsyncCancelPending 立即完成;RunningCanceled 传入传输等待,底层支持时取消活跃 IRP。
  • 取消是协作式的:调用 AsyncCancel 后仍须 AsyncWait 收尾,再 AsyncRelease

完成回调

AsyncOptions.OnComplete(及测试路径下的完成回调)在操作进入 Completed 时调用。回调内不要做重活或再次同步等待同一 op;适合投递上层队列。

驱动卸载

使用过异步 API 时,在卸载路径调用 Destroy,等待异步操作完成后再释放 WSK 与其它句柄:

wknet::http::Destroy();
// 再释放 WSK / 关闭 session 与其它句柄

未使用异步 API 时可不调用;调用也无害。Session 等句柄通过引用计数与等待,避免操作仍在进行时被释放。

与同步 API 的关系

同步 Get* / Send* 异步 Async*
调用 IRQL PASSIVE_LEVEL 提交/等待在 PASSIVE_LEVEL
阻塞点 调用线程 worker 线程
取消 无公开中途取消 AsyncCancel
连接池 / 重定向 / H3 同一 session 策略 同一 session 策略

异步不改变协议语义:stale 重试、redirect 上限、H3 Auto、证书校验规则与同步路径一致。


English

Async Model

Async APIs move synchronous Send* / WebSocket connect work onto a fixed 4-thread worker pool. Callers hold an opaque AsyncOp and finish with wait, poll, or cancel. Synchronous paths do not depend on the async runtime.

Runtime

Item Value
Workers Fixed 4 (AsyncWorkerCount)
Queue FIFO, max depth 256
Queue full STATUS_INSUFFICIENT_RESOURCES
Startup CAS lazy init

Kinds: HttpSend, WebSocketConnect. States: Pending → Running → Completed.

Lifecycle

  1. Create: AsyncGet / AsyncPost / AsyncSend or wknet::websocket::ConnectAsyncAsyncOp*, initially STATUS_PENDING, refcount 1.
  2. Enqueue: a worker runs the op; if already canceled before run, it completes immediately with STATUS_CANCELLED.
  3. Wait: AsyncWait(op, timeoutMs), or poll AsyncGetStatus / AsyncIsCompleted.
  4. Take result: HTTP via AsyncGetResponse; WebSocket via wknet::websocket::AsyncGetWebSocket.
  5. Release: AsyncRelease (cleanup when the refcount hits zero).
wknet::http::AsyncOp* op = nullptr;
NTSTATUS s = wknet::http::AsyncGetEx(session, url, urlLen, nullptr, nullptr, &op);
if (NT_SUCCESS(s) && wknet::http::AsyncWait(op, 30000) == STATUS_SUCCESS) {
    wknet::http::Response* r = nullptr;
    if (NT_SUCCESS(wknet::http::AsyncGetResponse(op, &r))) {
        // use r
        wknet::http::ResponseRelease(r);
    }
}
wknet::http::AsyncRelease(op);

Reference counting and cancellation

  • The user handle holds one reference; the worker holds another so the object can still observe cancellation after the user marks it closed.
  • AsyncCancel: completes Pending immediately; for Running, forwards Canceled into transport waits and cancels the active IRP when supported.
  • Cancellation is cooperative: after AsyncCancel, still AsyncWait, then AsyncRelease.

Completion callbacks

AsyncOptions.OnComplete (and test-path completion callbacks) run when the op becomes Completed. Do not do heavy work or synchronously wait on the same op inside the callback; post to an upper queue instead.

Driver unload

When async APIs were used, call Destroy on the unload path and wait for async work to finish before releasing WSK and other handles:

wknet::http::Destroy();
// then release WSK / close sessions and other handles

Sync-only paths may skip it; calling is always safe. Sessions and related handles use reference counts and waits so a handle is not freed while an operation still uses it.

Relation to sync APIs

Sync Get* / Send* Async Async*
Call IRQL PASSIVE_LEVEL Submit/wait at PASSIVE_LEVEL
Blocking point Calling thread Worker thread
Cancel No public mid-flight cancel AsyncCancel
Pool / redirect / H3 Same session policy Same session policy

Async does not change protocol semantics: stale retry, redirect limits, H3 Auto, and certificate rules match the synchronous path.

Clone this wiki locally