InterProcessCoordinator.kt
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.datastore.core
import kotlinx.coroutines.flow.Flow
/**
* InterProcessCoordinator provides functionalities that support DataStore instances to coordinate
* the concurrent work running on multiple threads and multiple processes to guarantee its data
* consistency. Typically users should use default coordinators provided by the library, including
* [SingleProcessCoordinator] for use cases where DataStore is only used in a single process, and
* [MultiProcessCoordinator] for a DataStore that needs to be accessed in multiple processes.
*/
interface InterProcessCoordinator {
/**
* A flow that emits a Unit when the data for the DataStore changes. [DataStore] collects this
* flow to signal the action to invalidate cache and re-read data from disk.
*/
val updateNotifications: Flow<Unit>
/**
* Get the exclusive lock shared by the coordinators from DataStore instances (even from
* different processes) to run a suspending code [block] that returns type `T`. It guarantees
* one-at-a-time execution for all the [block] called with this method. If some other process
* or thread is holding the lock, it will wait until the lock is available.
*
* @param block The block of code that is performed with the lock resource.
*/
suspend fun <T> lock(block: suspend () -> T): T
/**
* Attempt to get the exclusive lock shared by the coordinators from DataStore instances (even
* from different processes) and run the code [block] regardless of the attempt result. Pass a
* boolean to [block] to indicate if the attempt succeeds. If the attempt fails, [block] will
* run immediately after the attempt, without waiting for the lock to become available.
*
* @param block The block of code that is performed after attempting to get the lock resource.
* Block will receive a Boolean parameter which is true if the try lock succeeded.
*/
suspend fun <T> tryLock(block: suspend (Boolean) -> T): T
/**
* Atomically get the current version. [DataStore] instances for the same data use this method
* to access the shared version for its cached data and internal state. Notice concurrent access
* to the version should guarantee data consistency.
*/
suspend fun getVersion(): Int
/**
* Atomically increment version and return the new version. [DataStore] instances for the same
* data use this method to access the shared version for its cached data and internal state.
* Notice concurrent access to the version should guarantee data consistency.
*
* Note that the number of calls to the `incrementAndGetVersion` is an internal implementation
* detail for DataStore and implementers of this API should not make any assumption based on the
* number of version increments.
*/
suspend fun incrementAndGetVersion(): Int
}
/**
* Create a coordinator for single process use cases.
*/
fun createSingleProcessCoordinator(): InterProcessCoordinator = SingleProcessCoordinator()