package com.datastax.oss.driver.internal.core.os;
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 jnr.posix.POSIXFactory;
import jnr.posix.util.DefaultPOSIXHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Native {
private static final Logger LOG = LoggerFactory.getLogger(Native.class);
public static boolean isCurrentTimeMicrosAvailable() {
try {
return LibCLoader.GET_TIME_OF_DAY_AVAILABLE;
} catch (NoClassDefFoundError e) {
return false;
}
}
public static long currentTimeMicros() {
if (!isCurrentTimeMicrosAvailable()) {
throw new IllegalStateException(
"Native call not available. "
+ "Check isCurrentTimeMicrosAvailable() before calling this method.");
}
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 libc.gettimeofday() failed with result " + res);
}
return tv.tv_sec.get() * 1000000 + tv.tv_usec.get();
}
public static boolean isGetProcessIdAvailable() {
try {
return PosixLoader.GET_PID_AVAILABLE;
} catch (NoClassDefFoundError e) {
return false;
}
}
public static int getProcessId() {
if (!isGetProcessIdAvailable()) {
throw new IllegalStateException(
"Native call not available. "
+ "Check isGetProcessIdAvailable() before calling this method.");
}
return PosixLoader.POSIX.getpid();
}
public static boolean isPlatformAvailable() {
try {
return PlatformLoader.PLATFORM != null;
} catch (NoClassDefFoundError e) {
return false;
}
}
public static String getCPU() {
if (!isPlatformAvailable())
throw new IllegalStateException(
"JNR Platform class not loaded. "
+ "Check isPlatformAvailable() before calling this method.");
return PlatformLoader.PLATFORM.getCPU().toString();
}
private static class LibCLoader {
public interface LibC {
int gettimeofday(@Out @Transient Timeval tv, Pointer unused);
}
private static class Timeval extends Struct {
private final time_t tv_sec = new time_t();
private final Unsigned32 tv_usec = new Unsigned32();
private Timeval(Runtime runtime) {
super(runtime);
}
}
private static final LibC LIB_C;
private static final Runtime LIB_C_RUNTIME;
private static final boolean GET_TIME_OF_DAY_AVAILABLE;
static {
LibC libc;
Runtime runtime = null;
try {
libc = LibraryLoader.create(LibC.class).load("c");
runtime = Runtime.getRuntime(libc);
} catch (Throwable t) {
libc = null;
LOG.debug("Error loading libc", t);
}
LIB_C = libc;
LIB_C_RUNTIME = runtime;
boolean getTimeOfDayAvailable = false;
if (LIB_C_RUNTIME != null) {
try {
getTimeOfDayAvailable = LIB_C.gettimeofday(new Timeval(LIB_C_RUNTIME), null) == 0;
} catch (Throwable t) {
LOG.debug("Error accessing libc.gettimeofday()", t);
}
}
GET_TIME_OF_DAY_AVAILABLE = getTimeOfDayAvailable;
}
}
private static class PosixLoader {
@SuppressWarnings("VariableNameSameAsType")
private static final jnr.posix.POSIX POSIX;
private static final boolean GET_PID_AVAILABLE;
static {
jnr.posix.POSIX posix;
try {
posix = POSIXFactory.getPOSIX(new DefaultPOSIXHandler(), true);
} catch (Throwable t) {
posix = null;
LOG.debug("Error loading POSIX", t);
}
POSIX = posix;
boolean getPidAvailable = false;
if (POSIX != null) {
try {
POSIX.getpid();
getPidAvailable = true;
} catch (Throwable t) {
LOG.debug("Error accessing posix.getpid()", t);
}
}
GET_PID_AVAILABLE = getPidAvailable;
}
}
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;
LOG.debug("Error loading jnr.ffi.Platform class, this class will not be available.", t);
}
PLATFORM = platform;
}
}
}