Skip to content

Commit

Permalink
Fix ServerTimeOffsetTracker not being thread safe
Browse files Browse the repository at this point in the history
  • Loading branch information
MrApplejuice committed Oct 1, 2024
1 parent e1779b3 commit 036b6aa
Showing 1 changed file with 34 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,55 @@ import eu.pkgsoftware.babybuddywidgets.Constants
val MAX_OFFSETS = 20

open class ServerTimeOffsetTracker(initialOffsets: Sequence<Long> = sequenceOf()) {
private val mutex = Any()
private var _offsets = initialOffsets.toMutableList()

val offsets: List<Long> get() = _offsets.toList()
val measuredOffset: Long get() = _offsets.let {
if (it.size < 3) {
-1000
} else {
val sortedOffsets = it.sorted()
val p50 = sortedOffsets[it.size / 2]
val p20 = sortedOffsets[it.size * 2 / 10]
return p50 + (p20 - p50) * 5 / 3
val measuredOffset: Long
get() {
synchronized(mutex) {
return _offsets.let {
if (it.size < 3) {
-1000
} else {
val sortedOffsets = it.sorted()
val p50 = sortedOffsets[it.size / 2]
val p20 = sortedOffsets[it.size * 2 / 10]
return p50 + (p20 - p50) * 5 / 3
}
}
}
}
}

protected open fun currentTimeMillis(): Long {
return System.currentTimeMillis()
}

fun addOffsets(offsets: Sequence<Long>) {
_offsets.addAll(offsets)
while (_offsets.size > eu.pkgsoftware.babybuddywidgets.networking.babybuddy.MAX_OFFSETS) {
_offsets.removeAt(0)
synchronized(mutex) {
_offsets.addAll(offsets)
while (_offsets.size > MAX_OFFSETS) {
_offsets.removeAt(0)
}
}
}

fun updateServerTime(dateHeader: String) {
val date = Constants.SERVER_DATE_FORMAT.parse(dateHeader)
val serverMillis = date?.time ?: return

// Note: System.currentTimeMillis() includes the connection latency, but including it
// makes the offset larger, which does not hurt the reason for the offset. If latency is
// high, we just assume that the server time is earlier by the same amount. In the worst
// case, we log entries as being earlier than they actually are, as dictated by the current
// connection latency. What we cannot have is a time arriving at the server that is later
// than local server time.
val newOffset = serverMillis - currentTimeMillis()
addOffsets(sequenceOf(newOffset))
synchronized(mutex) {
val date = Constants.SERVER_DATE_FORMAT.parse(dateHeader)
val serverMillis = date?.time ?: return

// Note: System.currentTimeMillis() includes the connection latency, but including it
// makes the offset larger, which does not hurt the reason for the offset. If latency is
// high, we just assume that the server time is earlier by the same amount. In the worst
// case, we log entries as being earlier than they actually are, as dictated by the current
// connection latency. What we cannot have is a time arriving at the server that is later
// than local server time.
val newOffset = serverMillis - currentTimeMillis()
addOffsets(sequenceOf(newOffset))
}
}

// Not needing locking because measuredOffset is thread-safe
fun localToSafeServerTime(millis: Long): Long {
val mOffset = measuredOffset
val nowOffset = millis - currentTimeMillis()
Expand Down

0 comments on commit 036b6aa

Please sign in to comment.