Skip to content

Commit b835809

Browse files
committed
Prove process stop budgets without scheduler timing
1 parent 8e9293c commit b835809

2 files changed

Lines changed: 16 additions & 13 deletions

File tree

dory-core-swift/Sources/DorydKit/HvProcess.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ final class DoryProcessLifecycleMutex: @unchecked Sendable {
1212
semaphore.wait()
1313
}
1414

15-
func lock(until deadline: DispatchTime) -> Bool {
16-
semaphore.wait(timeout: deadline) == .success
15+
/// The optional waiter is an internal seam for proving absolute-deadline forwarding without
16+
/// depending on host scheduling. Production callers always use the semaphore-backed default.
17+
func lock(
18+
until deadline: DispatchTime,
19+
waitUntil: ((DispatchTime) -> DispatchTimeoutResult)? = nil
20+
) -> Bool {
21+
let boundedWait = waitUntil ?? { self.semaphore.wait(timeout: $0) }
22+
return boundedWait(deadline) == .success
1723
}
1824

1925
func unlock() {

dory-core-swift/Tests/DorydKitTests/HvProcessTests.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ final class HvProcessTests: XCTestCase {
340340

341341
func testAbsoluteStopBudgetBeginsBeforeLifecycleMutexAcquisition() {
342342
let lifecycleMutex = DoryProcessLifecycleMutex()
343-
lifecycleMutex.lock()
344343
let deadlineStartedAt = DispatchTime.now()
345344
let deadline = DoryProcessStopDeadline(
346345
gracefulTimeout: 0.04,
@@ -358,14 +357,13 @@ final class HvProcessTests: XCTestCase {
358357
let waiter = DispatchGroup()
359358
waiter.enter()
360359
defer { waiter.leave() }
361-
let mutexRelease = DispatchSemaphore(value: 0)
362-
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 0.07) {
363-
lifecycleMutex.unlock()
364-
mutexRelease.signal()
365-
}
366360

367-
XCTAssertTrue(lifecycleMutex.lock(until: deadline.final))
368-
lifecycleMutex.unlock()
361+
var observedLockDeadline: DispatchTime?
362+
XCTAssertTrue(lifecycleMutex.lock(until: deadline.final) { deadline in
363+
observedLockDeadline = deadline
364+
return .success
365+
})
366+
XCTAssertEqual(observedLockDeadline, deadline.final)
369367
var forced = false
370368
var observedWaitDeadlines: [DispatchTime] = []
371369
let terminated = HvProcess.waitForTermination(
@@ -380,9 +378,8 @@ final class HvProcessTests: XCTestCase {
380378
XCTAssertFalse(terminated)
381379
XCTAssertTrue(forced)
382380
XCTAssertEqual(observedWaitDeadlines, [deadline.graceful, deadline.final])
383-
XCTAssertEqual(mutexRelease.wait(timeout: .now() + 0.1), .success)
384-
// Both waits consumed the one deadline created before mutex acquisition. Inspecting those
385-
// exact arguments proves the forced phase was not restarted after the mutex cleared;
381+
// Mutex acquisition and both termination waits consumed the one deadline created at API
382+
// entry. Inspecting those exact arguments proves neither phase can restart the budget;
386383
// host scheduler latency is deliberately not treated as process-supervisor behavior.
387384
}
388385

0 commit comments

Comments
 (0)