1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.paging 18 19 import androidx.annotation.VisibleForTesting 20 import androidx.paging.internal.ReentrantLock 21 import androidx.paging.internal.withLock 22 23 /** Helper class for thread-safe invalidation callback tracking + triggering on registration. */ 24 internal class InvalidateCallbackTracker<T>( 25 private val callbackInvoker: (T) -> Unit, 26 /** User-provided override of DataSource.isInvalid */ 27 private val invalidGetter: (() -> Boolean)? = null, 28 ) { 29 private val lock = ReentrantLock() 30 private val callbacks = mutableListOf<T>() 31 internal var invalid = false 32 private set 33 callbackCountnull34 @VisibleForTesting internal fun callbackCount() = callbacks.size 35 36 internal fun registerInvalidatedCallback(callback: T) { 37 // This isn't sufficient, but is the best we can do in cases where DataSource.isInvalid 38 // is overridden, since we have no way of knowing when the result gets flipped if user 39 // never calls .invalidate(). 40 if (invalidGetter?.invoke() == true) { 41 invalidate() 42 } 43 44 if (invalid) { 45 callbackInvoker(callback) 46 return 47 } 48 49 val callImmediately = 50 lock.withLock { 51 if (invalid) { 52 true // call immediately 53 } else { 54 callbacks.add(callback) 55 false // don't call, not invalid yet. 56 } 57 } 58 59 if (callImmediately) { 60 callbackInvoker(callback) 61 } 62 } 63 unregisterInvalidatedCallbacknull64 internal fun unregisterInvalidatedCallback(callback: T) { 65 lock.withLock { callbacks.remove(callback) } 66 } 67 invalidatenull68 internal fun invalidate(): Boolean { 69 if (invalid) return false 70 71 val callbacksToInvoke = 72 lock.withLock { 73 if (invalid) return false 74 75 invalid = true 76 callbacks.toList().also { callbacks.clear() } 77 } 78 79 callbacksToInvoke.forEach(callbackInvoker) 80 return true 81 } 82 } 83