|
1 | 1 | package org.mobilenativefoundation.store.store5.util |
2 | 2 |
|
3 | | -import kotlinx.coroutines.channels.BroadcastChannel |
4 | | -import kotlinx.coroutines.channels.Channel |
| 3 | +import kotlinx.coroutines.NonCancellable |
| 4 | +import kotlinx.coroutines.channels.BufferOverflow |
5 | 5 | import kotlinx.coroutines.flow.Flow |
| 6 | +import kotlinx.coroutines.flow.MutableSharedFlow |
6 | 7 | import kotlinx.coroutines.flow.emitAll |
7 | 8 | import kotlinx.coroutines.flow.flow |
8 | 9 | import kotlinx.coroutines.sync.Mutex |
9 | 10 | import kotlinx.coroutines.sync.withLock |
| 11 | +import kotlinx.coroutines.withContext |
10 | 12 | import org.mobilenativefoundation.store.store5.SourceOfTruth |
11 | 13 |
|
12 | 14 | /** |
@@ -58,75 +60,68 @@ fun <Key : Any, Output : Any> SimplePersisterAsFlowable<Key, Output>.asSourceOfT |
58 | 60 | internal class KeyTracker<Key> { |
59 | 61 | private val lock = Mutex() |
60 | 62 |
|
61 | | - // list of open key channels |
62 | | - private val channels = mutableMapOf<Key, KeyChannel>() |
| 63 | + // list of open key flows |
| 64 | + private val flows = mutableMapOf<Key, KeyFlow>() |
63 | 65 |
|
64 | 66 | // for testing |
65 | | - internal fun activeKeyCount() = channels.size |
| 67 | + internal fun activeKeyCount() = flows.size |
66 | 68 |
|
67 | 69 | /** |
68 | 70 | * invalidates the given key. If there are flows returned from [keyFlow] for the given [key], |
69 | 71 | * they'll receive a new emission |
70 | 72 | */ |
71 | 73 | suspend fun invalidate(key: Key) { |
72 | 74 | lock.withLock { |
73 | | - channels[key] |
74 | | - }?.channel?.send(Unit) |
| 75 | + flows[key] |
| 76 | + }?.flow?.emit(Unit) |
75 | 77 | } |
76 | 78 |
|
77 | 79 | /** |
78 | 80 | * Returns a Flow that emits once and then every time the given [key] is invalidated via |
79 | 81 | * [invalidate] |
80 | 82 | */ |
81 | 83 | suspend fun keyFlow(key: Key): Flow<Unit> { |
82 | | - // it is important to allocate KeyChannel lazily (ony when the returned flow is collected |
| 84 | + // it is important to allocate KeyFlow lazily (ony when the returned flow is collected |
83 | 85 | // from). Otherwise, we might just create many of them that are never observed hence never |
84 | 86 | // cleaned up |
85 | 87 | return flow { |
86 | | - val keyChannel = |
| 88 | + val keyFlow = |
87 | 89 | lock.withLock { |
88 | | - channels.getOrPut(key) { |
89 | | - KeyChannel( |
90 | | - channel = |
91 | | - BroadcastChannel<Unit>(Channel.CONFLATED).apply { |
92 | | - // start w/ an initial value. |
93 | | - trySend(Unit).isSuccess |
94 | | - }, |
95 | | - ) |
96 | | - }.also { |
97 | | - it.acquire() // refcount |
| 90 | + flows.getOrPut(key) { KeyFlow() }.also { |
| 91 | + it.acquire() |
98 | 92 | } |
99 | 93 | } |
| 94 | + emit(Unit) |
100 | 95 | try { |
101 | | - emitAll(keyChannel.channel.openSubscription()) |
| 96 | + emitAll(keyFlow.flow) |
102 | 97 | } finally { |
103 | | - lock.withLock { |
104 | | - keyChannel.release() |
105 | | - if (keyChannel.channel.isClosedForSend) { |
106 | | - channels.remove(key) |
| 98 | + withContext(NonCancellable) { |
| 99 | + lock.withLock { |
| 100 | + if (keyFlow.release()) { |
| 101 | + flows.remove(key) |
| 102 | + } |
107 | 103 | } |
108 | 104 | } |
109 | 105 | } |
110 | 106 | } |
111 | 107 | } |
112 | 108 |
|
113 | 109 | /** |
114 | | - * A data structure to count how many active flows we have on this channel |
| 110 | + * A data structure to count how many active flows we have on this flow |
115 | 111 | */ |
116 | | - private data class KeyChannel( |
117 | | - val channel: BroadcastChannel<Unit>, |
118 | | - var collectors: Int = 0, |
119 | | - ) { |
| 112 | + private class KeyFlow { |
| 113 | + val flow = |
| 114 | + MutableSharedFlow<Unit>( |
| 115 | + extraBufferCapacity = 1, |
| 116 | + onBufferOverflow = BufferOverflow.DROP_OLDEST, |
| 117 | + ) |
| 118 | + private var collectors: Int = 0 |
| 119 | + |
120 | 120 | fun acquire() { |
121 | 121 | collectors++ |
122 | 122 | } |
123 | 123 |
|
124 | | - fun release() { |
125 | | - collectors-- |
126 | | - if (collectors == 0) { |
127 | | - channel.close() |
128 | | - } |
129 | | - } |
| 124 | + fun release() = (--collectors) == 0 |
130 | 125 | } |
131 | 126 | } |
132 | 127 |
|
|
0 commit comments