Skip to content
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
15 changes: 15 additions & 0 deletions spark/src/main/java/org/apache/comet/NativeBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ public abstract class NativeBase {

private static final String libraryToLoad = System.mapLibraryName(NATIVE_LIB_NAME);
private static boolean loaded = false;
// The bundled libcomet this class loader unpacked and loaded, or null. Unlike `loaded`, never
// reset: unpacking it again yields a new temporary file, which the JVM loads as a second
// library with its own uninitialized native state, and JNI methods can then bind to either copy.
private static File bundledLibrary = null;
private static volatile Throwable loadErr = null;
private static final String searchPattern = "libcomet-";
private static final AtomicBoolean released = new AtomicBoolean(false);
Expand Down Expand Up @@ -76,6 +80,11 @@ static synchronized void setLoaded(boolean b) {
loaded = b;
}

// Only for testing
static synchronized File bundledLibrary() {
return bundledLibrary;
}

static synchronized void load() {
if (loaded) {
return;
Expand Down Expand Up @@ -113,6 +122,11 @@ static synchronized void load() {
* Use the bundled native libraries. Functionally equivalent to <code>System.loadLibrary</code>.
*/
private static void bundleLoadLibrary() {
if (bundledLibrary != null) {
loaded = true;
return;
}

String resourceName = resourceName();
InputStream is = NativeBase.class.getResourceAsStream(resourceName);
if (is == null) {
Expand All @@ -133,6 +147,7 @@ private static void bundleLoadLibrary() {
Files.copy(is, tempLib.toPath(), StandardCopyOption.REPLACE_EXISTING);
System.load(tempLib.getAbsolutePath());
loaded = true;
bundledLibrary = tempLib;
} catch (IOException e) {
throw new IllegalStateException("Cannot unpack libcomet: " + e);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ class CometSparkSessionExtensionsSuite extends CometTestBase {
}
}

test("reloading NativeBase does not load a second copy of the native library") {
// The bundled library is unpacked to a new temporary file each time it is loaded. To the JVM
// a second copy is a distinct library with its own uninitialized native state, and a JNI
// method first called afterwards can bind to it (#6096).
val before = NativeBase.bundledLibrary()
try {
NativeBase.setLoaded(false)
NativeBase.load()
assert(NativeBase.isLoaded)
} finally {
NativeBase.setLoaded(true)
}
val after = NativeBase.bundledLibrary()
assert(after eq before, s"reload unpacked another copy of the native library: $after")
}

test("Arrow properties") {
NativeBase.setLoaded(false)
NativeBase.load()
Expand Down
Loading