fix(host): cancellable listing scans and O(log window) insertion
capability.list gains an optional AbortSignal threaded from the RPC carrier's request signal (the pickDirectory pattern): a disconnected or timed-out caller stops the opendir loop instead of the scan outliving its caller, and the abort surfaces as its own reason rather than a directory-unreadable dressing. boundedInsert rejects a full window's at-or-beyond-tail candidate on one comparison and binary-inserts retained candidates, so an oversized level no longer pays a window scan per dirent.
This commit is contained in:
@@ -1076,7 +1076,7 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro
|
||||
}
|
||||
},
|
||||
|
||||
async listDirectory(request) {
|
||||
async listDirectory(request, signal) {
|
||||
const capability = ctx.directoryPicker.capability()
|
||||
if (capability.kind !== 'browse') {
|
||||
return err(request, {
|
||||
@@ -1086,7 +1086,9 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro
|
||||
})
|
||||
}
|
||||
try {
|
||||
return ok(request, await capability.list(request.payload.path))
|
||||
// The carrier's signal follows the caller: a disconnect or timeout
|
||||
// stops the backend's directory scan instead of outliving it.
|
||||
return ok(request, await capability.list(request.payload.path, signal))
|
||||
} catch (error: unknown) {
|
||||
return err(request, directoryError(error))
|
||||
}
|
||||
|
||||
@@ -62,10 +62,13 @@ export interface HostApi {
|
||||
/**
|
||||
* List one directory level for the in-app browser; an absent path lists the
|
||||
* host account's home directory. Only served under the `browse` capability;
|
||||
* unreadable or missing targets fail with `directory-unreadable`.
|
||||
* unreadable or missing targets fail with `directory-unreadable`. The
|
||||
* carrier's request signal follows the caller, stopping the backend's scan
|
||||
* on disconnect or timeout.
|
||||
*/
|
||||
listDirectory(
|
||||
request: RpcRequest<{ path?: string }>,
|
||||
signal: AbortSignal,
|
||||
): Promise<RpcResponse<DirectoryListing>>
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,7 +64,7 @@ const UNARY_ROUTES: UnaryRoutes = {
|
||||
'session.cancel': { schema: sessionCancelRequestSchema, invoke: (api, r) => api.sessions.cancel(r) },
|
||||
'host.describe': { schema: hostDescribeRequestSchema, invoke: (api, r) => api.host.describe(r) },
|
||||
'host.pickDirectory': { schema: hostPickDirectoryRequestSchema, invoke: (api, r, signal) => api.host.pickDirectory(r, signal) },
|
||||
'host.listDirectory': { schema: hostListDirectoryRequestSchema, invoke: (api, r) => api.host.listDirectory(r) },
|
||||
'host.listDirectory': { schema: hostListDirectoryRequestSchema, invoke: (api, r, signal) => api.host.listDirectory(r, signal) },
|
||||
'host.createDirectory': { schema: hostCreateDirectoryRequestSchema, invoke: (api, r) => api.host.createDirectory(r) },
|
||||
'host.openPath': { schema: hostOpenPathRequestSchema, invoke: (api, r, signal) => api.host.openPath(r, signal) },
|
||||
'workspace.list': { schema: workspaceListRequestSchema, invoke: (api, r) => api.workspace.list(r) },
|
||||
|
||||
Reference in New Issue
Block a user