Updated ROOM patch

This commit is contained in:
M66B 2024-05-02 08:49:29 +02:00
parent 58a63a366d
commit b8250fe6a0
4 changed files with 248 additions and 54 deletions

View File

@ -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()) {

View File

@ -54,62 +54,79 @@ internal class RoomTrackingLiveData<T> (
val invalid = AtomicBoolean(true)
val computing = AtomicBoolean(false)
val registeredObserver = AtomicBoolean(false)
val queued = eu.faircode.email.ObjectHolder<Int>(0)
val lock = Object()
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())
}
eu.faircode.email.Log.i("MMM Refresh")
synchronized(lock) {
queued.value--
if (queued.value < 0) {
eu.faircode.email.Log.e("$computeFunction queued=$queued.value")
queued.value = 0
}
}
eu.faircode.email.Log.i("MMM Refreshing")
if (registeredObserver.compareAndSet(false, true)) {
database.invalidationTracker.addWeakObserver(observer)
}
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)
}
eu.faircode.email.Log.i("MMM Refreshed")
}
val invalidationRunnable = Runnable {
val isActive = hasActiveObservers()
if (invalid.compareAndSet(false, true)) {
if (isActive) {
queryExecutor.execute(refreshRunnable)
}
}
}
val isActive = hasActiveObservers()
if (invalid.compareAndSet(false, true)) {
if (isActive) {
eu.faircode.email.Log.i("MMM Invalidate")
synchronized(lock) {
if (queued.value > 0) {
eu.faircode.email.Log.persist(eu.faircode.email.EntityLog.Type.Debug, "$computeFunction queued=$queued.value")
} else {
queued.value++
queryExecutor.execute(refreshRunnable)
}
eu.faircode.email.Log.i("MMM Invalidated")
}
}
}
}
@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>)
eu.faircode.email.Log.i("MMM Active")
synchronized(lock) {
queued.value++
queryExecutor.execute(refreshRunnable)
}
eu.faircode.email.Log.i("MMM Actived")
}
@Suppress("UNCHECKED_CAST")
override fun onInactive() {

View File

@ -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();

171
patches/roomkt.patch Normal file
View File

@ -0,0 +1,171 @@
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..e253e98eee 100644
--- a/app/src/main/java/androidx/room/RoomTrackingLiveData.kt
+++ b/app/src/main/java/androidx/room/RoomTrackingLiveData.kt
@@ -54,62 +54,72 @@ internal class RoomTrackingLiveData<T> (
val invalid = AtomicBoolean(true)
val computing = AtomicBoolean(false)
val registeredObserver = AtomicBoolean(false)
+ val queued = eu.faircode.email.ObjectHolder<Int>(0)
+ val lock = Object()
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())
- }
+ synchronized(lock) {
+ queued.value--
+ if (queued.value < 0) {
+ eu.faircode.email.Log.e("$computeFunction queued=$queued.value")
+ queued.value = 0
+ }
+ }
+
+ if (registeredObserver.compareAndSet(false, true)) {
+ database.invalidationTracker.addWeakObserver(observer)
+ }
+
+ 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 (invalid.compareAndSet(false, true)) {
+ if (isActive) {
+ synchronized(lock) {
+ if (queued.value > 0) {
+ eu.faircode.email.Log.persist(eu.faircode.email.EntityLog.Type.Debug, "$computeFunction queued=$queued.value")
+ } else {
+ queued.value++
+ 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>)
+ synchronized(lock) {
+ queued.value++
+ 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();