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
1 change: 1 addition & 0 deletions changelog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- [new feature] JAVA-982: Introduce new method ConsistencyLevel.isSerial().
- [bug] JAVA-764: Retry with the normal consistency level (not the serial one) when a write times out on the Paxos phase.
- [bug] JAVA-727: Allow monotonic timestamp generators to drift in the future + use microsecond precision when possible.
- [improvement] JAVA-444: Add Java process information to UUIDs.makeNode() hash.

Merged from 2.0 branch:

Expand Down
7 changes: 7 additions & 0 deletions driver-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
<version>${jnr-ffi.version}</version>
</dependency>

<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-posix</artifactId>
<version>${jnr-posix.version}</version>
</dependency>

<!-- Compression libraries for the protocol. -->
<!-- Each of them is only a mandatory runtime dependency if you want to use the compression it offers -->

Expand Down Expand Up @@ -368,6 +374,7 @@
<skip>false</skip>
<includes>
<include>**/SSL*Test.java</include>
<include>**/UUIDsPID*.java</include>
</includes>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class ClockFactory {
private static final String USE_NATIVE_CLOCK_SYSTEM_PROPERTY = "com.datastax.driver.USE_NATIVE_CLOCK";

static Clock newInstance() {
if (Native.isLibCLoaded() && SystemProperties.getBoolean(USE_NATIVE_CLOCK_SYSTEM_PROPERTY, true)) {
if (Native.isGettimeofdayAvailable() && SystemProperties.getBoolean(USE_NATIVE_CLOCK_SYSTEM_PROPERTY, true)) {
LOGGER.info("Using native clock to generate timestamps.");
return new NativeClock();
} else {
Expand Down
211 changes: 173 additions & 38 deletions driver-core/src/main/java/com/datastax/driver/core/Native.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,49 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
* Helper class to deal with native system call through JNR.
* Helper class to deal with native system calls through the
* <a href="https://github.com/jnr/jnr-ffi">JNR library</a>.
* <p/>
* The driver can benefit from native system calls to improve its performance and accuracy
* in some situations.
* <p/>
* Currently, the following features may be used by the driver when available:
* <ol>
* <li>{@link #currentTimeMicros()}: thanks to a system call to {@code gettimeofday()},
* the driver is able to generate timestamps with true microsecond precision
* (see {@link AtomicMonotonicTimestampGenerator} or {@link ThreadLocalMonotonicTimestampGenerator} for
* more information);</li>
* <li>{@link #processId()}: thanks to a system call to {@code getpid()},
* the driver has access to the JVM's process ID it is running under – which
* makes time-based UUID generation easier and more reliable (see {@link com.datastax.driver.core.utils.UUIDs UUIDs}
* for more information).</li>
* </ol>
* <p/>
* The availability of the aforementioned system calls depends on the underlying operation system's
* capabilities. For instance, {@code gettimeofday()} is not available under Windows systems.
* You can check if any of the system calls exposed through this class is available
* by calling {@link #isGettimeofdayAvailable()} or {@link #isGetpidAvailable()}.
* <p/>
* Note: This class is public because it needs to be accessible from other packages of the Java driver,
* but it is not meant to be used directly by client code.
*
* @see <a href="https://github.com/jnr/jnr-ffi">JNR library on Github</a>
*/
class Native {
public final class Native {

private static final Logger LOGGER = LoggerFactory.getLogger(Native.class);

/**
* Interface for LIBC calls through JNR.
* Note that this interface must be declared public.
*/
public interface LibC {
private static class LibCLoader {

/**
* Timeval struct.
*
* @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
*/
class Timeval extends Struct {
static class Timeval extends Struct {

public final time_t tv_sec = new time_t();

Expand All @@ -54,56 +78,167 @@ public Timeval(Runtime runtime) {
}

/**
* JNR call to {@code gettimeofday}.
*
* @param tv Timeval struct
* @param unused Timezone struct (unused)
* @return 0 for success, or -1 for failure
* @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
* Interface for LIBC calls through JNR.
* Note that this interface must be declared public.
*/
int gettimeofday(@Out @Transient Timeval tv, Pointer unused);
public interface LibC {

/**
* JNR call to {@code gettimeofday}.
*
* @param tv Timeval struct
* @param unused Timezone struct (unused)
* @return 0 for success, or -1 for failure
* @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
*/
int gettimeofday(@Out @Transient Timeval tv, Pointer unused);

}

private static final LibC LIB_C;

private static final Runtime LIB_C_RUNTIME;

private static final boolean GETTIMEOFDAY_AVAILABLE;

static {
LibC libc;
Runtime runtime = null;
try {
libc = LibraryLoader.create(LibC.class).load("c");
runtime = Runtime.getRuntime(libc);
} catch (Throwable t) {
libc = null; // dereference proxy to library if runtime could not be loaded
if (LOGGER.isDebugEnabled())
LOGGER.debug("Could not load JNR C Library, native system calls through this library will not be available", t);
else
LOGGER.info("Could not load JNR C Library, native system calls through this library will not be available " +
"(set this logger level to DEBUG to see the full stack trace).");

}
LIB_C = libc;
LIB_C_RUNTIME = runtime;
boolean gettimeofday = false;
if (LIB_C_RUNTIME != null) {
try {
gettimeofday = LIB_C.gettimeofday(new Timeval(LIB_C_RUNTIME), null) == 0;
} catch (Throwable t) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Native calls to gettimeofday() not available on this system.", t);
else
LOGGER.info("Native calls to gettimeofday() not available on this system " +
"(set this logger level to DEBUG to see the full stack trace).");
}
}
GETTIMEOFDAY_AVAILABLE = gettimeofday;
}

}

private static final LibC LIB_C;
private static class PosixLoader {

private static final Runtime LIB_C_RUNTIME;
public static final jnr.posix.POSIX POSIX;

static {
LibC libc = null;
Runtime runtime = null;
private static final boolean GETPID_AVAILABLE;

static {
jnr.posix.POSIX posix;
try {
// use reflection below to get the classloader a chance to load this class
Class<?> posixHandler = Class.forName("jnr.posix.POSIXHandler");
Class<?> defaultPosixHandler = Class.forName("jnr.posix.util.DefaultPOSIXHandler");
Class<?> posixFactory = Class.forName("jnr.posix.POSIXFactory");
Method getPOSIX = posixFactory.getMethod("getPOSIX", posixHandler, Boolean.TYPE);
posix = (jnr.posix.POSIX) getPOSIX.invoke(null, defaultPosixHandler.newInstance(), true);
} catch (Throwable t) {
posix = null;
if (LOGGER.isDebugEnabled())
LOGGER.debug("Could not load JNR POSIX Library, native system calls through this library will not be available.", t);
else
LOGGER.info("Could not load JNR POSIX Library, native system calls through this library will not be available " +
"(set this logger level to DEBUG to see the full stack trace).");
}
POSIX = posix;
boolean getpid = false;
if (POSIX != null) {
try {
POSIX.getpid();
getpid = true;
} catch (Throwable t) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Native calls to getpid() not available on this system.", t);
else
LOGGER.info("Native calls to getpid() not available on this system " +
"(set this logger level to DEBUG to see the full stack trace).");
}
}
GETPID_AVAILABLE = getpid;
}

}

/**
* Returns {@code true} if JNR C library is loaded and
* a call to {@code gettimeofday} is possible through this library
* on this system, and {@code false} otherwise.
*
* @return {@code true} if JNR C library is loaded and
* a call to {@code gettimeofday} is possible.
*/
public static boolean isGettimeofdayAvailable() {
try {
libc = LibraryLoader.create(LibC.class).load("c");
runtime = Runtime.getRuntime(libc);
} catch (Throwable t) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Could not load JNR LibC Library, native calls will not be available", t);
else
LOGGER.info("Could not load JNR LibC Library, native calls will not be available (set this logger level to DEBUG to see the full stack trace)");
return LibCLoader.GETTIMEOFDAY_AVAILABLE;
} catch (NoClassDefFoundError e) {
return false;
}
LIB_C = libc;
LIB_C_RUNTIME = runtime;
}

/**
* Returns true if LibC could be loaded with JNR.
* Returns {@code true} if JNR POSIX library is loaded and
* a call to {@code getpid} is possible through this library
* on this system, and {@code false} otherwise.
*
* @return true if LibC could be loaded with JNR, false otherwise.
* @return {@code true} if JNR POSIX library is loaded and
* a call to {@code getpid} is possible.
*/
static boolean isLibCLoaded() {
return LIB_C_RUNTIME != null;
public static boolean isGetpidAvailable() {
try {
return PosixLoader.GETPID_AVAILABLE;
} catch (NoClassDefFoundError e) {
return false;
}

}

/**
* Returns the current timestamp with microsecond precision
* via a system call to {@code gettimeofday}.
* via a system call to {@code gettimeofday}, through JNR C library.
*
* @return the current timestamp with microsecond precision.
* @throws UnsupportedOperationException if JNR C library is not loaded or {@code gettimeofday} is not available.
* @throws IllegalStateException if the call to {@code gettimeofday} did not complete with return code 0.
*/
static long currentTimeMicros() {
LibC.Timeval tv = new LibC.Timeval(LIB_C_RUNTIME);
if (LIB_C.gettimeofday(tv, null) != 0)
LOGGER.error("gettimeofday failed");
public static long currentTimeMicros() {
if (!isGettimeofdayAvailable())
throw new UnsupportedOperationException("JNR C library not loaded or gettimeofday not available");
LibCLoader.Timeval tv = new LibCLoader.Timeval(LibCLoader.LIB_C_RUNTIME);
int res = LibCLoader.LIB_C.gettimeofday(tv, null);
if (res != 0)
throw new IllegalStateException("Call to gettimeofday failed with result " + res);
return tv.tv_sec.get() * 1000000 + tv.tv_usec.get();
}

/**
* Returns the JVM's process identifier (PID)
* via a system call to {@code getpid}.
*
* @return the JVM's process identifier (PID).
* @throws UnsupportedOperationException if JNR POSIX library is not loaded or {@code getpid} is not available.
*/
public static int processId() {
if (!isGetpidAvailable())
throw new UnsupportedOperationException("JNR POSIX library not loaded or getpid not available");
return PosixLoader.POSIX.getpid();
}

}
Loading