mirror of
https://github.com/zap-proto/cs.git
synced 2026-10-30 15:05:32 +00:00
Finishes the in-flight capnp→ZAP rebrand on this branch. The prior commits
renamed identifiers/namespaces/files/projects (Capnp.Net.Runtime→Zap.Net.Runtime,
CapnpC→ZapC, .capnp→.zap) but left the human-readable brand name "Cap'n Proto"
throughout doc-comments, .csproj/.nuspec descriptions, README, MSBuild .xaml
rule display names, and the generated XML doc. This completes brand neutrality:
- Replace 154 "Cap'n Proto" / 4 "Cap'n-Proto" / 1 "Cap'n'p" prose occurrences
with "ZAP" across 32 first-party files (runtime, generator, zapc-csharp,
MSBuild integration, chocolatey nuspecs, README, docs, XML doc).
- Revert the LICENSE provenance URL to the real upstream fork
(c80k/capnproto-dotnetcore) — upstream-fork license headers must stay.
- Canonicalize project/repo URLs to github.com/zap-proto/cs (were fabricated
c80k/zap-dotnetcore) in csproj/nuspec/VERIFICATION/README badges.
- Canonicalize docs URLs zap-protocol(.github.io|/zap-cs|/zap-cpp-core)
→ zap-proto.dev / github.com/zap-proto/{cs,cpp-core}.
- Collapse redundant "zap \"ZAP\"" PackageTags to "zap".
Verified consistent (no toolchain on host): every .sln/.csproj ProjectReference
resolves; all namespaces are Zap*/ZapC*; MSBuild task chain
(GenerateZapFileCodeBehindTask, ZapFiles ItemGroup, _ZapcCsharp_TaskAssembly)
is uniformly renamed; props/targets/xaml/coverage/chocolatey carry no capnp.
Deliberately preserved as upstream-derived test data (not first-party source):
upstream Cap'n Proto schema fixtures (rpc.zap, rpc-twoparty.zap, json.zap,
rpc-csharp.zap), golden compiler-error outputs (errors.txt), and the
machine-generated DO-NOT-EDIT test.zap.{c++,h}. Their "Cap'n Proto" strings are
the literal output of the external schema compiler the compat test matches.
114 lines
4.6 KiB
C#
114 lines
4.6 KiB
C#
using Zap.Util;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Zap.Rpc
|
|
{
|
|
abstract class RemoteResolvingCapability : RemoteCapability, IResolvingCapability
|
|
{
|
|
// Set DebugEmbargos to true to get logging output for calls. RPC calls are expected to
|
|
// be on the critical path, hence very relevant for performance. We just can't afford
|
|
// additional stuff on this path. Even if the logger filters the outputs away, there is
|
|
// overhead for creating the Logger object, calling the Logger methods and deciding to
|
|
// filter the output. This justifies the precompiler switch.
|
|
#if DebugEmbargos
|
|
ILogger Logger { get; } = Logging.CreateLogger<RemoteResolvingCapability>();
|
|
#endif
|
|
|
|
public abstract StrictlyOrderedAwaitTask WhenResolved { get; }
|
|
public abstract T? GetResolvedCapability<T>() where T : class;
|
|
|
|
protected RemoteResolvingCapability(IRpcEndpoint ep) : base(ep)
|
|
{
|
|
}
|
|
|
|
protected int _pendingCallsOnPromise;
|
|
StrictlyOrderedAwaitTask? _disembargo;
|
|
|
|
protected abstract ConsumedCapability? ResolvedCap { get; }
|
|
|
|
protected abstract void GetMessageTarget(MessageTarget.WRITER wr);
|
|
|
|
protected IPromisedAnswer CallOnResolution(ulong interfaceId, ushort methodId, DynamicSerializerState args)
|
|
{
|
|
var resolvedCap = ResolvedCap!;
|
|
|
|
try
|
|
{
|
|
if (resolvedCap is NullCapability ||
|
|
// Must not request disembargo on null cap
|
|
|
|
resolvedCap is RemoteCapability ||
|
|
//# Note that in the case where Carol actually lives in Vat B (i.e., the same vat that the promise
|
|
//# already pointed at), no embargo is needed, because the pipelined calls are delivered over the
|
|
//# same path as the later direct calls.
|
|
|
|
(_disembargo == null && _pendingCallsOnPromise == 0) ||
|
|
// No embargo is needed since all outstanding replies have returned
|
|
|
|
_disembargo?.IsCompleted == true
|
|
// Disembargo has returned
|
|
)
|
|
{
|
|
#if DebugEmbargos
|
|
Logger.LogDebug("Direct call");
|
|
#endif
|
|
using var proxy = new Proxy(resolvedCap);
|
|
return proxy.Call(interfaceId, methodId, args, default);
|
|
}
|
|
else
|
|
{
|
|
if (_disembargo == null)
|
|
{
|
|
#if DebugEmbargos
|
|
Logger.LogDebug("Requesting disembargo");
|
|
#endif
|
|
_disembargo = _ep.RequestSenderLoopback(GetMessageTarget).EnforceAwaitOrder();
|
|
}
|
|
else
|
|
{
|
|
#if DebugEmbargos
|
|
Logger.LogDebug("Waiting for requested disembargo");
|
|
#endif
|
|
}
|
|
|
|
var cancellationTokenSource = new CancellationTokenSource();
|
|
|
|
async Task<DeserializerState> AwaitAnswer()
|
|
{
|
|
await _disembargo!;
|
|
|
|
// Two reasons for ignoring exceptions on the previous task (i.e. not _.Wait()ing):
|
|
// 1. A faulting predecessor, especially due to cancellation, must not have any impact on this one.
|
|
// 2. A faulting disembargo request would imply that the other side cannot send pending requests anyway.
|
|
|
|
if (cancellationTokenSource.Token.IsCancellationRequested)
|
|
{
|
|
args.Dispose();
|
|
cancellationTokenSource.Token.ThrowIfCancellationRequested();
|
|
}
|
|
|
|
using var proxy = new Proxy(resolvedCap);
|
|
var promisedAnswer = proxy.Call(interfaceId, methodId, args, default);
|
|
|
|
using (cancellationTokenSource.Token.Register(promisedAnswer.Dispose))
|
|
{
|
|
return await promisedAnswer.WhenReturned;
|
|
}
|
|
}
|
|
|
|
return new LocalAnswer(cancellationTokenSource, AwaitAnswer());
|
|
}
|
|
}
|
|
catch (System.Exception exception)
|
|
{
|
|
// Wrap exception into local answer, since otherwise we'd get an AggregateException (which we don't want).
|
|
return new LocalAnswer(
|
|
new CancellationTokenSource(),
|
|
Task.FromException<DeserializerState>(exception));
|
|
}
|
|
}
|
|
}
|
|
} |