mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(sidecar): absorb first-leg double-tap collision sequentially
This commit is contained in:
1 parent
693b86fc61
commit
a68cbb0224
2 files changed
+27
-4
No files matched your search
@@ -186,9 +186,10 @@ private fun walkForStructuralHash(node: com.fasterxml.jackson.databind.JsonNode,
|
|||||||
|
|
||||||
// overlappedDoubleTap fires the second tap while the first is still in
|
// overlappedDoubleTap fires the second tap while the first is still in
|
||||||
// flight, so the on-device gap stays tight on transports with high per-tap
|
// flight, so the on-device gap stays tight on transports with high per-tap
|
||||||
// latency. The overlap can collide with the first tap still executing ("only
|
// latency. The overlap can collide with the other tap still executing ("only
|
||||||
// one gesture can be performed at a time"); the second tap then waits the
|
// one gesture can be performed at a time") on either leg; the colliding leg
|
||||||
// first out and lands sequentially instead of failing the step.
|
// then lands sequentially after the surviving one instead of failing the
|
||||||
|
// step.
|
||||||
internal fun overlappedDoubleTap(tapAction: () -> Unit) {
|
internal fun overlappedDoubleTap(tapAction: () -> Unit) {
|
||||||
val firstTap = java.util.concurrent.CompletableFuture.runAsync { tapAction() }
|
val firstTap = java.util.concurrent.CompletableFuture.runAsync { tapAction() }
|
||||||
Thread.sleep(40)
|
Thread.sleep(40)
|
||||||
@@ -199,7 +200,11 @@ internal fun overlappedDoubleTap(tapAction: () -> Unit) {
|
|||||||
tapAction()
|
tapAction()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
firstTap.join()
|
try {
|
||||||
|
firstTap.join()
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
tapAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MetricsSample(
|
data class MetricsSample(
|
||||||
|
|||||||
@@ -201,6 +201,24 @@ class DriverServiceTest {
|
|||||||
assertEquals(2, invocations.get())
|
assertEquals(2, invocations.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun overlappedDoubleTapRetriesWhenFirstLegCollides() {
|
||||||
|
val landed = java.util.concurrent.atomic.AtomicInteger(0)
|
||||||
|
val failedFirst = java.util.concurrent.atomic.AtomicBoolean(false)
|
||||||
|
// The async first tap loses the race and collides; the second tap
|
||||||
|
// succeeds. The collision must be absorbed with a sequential retry,
|
||||||
|
// not propagated out of the join.
|
||||||
|
val tapAction = {
|
||||||
|
if (failedFirst.compareAndSet(false, true)) {
|
||||||
|
Thread.sleep(60)
|
||||||
|
throw IllegalStateException("only one gesture can be performed at a time")
|
||||||
|
}
|
||||||
|
landed.incrementAndGet()
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
overlappedDoubleTap(tapAction)
|
||||||
|
assertEquals(2, landed.get())
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun doubleTapDefaultComposesTwoTaps() {
|
@Test fun doubleTapDefaultComposesTwoTaps() {
|
||||||
// Interface delegation would bind the default doubleTap to the
|
// Interface delegation would bind the default doubleTap to the
|
||||||
// delegate, bypassing the tap override, so implement the interface
|
// delegate, bypassing the tap override, so implement the interface
|
||||||
|
|||||||
Reference in new issue
Block a user