Changed NativeLibraryLocator to make extraction code multi-process safe.

This commit is contained in:
Adam Murdoch
2012-10-07 06:55:03 +10:00
parent bc84bb102b
commit fbf246dbf9
5 changed files with 76 additions and 22 deletions

View File

@@ -75,10 +75,16 @@ Some sample code to use the terminal:
## Changes
### 0.2
Fixes to make native library extraction multi-process safe.
### 0.1
Initial release
# Development
## Building
You will need to use the Gradle wrapper. Just run `gradlew` in the root directory.
@@ -174,7 +180,6 @@ You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
* String names for errno values.
* Split into multiple projects.
* Convert to c.
* Make native library extraction multi-process safe.
* Use fully decomposed form for unicode file names on hfs+ filesystems.
* Extend FileSystem to deal with removable media.

View File

@@ -19,22 +19,17 @@ public class Native {
private Native() {
}
/**
* Returns the version of the native library implementations used by this class.
*/
@ThreadSafe
static public String getNativeVersion() {
return String.valueOf(NativeLibraryFunctions.VERSION);
}
/**
* Initialises the native integration, if not already initialized.
*
* @param extractDir The directory to extract native resources into. May be null, in which case a default is
* selected.
*
* @throws NativeIntegrationUnavailableException When native integration is not available on the current machine.
* @throws NativeException On failure to load the native integration.
*/
@ThreadSafe
static public void init(File extractDir) {
static public void init(File extractDir) throws NativeIntegrationUnavailableException, NativeException {
synchronized (Native.class) {
if (!loaded) {
Platform platform = Platform.current();

View File

@@ -1,9 +1,11 @@
package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.NativeException;
import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions;
import java.io.*;
import java.net.URL;
import java.nio.channels.FileLock;
public class NativeLibraryLocator {
private final File extractDir;
@@ -13,22 +15,47 @@ public class NativeLibraryLocator {
}
public File find(String libraryName) throws IOException {
File libFile;
URL resource = getClass().getClassLoader().getResource(libraryName);
if (resource != null) {
File libDir = extractDir;
if (libDir == null) {
libDir = File.createTempFile("native-platform", "dir");
if (extractDir != null) {
File libFile = new File(extractDir, String.format("%s/%s", NativeLibraryFunctions.VERSION, libraryName));
File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock");
lockFile.getParentFile().mkdirs();
lockFile.createNewFile();
RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw");
try {
// Take exclusive lock on lock file
FileLock lock = lockFileAccess.getChannel().lock();
if (lockFile.length() > 0 && lockFileAccess.readBoolean()) {
// Library has been extracted
return libFile;
}
URL resource = getClass().getClassLoader().getResource(libraryName);
if (resource != null) {
// Extract library and write marker to lock file
libFile.getParentFile().mkdirs();
copy(resource, libFile);
lockFileAccess.seek(0);
lockFileAccess.writeBoolean(true);
return libFile;
}
} finally {
// Also releases lock
lockFileAccess.close();
}
} else {
URL resource = getClass().getClassLoader().getResource(libraryName);
if (resource != null) {
File libFile;
File libDir = File.createTempFile("native-platform", "dir");
libDir.delete();
libDir.mkdirs();
libFile = new File(libDir, libraryName);
libFile.deleteOnExit();
copy(resource, libFile);
return libFile;
}
libFile = new File(libDir, libraryName);
libFile.deleteOnExit();
copy(resource, libFile);
return libFile;
}
libFile = new File("build/binaries/" + libraryName);
File libFile = new File("build/binaries/" + libraryName);
if (libFile.isFile()) {
return libFile;
}

View File

@@ -17,6 +17,10 @@ if (project.hasProperty('release')) {
}
}
dependencies {
compile 'net.sf.jopt-simple:jopt-simple:4.2'
}
configurations.archives.artifacts.clear()
artifacts {
archives distZip

View File

@@ -1,10 +1,33 @@
package net.rubygrapefruit.platform.test;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.rubygrapefruit.platform.*;
import net.rubygrapefruit.platform.Process;
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
public static void main(String[] args) throws IOException {
OptionParser optionParser = new OptionParser();
optionParser.accepts("cache-dir", "The directory to cache native libraries in").withRequiredArg();
OptionSet result = null;
try {
result = optionParser.parse(args);
} catch (OptionException e) {
System.err.println(e.getMessage());
System.err.println();
optionParser.printHelpOn(System.err);
System.exit(1);
}
if (result.has("cache-dir")) {
Native.init(new File(result.valueOf("cache-dir").toString()));
}
System.out.println();
System.out.println("* OS: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + ' ' + System.getProperty("os.arch"));
System.out.println("* JVM: " + System.getProperty("java.vm.vendor") + ' ' + System.getProperty("java.version"));