.NET Common Runtime Public Interface¶
.NET per-language interface table of contents
1. Common Metadata And Call¶
Handler metadata is an immutable snapshot.
The first typed Decode<T>() on a received ZLinkMessage fixes either a value or a
failure. Later Decode<T>() calls reuse that outcome and do not invoke the serializer
again. A type cast fails if a later T cannot accept the first value.
Decode<ReadOnlyMemory<byte>>() returns a read-only view owned by the framework, while
Decode<byte[]>() returns a new caller-owned copy. Neither raw access fixes the typed
outcome.
public sealed class ZLinkMessage
{
public static ZLinkMessage Empty { get; }
public string? ContentType { get; }
public bool IsEmpty { get; }
public ZlinkStreamCodec? StreamCodec { get; }
public static ZLinkMessage From<T>(T value);
public T Decode<T>();
}
public sealed class ZLinkMessageMetadata
{
public ZLinkMessageMetadata(
IReadOnlyDictionary<string, string> values);
public static ZLinkMessageMetadata Empty { get; }
public IReadOnlyDictionary<string, string> Values { get; }
public string? Find(string key);
}
public interface IZLinkSendCall : IZLinkMetadataCall<IZLinkSendCall>
{
ValueTask Async(
CancellationToken cancellationToken = default);
void Submit(); // synchronous blocking; InvalidOperation in a runtime execution context (F2-a)
}
public interface IZLinkRequestCall : IZLinkMetadataCall<IZLinkRequestCall>
{
IZLinkRequestCall Timeout(TimeSpan timeout);
ValueTask<TReply> Async<TReply>(
CancellationToken cancellationToken = default);
TReply Submit<TReply>(); // synchronous blocking; InvalidOperation in a runtime execution context (F2-a)
ValueTask<TReply> Yield<TReply>(
CancellationToken cancellationToken = default);
}
public interface IZLinkPublishCall : IZLinkMetadataCall<IZLinkPublishCall>
{
ValueTask Async(
CancellationToken cancellationToken = default);
}
public interface IZLinkFanoutPublishCall
{
ValueTask Async(
CancellationToken cancellationToken = default);
}
public interface IZLinkWorkerCall<TResult>
{
IZLinkWorkerCall<TResult> Timeout(TimeSpan timeout);
void Submit(CancellationToken cancellationToken = default);
ValueTask<TResult> Async(CancellationToken cancellationToken = default);
ValueTask<TResult> Yield(CancellationToken cancellationToken = default);
}
public interface IZLinkWorkerOptions
{
int MinThreads { get; set; }
int MaxThreads { get; set; }
TimeSpan IdleTimeout { get; set; }
int MaxQueueLength { get; set; }
}
A one-way call's Async() doesn't produce a normal-completion value.
Normal completion means the source-local queue the operation family
defines accepted the message. It doesn't wait for remote handler
execution, subscriber receipt, remote Spot queue admission, or
application callback completion. If queue capacity is insufficient, it
waits for a capacity signal up to that family's send timeout, and submits
the message exactly once if room opens up within the deadline. Timeout
completes exceptionally with DeadlineExceeded, a route break with
Unavailable, and runtime shutdown with ShuttingDown. Absence of an
Actor/Spot/Mesh/session target uses NotFound. If CancellationToken is
triggered first, it completes with a cancelled ValueTask.
Each one-way call uses the send timeout set on its public configuration. The default when there's no public setting is 1 second.
Logical Multicast's IZLinkPublishCall starts the publish and completes
normally with no return value once it secures source-local execution
capacity within the send timeout. After starting, an individual target
failure doesn't turn into an overall failure and isn't automatically
retried. Per-target admission/failure results aren't returned or
aggregated into monitoring, and it completes normally even with no
targets.
If CancellationToken is triggered before admission, it completes
exactly once with a cancelled ValueTask. Pre-cancellation doesn't start
runtime admission. If admission/timeout/shutdown
and cancellation race, only one atomic terminal winner completes, and
late admission isn't created after a timeout or cancellation. For
Logical Multicast, only
cancellation before publish starts blocks the operation from starting.
Once publish has started, submission to the selected target set proceeds
to completion.
An invalid argument/handle/state, a duplicate terminal, and an
already-used reply token are handled as .NET exceptional completion. An
operation isn't automatically resubmitted after a timeout or
cancellation. The signature of IZLinkMetadataCall<TSelf> and the
1024-byte upper bound are owned by
Topology Configuration §6.
Setting the same key multiple times sends the last value. A reply doesn't
automatically copy request metadata.
The worker call's Submit, Async, and Yield follow the completion
semantics of
Async Execution Policy §1.2.
Worker options can only be set before the host starts.
The Yield terminal only exists on RequestToChannel,
RequestToSpot, RequestToActor, RunIoWorker, RunCpuWorker, and the
Actor/Spot create/get-or-create call. It isn't provided for Actor join,
Node direct request, send, publish, timer registration, close, and
destroy. Even for a common request/worker/create call, the runtime checks
the current execution context before operation submit. If it isn't a
SpotWide User Spot or Instance Spot application handler, it completes
with InvalidOperation without outbound admission, queue change, or gate
return.
If a SpotWide member Actor yields, the Actor queue claim is kept and
only the User Spot gate is returned. The terminal continuation
re-acquires the same gate, finishes the current Actor job, and then
releases the Actor claim. The same Actor's next job doesn't start before
that. Yield isn't allowed on a PerActor User Spot or Entry Spot.
The minimal attribute surface used for assembly scanning is as follows.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ZLinkHandlerGroupAttribute(string groupName) : Attribute
{
public string GroupName { get; } = groupName;
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkRequestAttribute : Attribute
{
public string? PacketName { get; init; }
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkSendAttribute : Attribute
{
public string? PacketName { get; init; }
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkPublishAttribute : Attribute
{
public string? PacketName { get; init; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class ZLinkPacketAttribute(string packetName) : Attribute
{
public string PacketName { get; } = packetName;
}
It looks for method attributes in the assemblies registered on the root,
and ZLinkHandlerGroupAttribute specifies the handler group that handler
participates in. If a method's PacketName is omitted, it checks the
message type's ZLinkPacketAttribute, and if that's also absent, uses
the type name. Packet name is decided once at registration time and
doesn't change with codec selection.
4. Handler Attribute¶
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkSpotRequestAttribute : Attribute
{
public string? PacketName { get; init; }
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class ZLinkSpotPacketHandlerAttribute(string packetName) : Attribute
{
public string PacketName { get; } = packetName;
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class ZLinkSpotRequestHandlerAttribute(string packetName) : Attribute
{
public string PacketName { get; } = packetName;
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkSpotSubscriptionAttribute : Attribute
{
public ZLinkSpotSubscriptionAttribute(
string spotNodeName,
string channelName,
string topic);
public string SpotNodeName { get; }
public string ChannelName { get; }
public string Topic { get; }
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class ZLinkSpotSubscriptionHandlerAttribute : Attribute
{
public ZLinkSpotSubscriptionHandlerAttribute(
string channelName,
string topic);
public string ChannelName { get; }
public string Topic { get; }
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkSpotActorSendAttribute : Attribute
{
public string? PacketName { get; init; }
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class ZLinkSpotActorSendHandlerAttribute(string packetName) : Attribute
{
public string PacketName { get; } = packetName;
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkSpotActorRequestAttribute : Attribute
{
public string? PacketName { get; init; }
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class ZLinkSpotActorRequestHandlerAttribute(string packetName) : Attribute
{
public string PacketName { get; } = packetName;
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class ZLinkSpotTimerHandlerAttribute(
string name,
double periodMilliseconds) : Attribute
{
public string Name { get; } = name;
public double PeriodMilliseconds { get; } = periodMilliseconds;
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ZLinkStreamPacketAttribute : Attribute;