Summary
In c/unet.c, unetsocket_send_reliable() does not implement reliable-delivery semantics. It returns 0 (success) as soon as the datagram is accepted (the request is AGREE'd), and never waits for a delivery confirmation. As a result it reports success even when delivery ultimately fails (e.g. an unreachable node).
Unlike the JS bug (#22, a too-short wait) this is a missing feature: there is no delivery wait at all.
Reference behavior
Per the Java/Groovy UnetSocket and Julia UnetSockets.jl, a reliable send returns success only once the datagram is delivered, and failure if the stack reports a delivery failure. The header documents this intent:
/// Transmits a datagram ... with reliability enabled.
/// @return 0 on success, -1 otherwise
int unetsocket_send_reliable(unetsocket_t sock, uint8_t* data, int len, int to, int protocol);
For a reliable send, "success" should mean delivered.
Where it diverges (C)
c/unet.c:
int unetsocket_send_reliable(unetsocket_t sock, uint8_t* data, int len, int to, int protocol) {
...
fjage_msg_add_bool(msg, "reliability", true);
rv = unetsocket_send_request(usock, msg); // same path as the unreliable send
return rv;
}
int unetsocket_send_request(unetsocket_t sock, fjage_msg_t req) {
...
req = request(usock, req, TIMEOUT);
if (req != NULL && fjage_msg_get_performative(req) == FJAGE_AGREE) {
fjage_msg_destroy(req);
return 0; // <-- returns on AGREE (acceptance), not delivery
}
fjage_msg_destroy(req);
return -1;
}
unetsocket_send_reliable() only adds reliability=true and then goes through the exact same code path as unetsocket_send(). Both return 0 the moment the provider AGREEs to the request. There is no wait for DatagramDeliveryNtf / DatagramFailureNtf.
Impact
- Reliable send to a reachable node → returns
0. Happens to look correct, but 0 is returned on acceptance, before delivery is confirmed.
- Reliable send to an unreachable node → returns
0 (wrong; the stack AGREEs to attempt reliable delivery, then emits DatagramFailureNtf after retries — the C API never observes this and reports success). The Java/Julia/(fixed) Python APIs return failure here.
So unetsocket_send_reliable() cannot distinguish delivered from failed — it degrades to the same "accepted for transmission" semantics as the unreliable unetsocket_send().
What's missing
After receiving the AGREE for a reliable request, the C implementation should block waiting for the completion notification matched by the request's message id (inReplyTo):
org.arl.unet.DatagramDeliveryNtf (or RemoteSuccessNtf) → return 0
org.arl.unet.DatagramFailureNtf (or RemoteFailureNtf) → return -1
The existing receive(usock, clazz, id, timeout) helper (wrapping fjage_receive by clazz/id) can wait on these keyed by the request id. Note the delivery wait must be effectively unbounded (reliable delivery can take seconds to tens of seconds); only the AGREE wait should use TIMEOUT. This should apply only when reliability=true (and/or in a blocking send mode), leaving the unreliable fast-path unchanged.
Suggested direction (for @notthetup to decide)
Assigning to @notthetup to decide scope — e.g. whether to add the delivery wait inside unetsocket_send_request gated on the request's reliability flag, or in unetsocket_send_reliable specifically, and how to surface the (currently absent) blocking/semi-blocking send-mode distinction that the other language bindings expose.
Refs: Python fix #21; JS issue #22; Java org.arl.unet.api.UnetSocket.send; UnetSockets.jl Fjage.send(::UnetSocket, ::Message).
Summary
In
c/unet.c,unetsocket_send_reliable()does not implement reliable-delivery semantics. It returns0(success) as soon as the datagram is accepted (the request is AGREE'd), and never waits for a delivery confirmation. As a result it reports success even when delivery ultimately fails (e.g. an unreachable node).Unlike the JS bug (#22, a too-short wait) this is a missing feature: there is no delivery wait at all.
Reference behavior
Per the Java/Groovy
UnetSocketand JuliaUnetSockets.jl, a reliable send returns success only once the datagram is delivered, and failure if the stack reports a delivery failure. The header documents this intent:For a reliable send, "success" should mean delivered.
Where it diverges (C)
c/unet.c:unetsocket_send_reliable()only addsreliability=trueand then goes through the exact same code path asunetsocket_send(). Both return0the moment the provider AGREEs to the request. There is no wait forDatagramDeliveryNtf/DatagramFailureNtf.Impact
0. Happens to look correct, but0is returned on acceptance, before delivery is confirmed.0(wrong; the stack AGREEs to attempt reliable delivery, then emitsDatagramFailureNtfafter retries — the C API never observes this and reports success). The Java/Julia/(fixed) Python APIs return failure here.So
unetsocket_send_reliable()cannot distinguish delivered from failed — it degrades to the same "accepted for transmission" semantics as the unreliableunetsocket_send().What's missing
After receiving the AGREE for a reliable request, the C implementation should block waiting for the completion notification matched by the request's message id (
inReplyTo):org.arl.unet.DatagramDeliveryNtf(orRemoteSuccessNtf) → return0org.arl.unet.DatagramFailureNtf(orRemoteFailureNtf) → return-1The existing
receive(usock, clazz, id, timeout)helper (wrappingfjage_receiveby clazz/id) can wait on these keyed by the request id. Note the delivery wait must be effectively unbounded (reliable delivery can take seconds to tens of seconds); only the AGREE wait should useTIMEOUT. This should apply only whenreliability=true(and/or in a blocking send mode), leaving the unreliable fast-path unchanged.Suggested direction (for @notthetup to decide)
Assigning to @notthetup to decide scope — e.g. whether to add the delivery wait inside
unetsocket_send_requestgated on the request'sreliabilityflag, or inunetsocket_send_reliablespecifically, and how to surface the (currently absent) blocking/semi-blocking send-mode distinction that the other language bindings expose.Refs: Python fix #21; JS issue #22; Java
org.arl.unet.api.UnetSocket.send;UnetSockets.jlFjage.send(::UnetSocket, ::Message).