11package org .ldk .batteries ;
22
3+ import org .ldk .impl .bindings ;
34import org .ldk .structs .*;
45
56import java .io .IOException ;
7+ import java .lang .reflect .Field ;
68import java .util .LinkedList ;
79import java .net .SocketAddress ;
810import java .net .StandardSocketOptions ;
1719public class NioPeerHandler {
1820 private static class Peer {
1921 SocketDescriptor descriptor ;
22+ long descriptor_raw_pointer ;
2023 SelectionKey key ;
2124 }
2225
@@ -45,6 +48,19 @@ private void do_selector_action(SelectorCall meth) throws IOException {
4548 }
4649 }
4750
51+ static private Field CommonBasePointer ;
52+ static {
53+ try {
54+ Class c = PeerManager .class .getSuperclass ();
55+ CommonBasePointer = c .getDeclaredField ("ptr" );
56+ CommonBasePointer .setAccessible (true );
57+ long _dummy_check = CommonBasePointer .getLong (Ping .of ((short )0 , (short )0 ));
58+ } catch (NoSuchFieldException | IllegalAccessException e ) {
59+ throw new IllegalArgumentException (
60+ "We currently use reflection to access protected fields as Java has no reasonable access controls" , e );
61+ }
62+ }
63+
4864 private Peer setup_socket (SocketChannel chan ) throws IOException {
4965 chan .configureBlocking (false );
5066 // Lightning tends to send a number of small messages back and forth between peers quickly, which Nagle is
@@ -89,6 +105,12 @@ public void disconnect_socket() {
89105 @ Override public long hash () { return our_id ; }
90106 });
91107 peer .descriptor = descriptor ;
108+ try {
109+ peer .descriptor_raw_pointer = CommonBasePointer .getLong (descriptor );
110+ } catch (IllegalAccessException e ) {
111+ throw new IllegalArgumentException (
112+ "We currently use reflection to access protected fields as Java has no reasonable access controls" , e );
113+ }
92114 return peer ;
93115 }
94116
@@ -108,7 +130,16 @@ public NioPeerHandler(PeerManager manager) throws IOException {
108130 this .peer_manager = manager ;
109131 this .selector = Selector .open ();
110132 io_thread = new Thread (() -> {
111- ByteBuffer buf = ByteBuffer .allocate (8192 );
133+ int BUF_SZ = 16 * 1024 ;
134+ byte [] max_buf_byte_object = new byte [BUF_SZ ];
135+ ByteBuffer buf = ByteBuffer .allocate (BUF_SZ );
136+
137+ long peer_manager_raw_pointer ;
138+ try {
139+ peer_manager_raw_pointer = CommonBasePointer .getLong (this .peer_manager );
140+ } catch (IllegalAccessException e ) {
141+ throw new RuntimeException (e );
142+ }
112143 while (true ) {
113144 try {
114145 if (IS_ANDROID ) {
@@ -168,17 +199,32 @@ public NioPeerHandler(PeerManager manager) throws IOException {
168199 key .cancel ();
169200 } else if (read > 0 ) {
170201 ((Buffer )buf ).flip ();
171- byte [] read_bytes = new byte [read ];
202+ // This code is quite hot during initial network graph sync, so we go a ways out of
203+ // our way to avoid object allocations that'll make the GC sweat later -
204+ // * when we're hot, we'll likely often be reading the full buffer, so we keep
205+ // around a full-buffer-sized byte array to reuse across reads,
206+ // * We use the manual memory management call logic directly in bindings instead of
207+ // the nice "human-readable" wrappers. This puts us at risk of memory issues,
208+ // so we indirectly ensure compile fails if the types change by writing the
209+ // "human-readable" form of the same code in the dummy function below.
210+ byte [] read_bytes ;
211+ if (read == BUF_SZ ) {
212+ read_bytes = max_buf_byte_object ;
213+ } else {
214+ read_bytes = new byte [read ];
215+ }
172216 buf .get (read_bytes , 0 , read );
173- Result_boolPeerHandleErrorZ res = this .peer_manager .read_event (peer .descriptor , read_bytes );
174- if (res instanceof Result_boolPeerHandleErrorZ .Result_boolPeerHandleErrorZ_OK ) {
175- if (((Result_boolPeerHandleErrorZ .Result_boolPeerHandleErrorZ_OK ) res ).res ) {
217+ long read_result_pointer = bindings .PeerManager_read_event (
218+ peer_manager_raw_pointer , peer .descriptor_raw_pointer , read_bytes );
219+ if (bindings .LDKCResult_boolPeerHandleErrorZ_result_ok (read_result_pointer )) {
220+ if (bindings .LDKCResult_boolPeerHandleErrorZ_get_ok (read_result_pointer )) {
176221 key .interestOps (key .interestOps () & (~SelectionKey .OP_READ ));
177222 }
178223 } else {
179224 key .channel ().close ();
180225 key .cancel ();
181226 }
227+ bindings .CResult_boolPeerHandleErrorZ_free (read_result_pointer );
182228 }
183229 }
184230 } catch (IOException ignored ) {
@@ -198,6 +244,13 @@ public NioPeerHandler(PeerManager manager) throws IOException {
198244 io_thread .start ();
199245 }
200246
247+ // Ensure the types used in the above manual code match what they were when the code was written.
248+ // Ensure the above manual bindings.* code changes if this fails to compile.
249+ private void dummy_check_return_type_matches_manual_memory_code_above (Peer peer ) {
250+ byte [] read_bytes = new byte [32 ];
251+ Result_boolPeerHandleErrorZ res = this .peer_manager .read_event (peer .descriptor , read_bytes );
252+ }
253+
201254 /**
202255 * Connect to a peer given their node id and socket address. Blocks until a connection is established (or returns
203256 * IOException) and then the connection handling runs in the background.
0 commit comments