|
|
|
diff --git a/app/src/main/java/androidx/room/InvalidationTracker.kt b/app/src/main/java/androidx/room/InvalidationTracker.kt
|
|
|
|
index 38067b702f..76cf95815c 100644
|
|
|
|
--- a/app/src/main/java/androidx/room/InvalidationTracker.kt
|
|
|
|
+++ b/app/src/main/java/androidx/room/InvalidationTracker.kt
|
|
|
|
@@ -405,10 +405,14 @@ open class InvalidationTracker @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX
|
|
|
|
|
|
|
|
private fun checkUpdatedTable(): Set<Int> {
|
|
|
|
val invalidatedTableIds = buildSet {
|
|
|
|
- database.query(SimpleSQLiteQuery(SELECT_UPDATED_TABLES_SQL)).useCursor { cursor ->
|
|
|
|
- while (cursor.moveToNext()) {
|
|
|
|
- add(cursor.getInt(0))
|
|
|
|
+ try {
|
|
|
|
+ database.query(SimpleSQLiteQuery(SELECT_UPDATED_TABLES_SQL)).useCursor { cursor ->
|
|
|
|
+ while (cursor.moveToNext()) {
|
|
|
|
+ add(cursor.getInt(0))
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
+ } catch (ex: Throwable) {
|
|
|
|
+ eu.faircode.email.Log.w(ex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (invalidatedTableIds.isNotEmpty()) {
|
|
|
|
diff --git a/app/src/main/java/androidx/room/RoomTrackingLiveData.kt b/app/src/main/java/androidx/room/RoomTrackingLiveData.kt
|
|
|
|
index 171b57d16e..32317b296a 100644
|
|
|
|
--- a/app/src/main/java/androidx/room/RoomTrackingLiveData.kt
|
|
|
|
+++ b/app/src/main/java/androidx/room/RoomTrackingLiveData.kt
|
|
|
|
@@ -23,6 +23,7 @@ import java.lang.RuntimeException
|
|
|
|
import java.util.concurrent.Callable
|
|
|
|
import java.util.concurrent.Executor
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
|
|
+import java.util.concurrent.atomic.AtomicInteger
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A LiveData implementation that closely works with [InvalidationTracker] to implement
|
|
|
|
@@ -54,62 +55,62 @@ internal class RoomTrackingLiveData<T> (
|
|
|
|
val invalid = AtomicBoolean(true)
|
|
|
|
val computing = AtomicBoolean(false)
|
|
|
|
val registeredObserver = AtomicBoolean(false)
|
|
|
|
+ val queued = AtomicInteger(0);
|
|
|
|
val refreshRunnable = Runnable {
|
|
|
|
- if (registeredObserver.compareAndSet(false, true)) {
|
|
|
|
- database.invalidationTracker.addWeakObserver(observer)
|
|
|
|
- }
|
|
|
|
- var computed: Boolean
|
|
|
|
- do {
|
|
|
|
- computed = false
|
|
|
|
- // compute can happen only in 1 thread but no reason to lock others.
|
|
|
|
- if (computing.compareAndSet(false, true)) {
|
|
|
|
- // as long as it is invalid, keep computing.
|
|
|
|
- try {
|
|
|
|
- var value: T? = null
|
|
|
|
- while (invalid.compareAndSet(true, false)) {
|
|
|
|
- computed = true
|
|
|
|
- try {
|
|
|
|
- value = computeFunction.call()
|
|
|
|
- } catch (e: Exception) {
|
|
|
|
- throw RuntimeException(
|
|
|
|
- "Exception while computing database live data.",
|
|
|
|
- e
|
|
|
|
- )
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (computed) {
|
|
|
|
- postValue(value)
|
|
|
|
- }
|
|
|
|
- } finally {
|
|
|
|
- // release compute lock
|
|
|
|
- computing.set(false)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- // check invalid after releasing compute lock to avoid the following scenario.
|
|
|
|
- // Thread A runs compute()
|
|
|
|
- // Thread A checks invalid, it is false
|
|
|
|
- // Main thread sets invalid to true
|
|
|
|
- // Thread B runs, fails to acquire compute lock and skips
|
|
|
|
- // Thread A releases compute lock
|
|
|
|
- // We've left invalid in set state. The check below recovers.
|
|
|
|
- } while (computed && invalid.get())
|
|
|
|
- }
|
|
|
|
+ if (registeredObserver.compareAndSet(false, true)) {
|
|
|
|
+ database.invalidationTracker.addWeakObserver(observer)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ val v = queued.decrementAndGet();
|
|
|
|
+ if (v < 0) {
|
|
|
|
+ queued.set(0)
|
|
|
|
+ eu.faircode.email.Log.e("$computeFunction queued=$v")
|
|
|
|
+ } else if (v > 0)
|
|
|
|
+ eu.faircode.email.Log.persist(eu.faircode.email.EntityLog.Type.Debug1, "$computeFunction queued=$v")
|
|
|
|
+
|
|
|
|
+ if (v <= 0) {
|
|
|
|
+ var value: T? = null
|
|
|
|
+ var computed = false
|
|
|
|
+ synchronized(computeFunction) {
|
|
|
|
+ var retry = 0
|
|
|
|
+ while (!computed) {
|
|
|
|
+ try {
|
|
|
|
+ value = computeFunction.call()
|
|
|
|
+ computed = true
|
|
|
|
+ } catch (e: Throwable) {
|
|
|
|
+ if (++retry > 5) {
|
|
|
|
+ eu.faircode.email.Log.e(e)
|
|
|
|
+ break
|
|
|
|
+ }
|
|
|
|
+ eu.faircode.email.Log.w(e)
|
|
|
|
+ try {
|
|
|
|
+ Thread.sleep(2000L)
|
|
|
|
+ } catch (ignored: InterruptedException) {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (computed) {
|
|
|
|
+ postValue(value)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
|
|
val invalidationRunnable = Runnable {
|
|
|
|
- val isActive = hasActiveObservers()
|
|
|
|
- if (invalid.compareAndSet(false, true)) {
|
|
|
|
- if (isActive) {
|
|
|
|
- queryExecutor.execute(refreshRunnable)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
+ val isActive = hasActiveObservers()
|
|
|
|
+ if (isActive) {
|
|
|
|
+ queued.incrementAndGet()
|
|
|
|
+ queryExecutor.execute(refreshRunnable)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
override fun onActive() {
|
|
|
|
- super.onActive()
|
|
|
|
- container.onActive(this as LiveData<Any>)
|
|
|
|
- queryExecutor.execute(refreshRunnable)
|
|
|
|
- }
|
|
|
|
+ super.onActive()
|
|
|
|
+ container.onActive(this as LiveData<Any>)
|
|
|
|
+ queued.incrementAndGet();
|
|
|
|
+ queryExecutor.execute(refreshRunnable)
|
|
|
|
+ }
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
override fun onInactive() {
|
|
|
|
diff --git a/app/src/main/java/androidx/room/paging/LimitOffsetDataSource.java b/app/src/main/java/androidx/room/paging/LimitOffsetDataSource.java
|
|
|
|
index 2b5c391dbc..077ae233e8 100644
|
|
|
|
--- a/app/src/main/java/androidx/room/paging/LimitOffsetDataSource.java
|
|
|
|
+++ b/app/src/main/java/androidx/room/paging/LimitOffsetDataSource.java
|
|
|
|
@@ -153,7 +153,7 @@ public abstract class LimitOffsetDataSource<T> extends androidx.paging.Positiona
|
|
|
|
@NonNull LoadInitialCallback<T> callback) {
|
|
|
|
registerObserverIfNecessary();
|
|
|
|
List<T> list = Collections.emptyList();
|
|
|
|
- int totalCount;
|
|
|
|
+ int totalCount = 0;
|
|
|
|
int firstLoadPosition = 0;
|
|
|
|
RoomSQLiteQuery sqLiteQuery = null;
|
|
|
|
Cursor cursor = null;
|
|
|
|
@@ -171,6 +171,8 @@ public abstract class LimitOffsetDataSource<T> extends androidx.paging.Positiona
|
|
|
|
mDb.setTransactionSuccessful();
|
|
|
|
list = rows;
|
|
|
|
}
|
|
|
|
+ } catch (Throwable ex) {
|
|
|
|
+ eu.faircode.email.Log.w(ex);
|
|
|
|
} finally {
|
|
|
|
if (cursor != null) {
|
|
|
|
cursor.close();
|