Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Java extension loading on Linux #2898

Merged
merged 3 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions tools/java_api/src/jni/kuzu_java.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#include <unordered_map>

#ifdef _WIN32
// Do nothing on Windows
#else
#include <dlfcn.h>
#endif

// This header is generated at build time. See CMakeLists.txt.
#include "com_kuzudb_KuzuNative.h"
#include "common/exception/conversion.h"
Expand Down Expand Up @@ -149,6 +155,22 @@
/**
* All Database native functions
*/
// protected static native void kuzu_native_reload_library(String lib_path);
JNIEXPORT void JNICALL Java_com_kuzudb_KuzuNative_kuzu_1native_1reload_1library(
JNIEnv* env, jclass, jstring lib_path) {
#ifdef _WIN32
// Do nothing on Windows
#else
const char* path = env->GetStringUTFChars(lib_path, JNI_FALSE);
void* handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
env->ReleaseStringUTFChars(lib_path, path);
if (handle == nullptr) {
jclass Exception = env->FindClass("java/lang/Exception");
auto error = dlerror(); // NOLINT(concurrency-mt-unsafe): load can only be executed in single thread.

Check warning on line 169 in tools/java_api/src/jni/kuzu_java.cpp

View check run for this annotation

Codecov / codecov/patch

tools/java_api/src/jni/kuzu_java.cpp#L169

Added line #L169 was not covered by tests
env->ThrowNew(Exception, error);
}
#endif
}

JNIEXPORT jlong JNICALL Java_com_kuzudb_KuzuNative_kuzu_1database_1init(JNIEnv* env, jclass,
jstring database_path, jlong buffer_pool_size, jboolean enable_compression,
Expand Down
11 changes: 10 additions & 1 deletion tools/java_api/src/main/java/com/kuzudb/KuzuNative.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ public class KuzuNative {
}
Files.copy(lib_res.openStream(), lib_file, StandardCopyOption.REPLACE_EXISTING);
new File(lib_file.toString()).deleteOnExit();
System.load(lib_file.toAbsolutePath().toString());
String lib_path = lib_file.toAbsolutePath().toString();
System.load(lib_path);
if(os_name.equals("linux")) {
kuzu_native_reload_library(lib_path);
}
} catch (IOException e) {
e.printStackTrace();
}
}

// Hack: Reload the native library again in JNI bindings to work around the
// extension loading issue on Linux as System.load() does not set
// `RTLD_GLOBAL` flag and there is no way to set it in Java.
protected static native void kuzu_native_reload_library(String lib_path);

// Database
protected static native long kuzu_database_init(String database_path, long buffer_pool_size, boolean enable_compression, boolean read_only);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ void HttpfsInstallAndLoad() throws KuzuObjectRefDestroyedException {
KuzuQueryResult result = conn.query("INSTALL httpfs");
assertTrue(result.isSuccess());
result.destroy();
// Skip loading the extension for now until the fix is in place
// result = conn.query("LOAD EXTENSION httpfs");
// assertTrue(result.isSuccess());
// result.destroy();
result = conn.query("LOAD EXTENSION httpfs");
assertTrue(result.isSuccess());
result.destroy();
}
}
4 changes: 2 additions & 2 deletions tools/python_api/src_py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@
import sys
import os

# Set RTLD_GLOBAL and RTLD_NOW flags on Linux to fix the issue with loading
# Set RTLD_GLOBAL and RTLD_LAZY flags on Linux to fix the issue with loading
# extensions
if sys.platform == "linux":
original_dlopen_flags = sys.getdlopenflags()
sys.setdlopenflags(os.RTLD_GLOBAL | os.RTLD_NOW)
sys.setdlopenflags(os.RTLD_GLOBAL | os.RTLD_LAZY)

from .database import *
from .connection import *
Expand Down
Loading