com.datastax.driver.core.Native Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* This software can be used solely with DataStax Enterprise. Please consult the license at
* http://www.datastax.com/terms/datastax-dse-driver-license-terms
*/
package com.datastax.driver.core;
import java.lang.reflect.Method;
import jnr.ffi.LibraryLoader;
import jnr.ffi.Platform;
import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
import jnr.ffi.Struct;
import jnr.ffi.annotations.Out;
import jnr.ffi.annotations.Transient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper class to deal with native system calls through the JNR library.
*
* The driver can benefit from native system calls to improve its performance and accuracy in
* some situations.
*
*
Currently, the following features may be used by the driver when available:
*
*
* - {@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);
*
- {@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).
*
- {@link #getCPU()}: thanks to {@link Platform#getCPU()}, the driver is able to report the
* current processor architecture the JVM is running on.
*
*
* 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()} or {@link
* #isPlatformAvailable()}. The method {@link #getCPU()} should always be available, unless the
* jnr-ffi dependency itself is not present on the classpath.
*
*
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 JNR library on Github
*/
public final class Native {
private static final Logger LOGGER = LoggerFactory.getLogger(Native.class);
private static class LibCLoader {
/**
* Timeval struct.
*
* @see GETTIMEOFDAY(2)
*/
static class Timeval extends Struct {
public final time_t tv_sec = new time_t();
public final Unsigned32 tv_usec = new Unsigned32();
public Timeval(Runtime runtime) {
super(runtime);
}
}
/** Interface for LIBC calls through JNR. Note that this interface must be declared public. */
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 GETTIMEOFDAY(2)
*/
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 class PosixLoader {
public static final jnr.posix.POSIX POSIX;
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;
}
}
private static class PlatformLoader {
private static final Platform PLATFORM;
static {
Platform platform;
try {
Class> platformClass = Class.forName("jnr.ffi.Platform");
Method getNativePlatform = platformClass.getMethod("getNativePlatform");
platform = (Platform) getNativePlatform.invoke(null);
} catch (Throwable t) {
platform = null;
if (LOGGER.isDebugEnabled())
LOGGER.debug(
"Could not load jnr.ffi.Platform class, this class will not be available.", t);
else
LOGGER.info(
"Could not load jnr.ffi.Platform class, this class will not be available "
+ "(set this logger level to DEBUG to see the full stack trace).");
}
PLATFORM = platform;
}
}
/**
* 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 {
return LibCLoader.GETTIMEOFDAY_AVAILABLE;
} catch (NoClassDefFoundError e) {
return false;
}
}
/**
* 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 {@code true} if JNR POSIX library is loaded and a call to {@code getpid} is possible.
*/
public static boolean isGetpidAvailable() {
try {
return PosixLoader.GETPID_AVAILABLE;
} catch (NoClassDefFoundError e) {
return false;
}
}
/**
* Returns {@code true} if JNR {@link Platform} class is loaded, and {@code false} otherwise.
*
* @return {@code true} if JNR {@link Platform} class is loaded.
*/
public static boolean isPlatformAvailable() {
try {
return PlatformLoader.PLATFORM != null;
} catch (NoClassDefFoundError e) {
return false;
}
}
/**
* Returns the current timestamp with microsecond precision 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.
*/
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();
}
/**
* Returns the current processor architecture the JVM is running on, as reported by {@link
* Platform#getCPU()}.
*
* @return the current processor architecture.
* @throws UnsupportedOperationException if JNR Platform library is not loaded.
*/
public static String getCPU() {
if (!isPlatformAvailable())
throw new UnsupportedOperationException("JNR Platform class not loaded");
return PlatformLoader.PLATFORM.getCPU().toString();
}
}