From f1a0db5b672b7caa66790c1d379265c45d5e934f Mon Sep 17 00:00:00 2001 From: SaidAlaoui Date: Mon, 2 Nov 2026 12:20:05 +0000 Subject: [PATCH] batteries: Close the temporary Selector in NioPeerHandler.connect `connect` opens a `Selector` to wait for `OP_CONNECT` and never closes it. Each call leaks the selector's fds and native memory. The socket's fd also stays open after the channel is closed, because the leaked selector still holds a key for the channel, and the JDK releases a channel's fd only once no selector has it registered. On Linux this is 3 fds per call (epoll fd, eventfd and the socket), on both failed connects and connects the peer later drops. A peer that drops every connection, or an application that retries connects in a loop, runs the process out of fds. Close the selector with try-with-resources. Closing it removes only its own key, so the later `register(this.selector, ...)` is unaffected. --- src/main/java/org/ldk/batteries/NioPeerHandler.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/ldk/batteries/NioPeerHandler.java b/src/main/java/org/ldk/batteries/NioPeerHandler.java index 8bd5172c..5f8c8051 100644 --- a/src/main/java/org/ldk/batteries/NioPeerHandler.java +++ b/src/main/java/org/ldk/batteries/NioPeerHandler.java @@ -266,12 +266,13 @@ public void connect(byte[] their_node_id, java.net.SocketAddress remote, int tim boolean connected; try { chan.configureBlocking(false); - Selector open_selector = Selector.open(); - chan.register(open_selector, SelectionKey.OP_CONNECT); - if (!chan.connect(remote)) { - open_selector.select(timeout_ms); + try (Selector open_selector = Selector.open()) { + chan.register(open_selector, SelectionKey.OP_CONNECT); + if (!chan.connect(remote)) { + open_selector.select(timeout_ms); + } + connected = chan.finishConnect(); } - connected = chan.finishConnect(); } catch (IOException e) { try { chan.close(); } catch (IOException _e) { } throw e;