1 /*
2  * Copyright 2023 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.datastore.core
18 
19 import androidx.annotation.RestrictTo
20 import kotlinx.coroutines.flow.Flow
21 
22 /**
23  * InterProcessCoordinator provides functionalities that support DataStore instances to coordinate
24  * the concurrent work running on multiple threads and multiple processes to guarantee its data
25  * consistency. Typically users should use default coordinators provided by the library, including
26  * [createSingleProcessCoordinator] for use cases where DataStore is only used in a single process,
27  * and [createMultiProcessCoordinator] for a DataStore that needs to be accessed in multiple
28  * processes.
29  */
30 interface InterProcessCoordinator {
31 
32     /**
33      * A flow that emits a Unit when the data for the DataStore changes. [DataStore] collects this
34      * flow to signal the action to invalidate cache and re-read data from disk.
35      */
36     val updateNotifications: Flow<Unit>
37 
38     /**
39      * Get the exclusive lock shared by the coordinators from DataStore instances (even from
40      * different processes) to run a suspending code [block] that returns type `T`. It guarantees
41      * one-at-a-time execution for all the [block] called with this method. If some other process or
42      * thread is holding the lock, it will wait until the lock is available.
43      *
44      * @param block The block of code that is performed with the lock resource.
45      */
locknull46     suspend fun <T> lock(block: suspend () -> T): T
47 
48     /**
49      * Attempt to get the exclusive lock shared by the coordinators from DataStore instances (even
50      * from different processes) and run the code [block] regardless of the attempt result. Pass a
51      * boolean to [block] to indicate if the attempt succeeds. If the attempt fails, [block] will
52      * run immediately after the attempt, without waiting for the lock to become available.
53      *
54      * @param block The block of code that is performed after attempting to get the lock resource.
55      *   Block will receive a Boolean parameter which is true if the try lock succeeded.
56      */
57     suspend fun <T> tryLock(block: suspend (Boolean) -> T): T
58 
59     /**
60      * Atomically get the current version. [DataStore] instances for the same data use this method
61      * to access the shared version for its cached data and internal state. Notice concurrent access
62      * to the version should guarantee data consistency.
63      */
64     suspend fun getVersion(): Int
65 
66     /**
67      * Atomically increment version and return the new version. [DataStore] instances for the same
68      * data use this method to access the shared version for its cached data and internal state.
69      * Notice concurrent access to the version should guarantee data consistency.
70      *
71      * Note that the number of calls to the `incrementAndGetVersion` is an internal implementation
72      * detail for DataStore and implementers of this API should not make any assumption based on the
73      * number of version increments.
74      */
75     suspend fun incrementAndGetVersion(): Int
76 }
77 
78 /**
79  * Create a coordinator for single process use cases.
80  *
81  * @param filePath The canonical file path of the file managed by [SingleProcessCoordinator]
82  */
83 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
84 fun createSingleProcessCoordinator(filePath: String): InterProcessCoordinator =
85     SingleProcessCoordinator(filePath)
86