Skip to content

Commit f8c25e5

Browse files
authored
Merge pull request #12 from TheBlueMatt/2021-03-fresh-bindings
Update bindings to latest upstream + Add CI
2 parents bbc02b4 + 0064602 commit f8c25e5

40 files changed

+1031
-397
lines changed

.github/workflows/build.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Continuous Integration Checks
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
check_bindings:
7+
runs-on: ubuntu-latest
8+
# Ubuntu's version of rustc uses its own LLVM instead of being a real native package.
9+
# This leaves us with an incompatible LLVM version when linking. Instead, use a real OS.
10+
container: debian:bullseye
11+
env:
12+
TOOLCHAIN: stable
13+
steps:
14+
- name: Install native Rust toolchain, Valgrind, and build utilitis
15+
run: |
16+
apt-get update
17+
apt-get -y dist-upgrade
18+
apt-get -y install cargo libstd-rust-dev-wasm32 valgrind lld git g++ clang openjdk-11-jdk maven
19+
- name: Checkout source code
20+
uses: actions/checkout@v2
21+
- name: Install cbindgen
22+
run: cargo install --force cbindgen
23+
- name: Checkout Rust-Lightning and LDK-C-Bindings git
24+
run: |
25+
git clone https://github.com/rust-bitcoin/rust-lightning
26+
cd rust-lightning
27+
git remote add matt https://git.bitcoin.ninja/rust-lightning
28+
git fetch matt
29+
git merge matt/2021-03-java-bindings-base
30+
cd ..
31+
git clone https://github.com/lightningdevkit/ldk-c-bindings
32+
- name: Rebuild C bindings, and check the sample app builds + links
33+
run: cd ldk-c-bindings && ./genbindings.sh ../rust-lightning && cd ..
34+
- name: Build Java/TS Debug Bindings
35+
run: ./genbindings.sh ./ldk-c-bindings/ "-I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/" true false
36+
- name: Run Java Tests against Debug Bindings
37+
run: |
38+
rm liblightningjni.so
39+
ln -s liblightningjni_debug.so liblightningjni.so
40+
export LD_LIBRARY_PATH=.
41+
export LD_PRELOAD=/usr/lib/llvm-11/lib/clang/11.0.1/lib/linux/libclang_rt.asan-x86_64.so
42+
export ASAN_OPTIONS=detect_leaks=0
43+
mvn test
44+
git checkout liblightningjni.so
45+
- name: Build Java/TS Release Bindings
46+
run: ./genbindings.sh ./ldk-c-bindings/ "-I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/" false false
47+
- name: Check latest headers are in git
48+
run: |
49+
git checkout liblightningjni_debug.so
50+
git checkout liblightningjni_release.so
51+
git diff --exit-code

genbindings.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ usage() {
66
echo "android should either be true or false"
77
exit 1
88
}
9-
[ "$1" = "" -o "$2" = "" ] && usage
9+
[ "$1" = "" ] && usage
1010
[ "$3" != "true" -a "$3" != "false" ] && usage
1111
[ "$4" != "true" -a "$4" != "false" ] && usage
1212

java_strings.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,10 @@ def c_fn_name_define_pfx(self, fn_name, has_args):
391391

392392
def init_str(self):
393393
res = ""
394-
for ty in self.c_array_class_caches:
394+
for ty in sorted(self.c_array_class_caches):
395395
res = res + "static jclass " + ty + "_clz = NULL;\n"
396396
res = res + "JNIEXPORT void Java_org_ldk_impl_bindings_init_1class_1cache(JNIEnv * env, jclass clz) {\n"
397-
for ty in self.c_array_class_caches:
397+
for ty in sorted(self.c_array_class_caches):
398398
res = res + "\t" + ty + "_clz = (*env)->FindClass(env, \"" + ty.replace("arr_of_", "[") + "\");\n"
399399
res = res + "\tCHECK(" + ty + "_clz != NULL);\n"
400400
res = res + "\t" + ty + "_clz = (*env)->NewGlobalRef(env, " + ty + "_clz);\n"
@@ -638,7 +638,7 @@ def native_c_map_trait(self, struct_name, field_vars, field_fns, trait_doc_comme
638638
for idx, fn_line in enumerate(field_fns):
639639
if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
640640
assert fn_line.ret_ty_info.ty_info.get_full_rust_ty()[1] == ""
641-
out_c = out_c + fn_line.ret_ty_info.ty_info.get_full_rust_ty()[0] + " " + fn_line.fn_name + "_jcall("
641+
out_c = out_c + fn_line.ret_ty_info.ty_info.get_full_rust_ty()[0] + " " + fn_line.fn_name + "_" + struct_name + "_jcall("
642642
if fn_line.self_is_const:
643643
out_c = out_c + "const void* this_arg"
644644
else:
@@ -714,7 +714,7 @@ def native_c_map_trait(self, struct_name, field_vars, field_fns, trait_doc_comme
714714
out_c = out_c + "\t\t.this_arg = (void*) calls,\n"
715715
for fn_line in field_fns:
716716
if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
717-
out_c = out_c + "\t\t." + fn_line.fn_name + " = " + fn_line.fn_name + "_jcall,\n"
717+
out_c = out_c + "\t\t." + fn_line.fn_name + " = " + fn_line.fn_name + "_" + struct_name + "_jcall,\n"
718718
elif fn_line.fn_name == "free":
719719
out_c = out_c + "\t\t.free = " + struct_name + "_JCalls_free,\n"
720720
else:

liblightningjni_debug.so

341 KB
Binary file not shown.

liblightningjni_release.so

10.5 KB
Binary file not shown.

src/main/java/org/ldk/batteries/NioPeerHandler.java

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.ldk.batteries;
22

3+
import org.ldk.impl.bindings;
34
import org.ldk.structs.*;
45

56
import java.io.IOException;
@@ -10,6 +11,7 @@
1011
import java.nio.channels.Selector;
1112
import java.nio.channels.ServerSocketChannel;
1213
import java.nio.channels.SocketChannel;
14+
import java.util.concurrent.Callable;
1315

1416
/**
1517
* A NioPeerHandler maps LDK's PeerHandler to Java's NIO I/O interface. It spawns a single background thread which
@@ -26,6 +28,31 @@ private static class Peer {
2628
SelectionKey key;
2729
}
2830

31+
// Android's java.nio implementation has a big lock inside the selector, preventing any concurrent access to it.
32+
// This appears to largely defeat the entire purpose of java.nio, but we work around it here by explicitly checking
33+
// for an Android environment and passing any selector access on any thread other than our internal one through
34+
// do_selector_action, which wakes up the selector before accessing it.
35+
private static boolean IS_ANDROID;
36+
static {
37+
IS_ANDROID = System.getProperty("java.vendor").toLowerCase().contains("android");
38+
}
39+
private boolean wakeup_selector = false;
40+
private interface SelectorCall {
41+
void meth() throws IOException;
42+
}
43+
private void do_selector_action(SelectorCall meth) throws IOException {
44+
if (IS_ANDROID) {
45+
wakeup_selector = true;
46+
this.selector.wakeup();
47+
synchronized (this.selector) {
48+
meth.meth();
49+
wakeup_selector = false;
50+
}
51+
} else {
52+
meth.meth();
53+
}
54+
}
55+
2956
private Peer setup_socket(SocketChannel chan) throws IOException {
3057
chan.configureBlocking(false);
3158
// Lightning tends to send a number of small messages back and forth between peers quickly, which Nagle is
@@ -41,15 +68,13 @@ private Peer setup_socket(SocketChannel chan) throws IOException {
4168
SocketDescriptor descriptor = SocketDescriptor.new_impl(new SocketDescriptor.SocketDescriptorInterface() {
4269
@Override
4370
public long send_data(byte[] data, boolean resume_read) {
44-
if (resume_read) {
45-
peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_READ);
46-
selector.wakeup();
47-
}
4871
try {
72+
if (resume_read) {
73+
do_selector_action(() -> peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_READ));
74+
}
4975
long written = chan.write(ByteBuffer.wrap(data));
5076
if (written != data.length) {
51-
peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_WRITE);
52-
selector.wakeup();
77+
do_selector_action(() -> peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_WRITE));
5378
}
5479
return written;
5580
} catch (IOException e) {
@@ -61,9 +86,10 @@ public long send_data(byte[] data, boolean resume_read) {
6186
@Override
6287
public void disconnect_socket() {
6388
try {
64-
peer.key.cancel();
65-
peer.key.channel().close();
66-
selector.wakeup();
89+
do_selector_action(() -> {
90+
peer.key.cancel();
91+
peer.key.channel().close();
92+
});
6793
} catch (IOException ignored) { }
6894
synchronized (peer) {
6995
while (peer.block_disconnect_socket) {
@@ -82,7 +108,7 @@ public void disconnect_socket() {
82108

83109
PeerManager peer_manager;
84110
Thread io_thread;
85-
Selector selector;
111+
final Selector selector;
86112
long socket_id;
87113
volatile boolean shutdown = false;
88114

@@ -101,7 +127,18 @@ public NioPeerHandler(PeerManager manager) throws IOException {
101127
long lastTimerTick = System.currentTimeMillis();
102128
while (true) {
103129
try {
104-
this.selector.select(1000);
130+
if (IS_ANDROID) {
131+
while (true) {
132+
synchronized (this.selector) {
133+
if (!wakeup_selector) {
134+
this.selector.select(1000);
135+
break;
136+
}
137+
}
138+
}
139+
} else {
140+
this.selector.select(1000);
141+
}
105142
} catch (IOException ignored) {
106143
System.err.println("java.nio threw an unexpected IOException. Stopping PeerHandler thread!");
107144
return;
@@ -210,8 +247,7 @@ public void connect(byte[] their_node_id, SocketAddress remote, int timeout_ms)
210247
if (chan.write(ByteBuffer.wrap(initial_bytes)) != initial_bytes.length) {
211248
throw new IOException("We assume TCP socket buffer is at least a single packet in length");
212249
}
213-
peer.key = chan.register(this.selector, SelectionKey.OP_READ, peer);
214-
this.selector.wakeup();
250+
do_selector_action(() -> peer.key = chan.register(this.selector, SelectionKey.OP_READ, peer));
215251
} else {
216252
throw new IOException("LDK rejected outbound connection. This likely shouldn't ever happen.");
217253
}
@@ -228,8 +264,7 @@ public void bind_listener(SocketAddress socket_address) throws IOException {
228264
ServerSocketChannel listen_channel = ServerSocketChannel.open();
229265
listen_channel.bind(socket_address);
230266
listen_channel.configureBlocking(false);
231-
listen_channel.register(this.selector, SelectionKey.OP_ACCEPT);
232-
this.selector.wakeup();
267+
do_selector_action(() -> listen_channel.register(this.selector, SelectionKey.OP_ACCEPT));
233268
}
234269

235270
/**

src/main/java/org/ldk/enums/LDKAccessError.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
/**
44
* An error when accessing the chain via [`Access`].
5-
*
6-
* [`Access`]: trait.Access.html
75
*/
86
public enum LDKAccessError {
97
LDKAccessError_UnknownChain,

0 commit comments

Comments
 (0)