|
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