Kraken’s nightly architectural scan flagged PinProvider’s bearer-token cache as unsynchronized under concurrent access. refreshIfStale() checked and wrote two separate mutable fields (cachedBearerToken, lastReadMs) with no lock, so N threads could all observe a stale cache simultaneously and all call readTokenFile() — producing redundant file reads and interleaved writes.
private var cachedBearerToken: String? and private var lastReadMs: Long were plain mutable fields with no @Volatile or lock guard. The read-check-write sequence in refreshIfStale() was not atomic: between one thread reading lastReadMs and writing the new values, another thread could make the same staleness decision and race to refresh.
@Volatile private var cached: CachedToken where CachedToken is an immutable data class holding both fields together. Writing a single reference is atomic on the JVM; @Volatile guarantees cross-thread visibility.refreshIfStale(): the outer check is lock-free (fast path for fresh-cache requests); only the stale path enters synchronized(this), where a second check ensures at most one thread actually calls readTokenFile().clock: () -> Long = System::currentTimeMillis constructor parameter (default keeps all call sites unchanged) to make the staleness logic deterministic in tests.readTokenFile() from private to protected open so test subclasses can inject a controlled token source and count calls.@Volatile reference to an immutable holder, not two separate mutable fields — this makes atomic swap the default and eliminates TOCTOU between the fields.CountDownLatch to fire N threads simultaneously at a stale cache and assert readTokenFile() is called exactly once. This regression test is now in PinProviderTest.Mutex.