Skip to content

Commit a5b2ad0

Browse files
committed
coroutines: only let async propagate exceptions in one direction
If we don't do this, catching the exception isn't enough; it'll still bubble to its parent and kill everything. See Kotlin/kotlinx.coroutines#763 for an extended discussion of Kotlin misdesigns. Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent 47c0100 commit a5b2ad0

File tree

4 files changed

+15
-6
lines changed

4 files changed

+15
-6
lines changed

ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt

+5-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import com.wireguard.config.Config
3939
import kotlinx.coroutines.CoroutineScope
4040
import kotlinx.coroutines.Deferred
4141
import kotlinx.coroutines.Dispatchers
42+
import kotlinx.coroutines.Job
43+
import kotlinx.coroutines.SupervisorJob
4244
import kotlinx.coroutines.async
4345
import kotlinx.coroutines.awaitAll
4446
import kotlinx.coroutines.launch
@@ -132,12 +134,12 @@ class TunnelListFragment : BaseFragment() {
132134
null
133135
}?.let {
134136
val nameCopy = name
135-
futureTunnels.add(async { Application.getTunnelManager().create(nameCopy, it) })
137+
futureTunnels.add(async(SupervisorJob(coroutineContext[Job])) { Application.getTunnelManager().create(nameCopy, it) })
136138
}
137139
}
138140
}
139141
} else {
140-
futureTunnels.add(async { Application.getTunnelManager().create(name, Config.parse(contentResolver.openInputStream(uri)!!)) })
142+
futureTunnels.add(async(SupervisorJob(coroutineContext[Job])) { Application.getTunnelManager().create(name, Config.parse(contentResolver.openInputStream(uri)!!)) })
141143
}
142144

143145
if (futureTunnels.isEmpty()) {
@@ -321,7 +323,7 @@ class TunnelListFragment : BaseFragment() {
321323
val tunnels = Application.getTunnelManager().tunnels.await()
322324
val tunnelsToDelete = ArrayList<ObservableTunnel>()
323325
for (position in copyCheckedItems) tunnelsToDelete.add(tunnels[position])
324-
val futures = tunnelsToDelete.map { async { it.delete() } }
326+
val futures = tunnelsToDelete.map { async(SupervisorJob(coroutineContext[Job])) { it.delete() } }
325327
onTunnelDeletionFinished(futures.awaitAll().size, null)
326328
} catch (e: Throwable) {
327329
onTunnelDeletionFinished(0, e)

ui/src/main/java/com/wireguard/android/model/TunnelManager.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ import com.wireguard.config.Config
2626
import kotlinx.coroutines.CompletableDeferred
2727
import kotlinx.coroutines.CoroutineScope
2828
import kotlinx.coroutines.Dispatchers
29+
import kotlinx.coroutines.Job
30+
import kotlinx.coroutines.SupervisorJob
2931
import kotlinx.coroutines.async
32+
import kotlinx.coroutines.awaitAll
3033
import kotlinx.coroutines.launch
3134
import kotlinx.coroutines.withContext
3235

@@ -139,7 +142,7 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
139142
coroutineScope.launch {
140143
withContext(Dispatchers.IO) {
141144
try {
142-
tunnelMap.filter { previouslyRunning.contains(it.name) }.map { async { setTunnelState(it, Tunnel.State.UP) } }.map { it.await() }
145+
tunnelMap.filter { previouslyRunning.contains(it.name) }.map { async(SupervisorJob(coroutineContext[Job])) { setTunnelState(it, Tunnel.State.UP) } }.awaitAll()
143146
} catch (e: Throwable) {
144147
Log.println(Log.ERROR, TAG, Log.getStackTraceString(e))
145148
}

ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import com.wireguard.android.backend.Tunnel
1717
import com.wireguard.android.backend.WgQuickBackend
1818
import kotlinx.coroutines.CoroutineScope
1919
import kotlinx.coroutines.Dispatchers
20+
import kotlinx.coroutines.Job
21+
import kotlinx.coroutines.SupervisorJob
2022
import kotlinx.coroutines.async
2123
import kotlinx.coroutines.awaitAll
2224
import kotlinx.coroutines.launch
@@ -49,7 +51,7 @@ class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : P
4951
}
5052
coroutineScope.launch {
5153
val observableTunnels = Application.getTunnelManager().tunnels.await()
52-
val downings = observableTunnels.map { async { it.setStateAsync(Tunnel.State.DOWN) } }
54+
val downings = observableTunnels.map { async(SupervisorJob(coroutineContext[Job])) { it.setStateAsync(Tunnel.State.DOWN) } }
5355
try {
5456
downings.awaitAll()
5557
withContext(Dispatchers.IO) {

ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import com.wireguard.android.util.ErrorMessages
2121
import com.wireguard.android.util.FragmentUtils
2222
import kotlinx.coroutines.CoroutineScope
2323
import kotlinx.coroutines.Dispatchers
24+
import kotlinx.coroutines.Job
25+
import kotlinx.coroutines.SupervisorJob
2426
import kotlinx.coroutines.async
2527
import kotlinx.coroutines.awaitAll
2628
import kotlinx.coroutines.launch
@@ -45,7 +47,7 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
4547
try {
4648
val name: String
4749
withContext(Dispatchers.IO) {
48-
val configs = tunnels.map { async { it.getConfigAsync() } }.awaitAll()
50+
val configs = tunnels.map { async(SupervisorJob(coroutineContext[Job])) { it.getConfigAsync() } }.awaitAll()
4951
if (configs.isEmpty()) {
5052
throw IllegalArgumentException(context.getString(R.string.no_tunnels_error))
5153
}

0 commit comments

Comments
 (0)