diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-12-01 22:50:45 +0000 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-12-03 10:46:15 +0000 |
| commit | 3e67a82c94012d715d4bf6e1e0b63156a4820054 (patch) | |
| tree | e45584bf64f96079581f02a2e33f7620b5a9aa91 /android/src | |
| parent | fe7c6370abe2b6bcb8252bff1864ee783ced94aa (diff) | |
| download | mullvadvpn-3e67a82c94012d715d4bf6e1e0b63156a4820054.tar.xz mullvadvpn-3e67a82c94012d715d4bf6e1e0b63156a4820054.zip | |
Refactor `LocationInfoCache` into an actor
Diffstat (limited to 'android/src')
| -rw-r--r-- | android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt | 103 |
1 files changed, 52 insertions, 51 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt index 38e0dc209a..b123271fef 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/LocationInfoCache.kt @@ -2,10 +2,13 @@ package net.mullvad.mullvadvpn.service import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ClosedReceiveChannelException +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.sendBlocking +import kotlinx.coroutines.withTimeout import net.mullvad.mullvadvpn.model.GeoIpLocation import net.mullvad.mullvadvpn.model.TunnelState import net.mullvad.mullvadvpn.relaylist.Relay @@ -21,13 +24,11 @@ class LocationInfoCache( val connectionProxy: ConnectionProxy, val connectivityListener: ConnectivityListener ) { - private var activeFetch: Job? = null + private val fetchRequestChannel = runFetcher() + private var lastKnownRealLocation: GeoIpLocation? = null private var selectedRelayLocation: GeoIpLocation? = null - private var fetchIdCounter = 0L - private var fetchIdIsActive = false - var onNewLocation: ((GeoIpLocation?) -> Unit)? = null set(value) { field = value @@ -43,17 +44,16 @@ class LocationInfoCache( var state: TunnelState = TunnelState.Disconnected() set(value) { field = value - cancelFetch() when (value) { is TunnelState.Disconnected -> { location = lastKnownRealLocation - fetchLocation(true) + fetchRequestChannel.sendBlocking(true) } is TunnelState.Connecting -> location = value.location is TunnelState.Connected -> { location = value.location - fetchLocation(false) + fetchRequestChannel.sendBlocking(false) } is TunnelState.Disconnecting -> { when (value.actionAfterDisconnect) { @@ -77,7 +77,7 @@ class LocationInfoCache( init { connectivityListener.connectivityNotifier.subscribe(this) { isConnected -> if (isConnected && state is TunnelState.Disconnected) { - fetchLocation(true) + fetchRequestChannel.sendBlocking(true) } } @@ -89,7 +89,7 @@ class LocationInfoCache( fun onDestroy() { connectivityListener.connectivityNotifier.unsubscribe(this) connectionProxy.onStateChange.unsubscribe(this) - cancelFetch() + fetchRequestChannel.close() } private fun updateSelectedRelayLocation(relayItem: RelayItem?) { @@ -113,56 +113,57 @@ class LocationInfoCache( } } - private fun newFetchId(): Long { - synchronized(this) { - if (fetchIdIsActive) { - fetchIdCounter += 1 - } else { - fetchIdIsActive = true - } - - return fetchIdCounter + private fun runFetcher() = GlobalScope.actor<Boolean>(Dispatchers.Default, Channel.CONFLATED) { + try { + fetcherLoop(channel) + } catch (exception: ClosedReceiveChannelException) { } } - private fun cancelFetch() { - synchronized(this) { - if (fetchIdIsActive) { - fetchIdCounter += 1 - fetchIdIsActive = false - } + private suspend fun fetcherLoop(channel: ReceiveChannel<Boolean>) { + val delays = ExponentialBackoff().apply { + scale = 50 + cap = 30 /* min */ * 60 /* s */ * 1000 /* ms */ + count = 17 // ceil(log2(cap / scale) + 1) } - } - private fun fetchLocation(isRealLocation: Boolean) { - val fetchId = newFetchId() - val previousFetch = activeFetch + while (true) { + var isRealLocation = channel.receive() + var newLocation = daemon.getCurrentLocation() - activeFetch = GlobalScope.launch(Dispatchers.Default) { - val delays = ExponentialBackoff().apply { - scale = 50 - cap = 30 /* min */ * 60 /* s */ * 1000 /* ms */ - count = 17 // ceil(log2(cap / scale) + 1) + while (newLocation == null || !channel.isEmpty) { + isRealLocation = delayOrReceive(delays, channel, isRealLocation) + newLocation = daemon.getCurrentLocation() } - var newLocation: GeoIpLocation? = null - - previousFetch?.join() + handleNewLocation(newLocation, isRealLocation) + delays.reset() + } + } - while (newLocation == null && fetchId == fetchIdCounter) { - delay(delays.next()) - newLocation = daemon.getCurrentLocation() + private suspend fun delayOrReceive( + delays: ExponentialBackoff, + channel: ReceiveChannel<Boolean>, + currentValue: Boolean + ): Boolean { + try { + val newValue = withTimeout(delays.next()) { + channel.receive() } - synchronized(this@LocationInfoCache) { - if (newLocation != null && fetchId == fetchIdCounter) { - location = newLocation + delays.reset() - if (isRealLocation) { - lastKnownRealLocation = newLocation - } - } - } + return newValue + } catch (timeOut: TimeoutCancellationException) { + return currentValue } } + + private fun handleNewLocation(newLocation: GeoIpLocation, isRealLocation: Boolean) { + if (isRealLocation) { + lastKnownRealLocation = newLocation + } + + location = newLocation + } } |
