Make the CFHost cancel probe wait for delivery and reject completed lookups

Wait up to 2 s for the cancellation callback instead of one 50 ms run-loop
pass, count only callbacks without stored addresses so a completed lookup
cannot pass as a cancellation, print the failing stage, and have the runner
require the exact success line since Darling can exit zero after a trap.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
lucannez64andClaude Opus 5.5 authored and Kalah committed 2026-10-16 09:09:28 +02:00
1 parent 67626612ac
commit 72093a1b58
3 files changed
+27 -8

No files matched your search

+4 -2
View File
@@ -406,7 +406,9 @@ python3 build/runtime/tools/run-riot-native-check.py riot-cfhost-cancel-check
Run against the installed CFNetwork override. Expect
`CFHOST_CANCEL_CHECK cancel callback lifetime repeated pass=1`. The unpatched
library traps in `CFRetain(NULL)`; the runner checks the success marker because
library traps in `CFRetain(NULL)`; the runner requires the exact success line because
Darling can return status zero even when the guest traps. The probe uses localhost
and exercises 100 cancellations with both retained and early-released caller
references, without launching or inspecting Riot.
references, without launching or inspecting Riot. A failure prints
`CFHOST_CANCEL_CHECK fail stage=...`; `unexpected-callback` means a completed
lookup arrived instead of the cancellation.
+20 -6
View File
@@ -2,11 +2,22 @@
#include <CFNetwork/CFHost.h>
#include <stdio.h>
static unsigned callbacks;
static unsigned callbacks, unexpected;
static void completed(CFHostRef host, CFHostInfoType type,
const CFStreamError *error, void *info) {
if (host && type == kCFHostAddresses && info == &callbacks)
/* A cancellation stores no addresses; a completed lookup always does. */
Boolean resolved = false;
if (host) CFHostGetAddressing(host, &resolved);
if (host && type == kCFHostAddresses && info == &callbacks && !resolved)
++callbacks;
else
++unexpected;
}
static int fail(const char *stage, unsigned i) {
printf("CFHOST_CANCEL_CHECK fail stage=%s iteration=%u callbacks=%u unexpected=%u\n",
stage, i, callbacks, unexpected);
return 1;
}
int main(void) {
@@ -14,14 +25,17 @@ int main(void) {
for (unsigned i = 0; i < 100; ++i) {
CFHostRef host = CFHostCreateWithName(NULL, CFSTR("localhost"));
CFStreamError error = {0};
if (!host || !CFHostSetClient(host, completed, &context)) return 1;
if (!host || !CFHostSetClient(host, completed, &context)) return fail("client", i);
CFHostScheduleWithRunLoop(host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (!CFHostStartInfoResolution(host, kCFHostAddresses, &error)) return 2;
if (!CFHostStartInfoResolution(host, kCFHostAddresses, &error)) return fail("start", i);
CFHostCancelInfoResolution(host, kCFHostAddresses);
/* The cancel source must keep its host alive until delivery. */
if (i % 2) CFRelease(host);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.05, false);
if (callbacks != i + 1) return 3;
CFAbsoluteTime deadline = CFAbsoluteTimeGetCurrent() + 2.0;
while (callbacks != i + 1 && !unexpected && CFAbsoluteTimeGetCurrent() < deadline)
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.05, true);
if (unexpected) return fail("unexpected-callback", i);
if (callbacks != i + 1) return fail("cancel-callback", i);
if (!(i % 2)) CFRelease(host);
}
puts("CFHOST_CANCEL_CHECK cancel callback lifetime repeated pass=1");
+3
View File
@@ -88,6 +88,9 @@ with tempfile.TemporaryFile() as output:
if (args.check == "riot-running-application-check" and
line != "RUNNING_APP found=1 activated=1 missing=1"):
result = 1
if (args.check == "riot-cfhost-cancel-check" and
line != "CFHOST_CANCEL_CHECK cancel callback lifetime repeated pass=1"):
result = 1
if (args.check == "riot-mach-exception-state-check" and
line != "MACH_EXCEPTION_STATE state=1 identity=1 handled=2 server=1"):
result = 1