Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class EosSdkBridgeImpl : EosSdkBridge {
override val isLoggedIn: Boolean get() = localUser != null

override fun isStalled(): Boolean =
EosTickHealth.isStalled(running && platform != null, lastTickMs, monotonicMs())
EosTickHealth.isStalled(running && platform != null, lastTickMs, EosTickHealth.monotonicMs())

companion object {
@Volatile private var logTarget: EosSdkBridgeImpl? = null
Expand Down Expand Up @@ -260,7 +260,7 @@ class EosSdkBridgeImpl : EosSdkBridge {
var tick = 0L

while (running) {
lastTickMs = monotonicMs()
lastTickMs = EosTickHealth.monotonicMs()
runCatching { pump() }.onFailure { logger.error("An EOS tick failed", it) }

tick++
Expand Down Expand Up @@ -339,8 +339,6 @@ class EosSdkBridgeImpl : EosSdkBridge {
java.util.concurrent.locks.LockSupport.parkNanos(nanos)
}

private fun monotonicMs(): Long = System.nanoTime() / 1_000_000

private fun detachHandlers() {
inboundHandler = null
loginLostHandler = null
Expand Down Expand Up @@ -427,7 +425,7 @@ class EosSdkBridgeImpl : EosSdkBridge {
}

private fun onLog(category: String, level: EosLogLevel, message: String) {
val suppressed = logThrottle.onMessage(message, monotonicMs()) ?: return
val suppressed = logThrottle.onMessage(message, EosTickHealth.monotonicMs()) ?: return
val text = if (suppressed == 0) message else "$message [+$suppressed identical messages suppressed]"

val logName = category.removePrefix("LogEOS").ifBlank { "polyplus/eos" }.let { "polyplus/eos/$it" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ object EosTickHealth {
const val THREAD_NAME = "polyplus-eos"
const val STALL_THRESHOLD_MS = 10_000L

const val OBSERVER_GAP_TOLERANCE_MS = 2_000L

fun monotonicMs(): Long = System.nanoTime() / 1_000_000

fun isStalled(ticking: Boolean, lastTickMs: Long, nowMs: Long): Boolean =
ticking && lastTickMs != 0L && nowMs - lastTickMs > STALL_THRESHOLD_MS

fun isObservationTrustworthy(lastCheckMs: Long, nowMs: Long): Boolean =
lastCheckMs != 0L && nowMs - lastCheckMs <= OBSERVER_GAP_TOLERANCE_MS
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,20 @@ object P2PSessionManager : EarlyInitializable {
}

private var stallReported = false
private var lastStallCheckMs = 0L
private var suppressStallUntilMs = 0L

private fun checkForStalledEos() {
val bridge = this.bridge ?: return
val now = EosTickHealth.monotonicMs()
val lastCheck = lastStallCheckMs
lastStallCheckMs = now

if (!EosTickHealth.isObservationTrustworthy(lastCheck, now)) {
suppressStallUntilMs = now + EosTickHealth.STALL_THRESHOLD_MS
}
if (now < suppressStallUntilMs) return

if (!bridge.isStalled()) {
if (stallReported) {
stallReported = false
Expand Down
22 changes: 22 additions & 0 deletions src/test/kotlin/org/polyfrost/polyplus/test/EosTickHealthTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,26 @@ class EosTickHealthTest {
assertFalse(EosTickHealth.isStalled(ticking = true, lastTickMs = 0L, nowMs = NOW))
assertFalse(EosTickHealth.isStalled(ticking = false, lastTickMs = NOW - OVER, nowMs = NOW))
}

@Test
fun `a look taken after the watchdog was away itself proves nothing`() {
assertFalse(EosTickHealth.isObservationTrustworthy(lastCheckMs = 0L, nowMs = NOW))
assertFalse(
EosTickHealth.isObservationTrustworthy(
lastCheckMs = NOW - EosTickHealth.OBSERVER_GAP_TOLERANCE_MS - 1,
nowMs = NOW,
),
)
}

@Test
fun `a look taken while the client keeps ticking is trustworthy`() {
assertTrue(EosTickHealth.isObservationTrustworthy(lastCheckMs = NOW - 50L, nowMs = NOW))
assertTrue(
EosTickHealth.isObservationTrustworthy(
lastCheckMs = NOW - EosTickHealth.OBSERVER_GAP_TOLERANCE_MS,
nowMs = NOW,
),
)
}
}