Improved performance when multiple FS changes occur in a fraction of a second

Between update there is a minimum of 0.5s even if there have been multiple file updates
This commit is contained in:
Abdelilah El Aissaoui 2022-02-25 00:40:11 +01:00
parent 41ff6a57b8
commit afc7d9df8e
3 changed files with 50 additions and 18 deletions

View file

@ -87,7 +87,7 @@ class DiffManager @Inject constructor(
}
}
fun prepareTreeParser(repository: Repository, commit: RevCommit): AbstractTreeIterator? {
fun prepareTreeParser(repository: Repository, commit: RevCommit): AbstractTreeIterator {
// from the commit we can build the tree which allows us to construct the TreeParser
RevWalk(repository).use { walk ->
val tree: RevTree = walk.parseTree(commit.tree.id)

View file

@ -1,17 +1,26 @@
package app.git
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import java.io.IOException
import java.nio.file.*
import java.nio.file.StandardWatchEventKinds.*
import java.nio.file.attribute.BasicFileAttributes
import javax.inject.Inject
private const val MIN_TIME_IN_MS_BETWEEN_REFRESHES = 500L
class FileChangesWatcher @Inject constructor() {
suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = flow {
private var lastNotify = 0L
private var asyncJob: Job? = null
private val _changesNotifier = MutableSharedFlow<Long>()
val changesNotifier: SharedFlow<Long> = _changesNotifier
suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = withContext(Dispatchers.IO) {
println(ignoredDirsPath)
val watchService = FileSystems.getDefault().newWatchService()
val path = Paths.get(pathStr)
@ -40,10 +49,33 @@ class FileChangesWatcher @Inject constructor() {
var key: WatchKey
while (watchService.take().also { key = it } != null) {
this.emit(Unit)
key.pollEvents()
println("Polled events")
asyncJob?.cancel()
// Sometimes external apps can run filesystem multiple operations in a fraction of a second.
// To prevent excessive updates, we add a slight delay between updates emission to prevent slowing down
// the app by constantly running "git status".
val currentTimeMillis = System.currentTimeMillis()
val diffTime = currentTimeMillis - lastNotify
if (diffTime > MIN_TIME_IN_MS_BETWEEN_REFRESHES) {
_changesNotifier.emit(currentTimeMillis)
println("Sync emit with diff time $diffTime")
} else {
asyncJob = async {
delay(MIN_TIME_IN_MS_BETWEEN_REFRESHES)
println("Async emit")
if (isActive)
_changesNotifier.emit(currentTimeMillis)
}
}
lastNotify = currentTimeMillis
key.reset()
}
}.flowOn(Dispatchers.IO)
}
}

View file

@ -7,13 +7,10 @@ import app.credentials.CredentialsStateManager
import app.git.*
import app.newErrorNow
import app.ui.SelectedItem
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.lib.Repository
@ -148,15 +145,18 @@ class TabViewModel @Inject constructor(
private suspend fun watchRepositoryChanges(git: Git) = tabState.managerScope.launch(Dispatchers.IO) {
val ignored = git.status().call().ignoredNotInIndex.toList()
launch {
fileChangesWatcher.changesNotifier.collect {
if (!tabState.operationRunning) { // Only update if there isn't any process running
println("Changes detected, loading status")
checkUncommitedChanges()
}
}
}
fileChangesWatcher.watchDirectoryPath(
pathStr = git.repository.directory.parent,
ignoredDirsPath = ignored,
).collect {
if (!tabState.operationRunning) { // Only update if there isn't any process running
println("Changes detected, loading status")
checkUncommitedChanges()
}
}
)
}
private suspend fun checkUncommitedChanges(fullUpdateLog: Boolean = false) = tabState.runOperation(