Skip to content

Commit 1e9caa4

Browse files
committed
feat: Enhance DefaultNetworkListener to await network processing completion
1 parent 1a06d23 commit 1e9caa4

2 files changed

Lines changed: 23 additions & 8 deletions

File tree

app/src/main/java/io/nekohasekai/sagernet/utils/DefaultNetworkListener.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import java.net.UnknownHostException
2020

2121
object DefaultNetworkListener {
2222
private sealed class NetworkMessage {
23-
class Start(val key: Any, val listener: (Network?) -> Unit) : NetworkMessage()
23+
class Start(val key: Any, val listener: (Network?) -> Unit) : NetworkMessage() {
24+
val processed = CompletableDeferred<Unit>()
25+
}
2426
class Get : NetworkMessage() {
2527
val response = CompletableDeferred<Network>()
2628
}
@@ -38,9 +40,16 @@ object DefaultNetworkListener {
3840
val pendingRequests = arrayListOf<NetworkMessage.Get>()
3941
for (message in channel) when (message) {
4042
is NetworkMessage.Start -> {
41-
if (listeners.isEmpty()) register()
42-
listeners[message.key] = message.listener
43-
if (network != null) message.listener(network)
43+
try {
44+
if (listeners.isEmpty()) register()
45+
listeners[message.key] = message.listener
46+
if (network != null) message.listener(network)
47+
message.processed.complete(Unit)
48+
} catch (error: Throwable) {
49+
val removed = listeners.remove(message.key) != null
50+
if (removed && listeners.isEmpty()) runCatching { unregister() }
51+
message.processed.completeExceptionally(error)
52+
}
4453
}
4554
is NetworkMessage.Get -> {
4655
check(listeners.isNotEmpty()) { "Getting network without any listeners is not supported" }
@@ -84,8 +93,14 @@ object DefaultNetworkListener {
8493
}
8594
}
8695

87-
suspend fun start(key: Any, listener: (Network?) -> Unit) =
88-
networkActor.send(NetworkMessage.Start(key, listener))
96+
suspend fun start(key: Any, listener: (Network?) -> Unit) {
97+
val message = NetworkMessage.Start(key, listener)
98+
networkActor.send(message)
99+
// send 只保证消息进入 actor,不保证 Start 分支和缓存网络的首次回调已经完成。
100+
// 必须等待 processed,否则并发创建测试 Box 时 Go monitor 可能以 default=nil 返回,
101+
// 随后的首拨会抢在 updateDefaultInterface(wlan0, index) 之前并立即失败。
102+
message.processed.await()
103+
}
89104

90105
suspend fun get() = if (fallback) @TargetApi(23) {
91106
SagerNet.connectivity.activeNetwork

app/src/main/java/moe/matsuri/nb4a/NativeInterface.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class NativeInterface : BoxPlatformInterface, NB4AInterface {
100100
// "no available network interface"(见 libcore/interface_monitor.go 批注)。
101101
// 原先 runOnDefaultDispatcher 异步注册,测试盒 box.Start() 后立刻拨号,
102102
// 首拨几乎必然抢在首次回调之前 → 批量测速大面积"超时"。
103-
// DefaultNetworkListener actor 是 Dispatchers.Unconfined,send 内联处理,
104-
// 缓存命中时首次回调在此调用返回前即完成(调用的 Go 线程短暂阻塞,可接受)。
103+
// DefaultNetworkListener.start 会等待 actor 处理 Start;缓存命中时还会等待
104+
// 首次回调及 Go updateDefaultInterface 完成后才返回(Go 线程短暂阻塞,可接受)。
105105
runBlocking {
106106
DefaultNetworkListener.start(listener) { network ->
107107
checkDefaultInterfaceUpdate(listener, network)

0 commit comments

Comments
 (0)