From a07bb680c32ea84f57b5141e4a9f2489d5853d1e Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 12 Dec 2023 15:31:53 +0530 Subject: [PATCH] Introduced a mutex to manage concurrency in the search functionality. This enables proper clearing of the first running job before executing a new one. By implementing this approach, we ensure that access to the libzim search results occurs one at a time, resolving the crashing issue caused by multiple attempts to access libzim resources. * Replaced the `cancel` function for the Job with `cancelAndJoin`, ensuring that it thoroughly cancels the first job before initiating the new task. --- .../kiwixmobile/core/search/SearchFragment.kt | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt index caa8f0979..77c29dd4a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt @@ -43,7 +43,10 @@ import androidx.recyclerview.widget.RecyclerView import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.base.BaseActivity @@ -86,6 +89,7 @@ class SearchFragment : BaseFragment() { private var searchAdapter: SearchAdapter? = null private var isDataLoading = false private var renderingJob: Job? = null + private val searchMutex = Mutex() override fun inject(baseActivity: BaseActivity) { baseActivity.cachedComponent.inject(this) @@ -252,22 +256,25 @@ class SearchFragment : BaseFragment() { ) } - private fun render(state: SearchState) { - renderingJob?.cancel() - isDataLoading = false - searchInTextMenuItem?.isVisible = state.searchOrigin == FromWebView - searchInTextMenuItem?.isEnabled = state.searchTerm.isNotBlank() - fragmentSearchBinding?.searchLoadingIndicator?.isShowing(true) - renderingJob = searchViewModel.viewModelScope.launch(Dispatchers.Main) { - val searchResult = withContext(Dispatchers.IO) { - state.getVisibleResults(0, renderingJob) - } + private suspend fun render(state: SearchState) { + searchMutex.withLock { + // `cancelAndJoin` cancels the previous running job and waits for it to completely cancel. + renderingJob?.cancelAndJoin() + isDataLoading = false + searchInTextMenuItem?.isVisible = state.searchOrigin == FromWebView + searchInTextMenuItem?.isEnabled = state.searchTerm.isNotBlank() + fragmentSearchBinding?.searchLoadingIndicator?.isShowing(true) + renderingJob = searchViewModel.viewModelScope.launch(Dispatchers.Main) { + val searchResult = withContext(Dispatchers.IO) { + state.getVisibleResults(0, renderingJob) + } - fragmentSearchBinding?.searchLoadingIndicator?.isShowing(false) + fragmentSearchBinding?.searchLoadingIndicator?.isShowing(false) - searchResult?.let { - fragmentSearchBinding?.searchNoResults?.isVisible = it.isEmpty() - searchAdapter?.items = it + searchResult?.let { + fragmentSearchBinding?.searchNoResults?.isVisible = it.isEmpty() + searchAdapter?.items = it + } } } }