capnproto_dart_rpc 0.2.0
capnproto_dart_rpc: ^0.2.0 copied to clipboard
Cap'n Proto RPC (Level 1) support for Dart, built on capnproto_dart.
0.2.0 #
Breaking changes #
The RPC capability API now uses names that describe dispatch, pipelining, and reference ownership more explicitly:
CapCallwas renamed toDispatchHandle.DispatchContextwas renamed toDispatchCancellationContext.TailCallwas renamed toTailCallRequest.vendCapabilityHandle()was renamed toacquireCapabilityLease().Capability.beginDispatch()was renamed toCapability.dispatchForPipelining().Capability.dispatchBuilding()was renamed toCapability.dispatchWithParamsBuilder().CapCall.pipelineResult()was renamed toDispatchHandle.pipelinedCapability().CapCall.pipelineResultPath()was renamed toDispatchHandle.pipelinedCapabilityFromResultPath().
For example:
final DispatchHandle handle = capability.dispatchForPipelining(
interfaceId,
methodId,
params,
);
final Capability resultCapability = handle.pipelinedCapability(0);
- Added
WeakCapabilityRef, a non-owning reference to aCapabilityfor holding onto a peer-supplied capability (e.g. as a long-lived callback/observer) without keeping it reachable or needing todispose()it yourself. Releasemessages for imports are now batched: severaldispose()calls issued without an interveningawait(e.g. disposing a whole observer list in one synchronous pass, orFuture.wait([...].map((c) => c.dispose()))) coalesce into a singleReleaseper import ID withreferenceCount > 1, instead of one wire message each.- Implemented
Return.releaseParamCaps: when a dispatched call's params capabilities are all disposed by the time it settles, the Return now setsreleaseParamCaps: trueand skips the separateReleasemessages that would otherwise follow; the client applies the same local effect without waiting for one. Falls back to individualReleases when only some are disposed in time. - Implemented
Return.noFinishNeeded: a Return whose results carry no capabilities (or which is an exception) now tells the peer noFinishis required, and the client skips sending one. - Fixed a resource leak: when a Call's params-capTable resolution created a new/reused export for a params capability and then failed before the Call itself reached the wire (e.g. a broken import discovered later in the same params list, or the target import breaking before send), the export's refcount bump was never rolled back.
- Fixed a protocol violation: a params capability referencing another in-flight call's still-unresolved result (wire-level promise pipelining) could be sent as a
receiverAnswercapability descriptor before that parent Call itself reached the wire, causing a compliant peer (e.g. capnp-rust) to reject it as referencing an unknown question id. - Fixed a bug where a capability received from a peer (import or wire-pipelined promise) and then passed back to that same peer as a call parameter — via any of the
acquireCapabilityLease-wrapped accessors generated code normally uses — was encoded as a brand-new export instead of the cheapreceiverHostedreference, causing the peer's normal params-release behavior to prematurely dispose the shared underlying capability out from under other live references to it. - Fixed a bug (#99): when a tail-call optimization (
sendResultsTo=yourself) forwarded a call back to a capability hosted on this same vat, and the connection was torn down while that forwarded dispatch was still running, the original call stayed pending instead of failing — if the forwarded dispatch legally ignored cooperative cancellation and finished later anyway, the original call would then succeed from purely local state, even though the connection that correlated it was long gone. It now fails with the same disconnection error every other still-pending call gets on teardown, without needing to abort the still-running forwarded dispatch itself. - Fixed a protocol-correctness bug (#109): a
Finishreceived from the peer before an incoming call's dispatch had completed unconditionally canceled that dispatch and dropped its eventual result — even when a pipelined call already queued behind it ({promisedAnswer: {questionId: ...}}) still depended on that result. Cancellation is now deferred until every such pipelined dependent has finished with the result; the dispatch is left to complete normally and answered with an ordinaryReturnin that case, exactly as Cap'n Proto's RPC spec permits. Also implementedReturn(canceled), which this vat never sent at all before: when cancellation is genuinely accepted (no pipelined dependents), it's now sent once the dispatch actually settles, with the correctReturn.releaseParamCaps. - Fixed a related race (#116): the same
Finish-during-dispatch handling could also cancel a dispatch a peer had not asked to cancel — for asendResultsTo=yourselfforwarded call, once a correlatingReturn.takeFromOtherQuestionhad already claimed this vat's own local reference to the eventual result, the forwarder's ownFinishfor that call no longer meant nothing else needed it. Cancellation is now withheld in that case too, symmetrically with the pipelined-dependent case above. Also fixed the same race's other half: aReturn.takeFromOtherQuestionarriving while the correlated dispatch was still pending is now correlated to it synchronously, the instant theReturnis processed, rather than in a later continuation — previously, aFinishfor that same call arriving in between could turn a legitimate result into an "unknown question id" error. - Fixed a reentrant-peer hazard: a peer that reacted to one of this vat's own
Returnmessages synchronously (e.g. over an in-memory orsync: truetransport) by immediately sendingFinishand reusing the same question id for a new, unrelatedCallcould, in rare orderings, have that new call's state corrupted by leftover cleanup from the old one. Answer bookkeeping is now fully resolved before any Return is sent, closing the window entirely.