Attempt to prevent timeline loops

This doesn't fix existing loops, and I'm not sure if this will lead to
missing messages (but probably still better than timeline looping...?).

Change-Id: I9c14da58736d61b89d09458168eb2d33e9f21d80
This commit is contained in:
SpiritCroc 2022-04-21 11:30:24 +02:00
parent b3599aac23
commit fe4ec79214
2 changed files with 45 additions and 12 deletions

View file

@ -209,12 +209,35 @@ internal fun ChunkEntity.nextDisplayIndex(direction: PaginationDirection): Int {
}
}
internal fun ChunkEntity.doesPrevChunksVerifyCondition(linkCondition: (ChunkEntity) -> Boolean): Boolean {
var prevChunkToCheck = this.prevChunk
val visitedChunks = hashSetOf(identifier())
while (prevChunkToCheck != null) {
if (visitedChunks.contains(prevChunkToCheck.identifier())) {
Timber.e("doesPrevChunksVerifyCondition: infinite loop detected at ${prevChunkToCheck.identifier()} while checking ${identifier()}")
return false
}
if (linkCondition(prevChunkToCheck)) {
return true
}
visitedChunks.add(prevChunkToCheck.identifier())
prevChunkToCheck = prevChunkToCheck.prevChunk
}
return false
}
internal fun ChunkEntity.doesNextChunksVerifyCondition(linkCondition: (ChunkEntity) -> Boolean): Boolean {
var nextChunkToCheck = this.nextChunk
val visitedChunks = hashSetOf(identifier())
while (nextChunkToCheck != null) {
if (visitedChunks.contains(nextChunkToCheck.identifier())) {
Timber.e("doesNextChunksVerifyCondition: infinite loop detected at ${nextChunkToCheck.identifier()} while checking ${identifier()}")
return false
}
if (linkCondition(nextChunkToCheck)) {
return true
}
visitedChunks.add(nextChunkToCheck.identifier())
nextChunkToCheck = nextChunkToCheck.nextChunk
}
return false

View file

@ -26,6 +26,8 @@ import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.helper.addIfNecessary
import org.matrix.android.sdk.internal.database.helper.addStateEvent
import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
import org.matrix.android.sdk.internal.database.helper.doesNextChunksVerifyCondition
import org.matrix.android.sdk.internal.database.helper.doesPrevChunksVerifyCondition
import org.matrix.android.sdk.internal.database.helper.updateThreadSummaryIfNeeded
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
import org.matrix.android.sdk.internal.database.mapper.toEntity
@ -183,22 +185,30 @@ internal class TokenChunkEventPersistor @Inject constructor(
// If it exists, we want to stop here, just link the prevChunk
val existingChunk = existingTimelineEvent?.chunk?.firstOrNull()
if (existingChunk != null) {
val alreadyLinkedNext = currentChunk.doesNextChunksVerifyCondition { it == existingChunk }
val alreadyLinkedPrev = currentChunk.doesPrevChunksVerifyCondition { it == existingChunk }
if (alreadyLinkedNext || alreadyLinkedPrev) {
Timber.w("Avoid double link, shouldn't happen in an ideal world | " +
"direction: $direction " +
"room: $roomId event: $eventId" +
"linkedPrev: $alreadyLinkedPrev linkedNext: $alreadyLinkedNext " +
"oldChunk: ${existingChunk.identifier()} newChunk: ${existingChunk.identifier()} " +
"oldBackwardCheck: ${currentChunk.nextChunk == existingChunk} " +
"oldForwardCheck: ${currentChunk.prevChunk == existingChunk}"
)
// Stop processing here
return@processTimelineEvents
}
when (direction) {
PaginationDirection.BACKWARDS -> {
if (currentChunk.nextChunk == existingChunk) {
Timber.w("Avoid double link, shouldn't happen in an ideal world")
} else {
currentChunk.prevChunk = existingChunk
existingChunk.nextChunk = currentChunk
}
Timber.i("Backwards insert chunk: ${existingChunk.identifier()} -> ${currentChunk.identifier()}")
currentChunk.prevChunk = existingChunk
existingChunk.nextChunk = currentChunk
}
PaginationDirection.FORWARDS -> {
if (currentChunk.prevChunk == existingChunk) {
Timber.w("Avoid double link, shouldn't happen in an ideal world")
} else {
currentChunk.nextChunk = existingChunk
existingChunk.prevChunk = currentChunk
}
Timber.i("Forward insert chunk: ${currentChunk.identifier()} -> ${existingChunk.identifier()}")
currentChunk.nextChunk = existingChunk
existingChunk.prevChunk = currentChunk
}
}
// Stop processing here