2424import org .slf4j .Logger ;
2525import org .slf4j .LoggerFactory ;
2626
27+ import java .lang .reflect .Method ;
28+
2729/**
28- * Helper class to deal with native system call through JNR.
30+ * Helper class to deal with native system calls through the
31+ * <a href="https://github.com/jnr/jnr-ffi">JNR library</a>.
32+ * <p/>
33+ * The driver can benefit from native system calls to improve its performance and accuracy
34+ * in some situations.
35+ * <p/>
36+ * Currently, the following features may be used by the driver when available:
37+ * <ol>
38+ * <li>{@link #currentTimeMicros()}: thanks to a system call to {@code gettimeofday()},
39+ * the driver is able to generate timestamps with true microsecond precision
40+ * (see {@link AtomicMonotonicTimestampGenerator} or {@link ThreadLocalMonotonicTimestampGenerator} for
41+ * more information);</li>
42+ * <li>{@link #processId()}: thanks to a system call to {@code getpid()},
43+ * the driver has access to the JVM's process ID it is running under – which
44+ * makes time-based UUID generation easier and more reliable (see {@link com.datastax.driver.core.utils.UUIDs UUIDs}
45+ * for more information).</li>
46+ * </ol>
47+ * <p/>
48+ * The availability of the aforementioned system calls depends on the underlying operation system's
49+ * capabilities. For instance, {@code gettimeofday()} is not available under Windows systems.
50+ * You can check if any of the system calls exposed through this class is available
51+ * by calling {@link #isGettimeofdayAvailable()} or {@link #isGetpidAvailable()}.
52+ * <p/>
53+ * Note: This class is public because it needs to be accessible from other packages of the Java driver,
54+ * but it is not meant to be used directly by client code.
55+ *
56+ * @see <a href="https://github.com/jnr/jnr-ffi">JNR library on Github</a>
2957 */
30- class Native {
58+ public final class Native {
3159
3260 private static final Logger LOGGER = LoggerFactory .getLogger (Native .class );
3361
34- /**
35- * Interface for LIBC calls through JNR.
36- * Note that this interface must be declared public.
37- */
38- public interface LibC {
62+ private static class LibCLoader {
3963
4064 /**
4165 * Timeval struct.
4266 *
4367 * @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
4468 */
45- class Timeval extends Struct {
69+ static class Timeval extends Struct {
4670
4771 public final time_t tv_sec = new time_t ();
4872
@@ -54,56 +78,167 @@ public Timeval(Runtime runtime) {
5478 }
5579
5680 /**
57- * JNR call to {@code gettimeofday}.
58- *
59- * @param tv Timeval struct
60- * @param unused Timezone struct (unused)
61- * @return 0 for success, or -1 for failure
62- * @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
81+ * Interface for LIBC calls through JNR.
82+ * Note that this interface must be declared public.
6383 */
64- int gettimeofday (@ Out @ Transient Timeval tv , Pointer unused );
84+ public interface LibC {
85+
86+ /**
87+ * JNR call to {@code gettimeofday}.
88+ *
89+ * @param tv Timeval struct
90+ * @param unused Timezone struct (unused)
91+ * @return 0 for success, or -1 for failure
92+ * @see <a href="http://man7.org/linux/man-pages/man2/settimeofday.2.html">GETTIMEOFDAY(2)</a>
93+ */
94+ int gettimeofday (@ Out @ Transient Timeval tv , Pointer unused );
95+
96+ }
97+
98+ private static final LibC LIB_C ;
99+
100+ private static final Runtime LIB_C_RUNTIME ;
101+
102+ private static final boolean GETTIMEOFDAY_AVAILABLE ;
103+
104+ static {
105+ LibC libc ;
106+ Runtime runtime = null ;
107+ try {
108+ libc = LibraryLoader .create (LibC .class ).load ("c" );
109+ runtime = Runtime .getRuntime (libc );
110+ } catch (Throwable t ) {
111+ libc = null ; // dereference proxy to library if runtime could not be loaded
112+ if (LOGGER .isDebugEnabled ())
113+ LOGGER .debug ("Could not load JNR C Library, native system calls through this library will not be available" , t );
114+ else
115+ LOGGER .info ("Could not load JNR C Library, native system calls through this library will not be available " +
116+ "(set this logger level to DEBUG to see the full stack trace)." );
117+
118+ }
119+ LIB_C = libc ;
120+ LIB_C_RUNTIME = runtime ;
121+ boolean gettimeofday = false ;
122+ if (LIB_C_RUNTIME != null ) {
123+ try {
124+ gettimeofday = LIB_C .gettimeofday (new Timeval (LIB_C_RUNTIME ), null ) == 0 ;
125+ } catch (Throwable t ) {
126+ if (LOGGER .isDebugEnabled ())
127+ LOGGER .debug ("Native calls to gettimeofday() not available on this system." , t );
128+ else
129+ LOGGER .info ("Native calls to gettimeofday() not available on this system " +
130+ "(set this logger level to DEBUG to see the full stack trace)." );
131+ }
132+ }
133+ GETTIMEOFDAY_AVAILABLE = gettimeofday ;
134+ }
135+
65136 }
66137
67- private static final LibC LIB_C ;
138+ private static class PosixLoader {
68139
69- private static final Runtime LIB_C_RUNTIME ;
140+ public static final jnr . posix . POSIX POSIX ;
70141
71- static {
72- LibC libc = null ;
73- Runtime runtime = null ;
142+ private static final boolean GETPID_AVAILABLE ;
143+
144+ static {
145+ jnr .posix .POSIX posix ;
146+ try {
147+ // use reflection below to get the classloader a chance to load this class
148+ Class <?> posixHandler = Class .forName ("jnr.posix.POSIXHandler" );
149+ Class <?> defaultPosixHandler = Class .forName ("jnr.posix.util.DefaultPOSIXHandler" );
150+ Class <?> posixFactory = Class .forName ("jnr.posix.POSIXFactory" );
151+ Method getPOSIX = posixFactory .getMethod ("getPOSIX" , posixHandler , Boolean .TYPE );
152+ posix = (jnr .posix .POSIX ) getPOSIX .invoke (null , defaultPosixHandler .newInstance (), true );
153+ } catch (Throwable t ) {
154+ posix = null ;
155+ if (LOGGER .isDebugEnabled ())
156+ LOGGER .debug ("Could not load JNR POSIX Library, native system calls through this library will not be available." , t );
157+ else
158+ LOGGER .info ("Could not load JNR POSIX Library, native system calls through this library will not be available " +
159+ "(set this logger level to DEBUG to see the full stack trace)." );
160+ }
161+ POSIX = posix ;
162+ boolean getpid = false ;
163+ if (POSIX != null ) {
164+ try {
165+ POSIX .getpid ();
166+ getpid = true ;
167+ } catch (Throwable t ) {
168+ if (LOGGER .isDebugEnabled ())
169+ LOGGER .debug ("Native calls to getpid() not available on this system." , t );
170+ else
171+ LOGGER .info ("Native calls to getpid() not available on this system " +
172+ "(set this logger level to DEBUG to see the full stack trace)." );
173+ }
174+ }
175+ GETPID_AVAILABLE = getpid ;
176+ }
177+
178+ }
179+
180+ /**
181+ * Returns {@code true} if JNR C library is loaded and
182+ * a call to {@code gettimeofday} is possible through this library
183+ * on this system, and {@code false} otherwise.
184+ *
185+ * @return {@code true} if JNR C library is loaded and
186+ * a call to {@code gettimeofday} is possible.
187+ */
188+ public static boolean isGettimeofdayAvailable () {
74189 try {
75- libc = LibraryLoader .create (LibC .class ).load ("c" );
76- runtime = Runtime .getRuntime (libc );
77- } catch (Throwable t ) {
78- if (LOGGER .isDebugEnabled ())
79- LOGGER .debug ("Could not load JNR LibC Library, native calls will not be available" , t );
80- else
81- 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)" );
190+ return LibCLoader .GETTIMEOFDAY_AVAILABLE ;
191+ } catch (NoClassDefFoundError e ) {
192+ return false ;
82193 }
83- LIB_C = libc ;
84- LIB_C_RUNTIME = runtime ;
85194 }
86195
87196 /**
88- * Returns true if LibC could be loaded with JNR.
197+ * Returns {@code true} if JNR POSIX library is loaded and
198+ * a call to {@code getpid} is possible through this library
199+ * on this system, and {@code false} otherwise.
89200 *
90- * @return true if LibC could be loaded with JNR, false otherwise.
201+ * @return {@code true} if JNR POSIX library is loaded and
202+ * a call to {@code getpid} is possible.
91203 */
92- static boolean isLibCLoaded () {
93- return LIB_C_RUNTIME != null ;
204+ public static boolean isGetpidAvailable () {
205+ try {
206+ return PosixLoader .GETPID_AVAILABLE ;
207+ } catch (NoClassDefFoundError e ) {
208+ return false ;
209+ }
210+
94211 }
95212
96213 /**
97214 * Returns the current timestamp with microsecond precision
98- * via a system call to {@code gettimeofday}.
215+ * via a system call to {@code gettimeofday}, through JNR C library .
99216 *
100217 * @return the current timestamp with microsecond precision.
218+ * @throws UnsupportedOperationException if JNR C library is not loaded or {@code gettimeofday} is not available.
219+ * @throws IllegalStateException if the call to {@code gettimeofday} did not complete with return code 0.
101220 */
102- static long currentTimeMicros () {
103- LibC .Timeval tv = new LibC .Timeval (LIB_C_RUNTIME );
104- if (LIB_C .gettimeofday (tv , null ) != 0 )
105- LOGGER .error ("gettimeofday failed" );
221+ public static long currentTimeMicros () {
222+ if (!isGettimeofdayAvailable ())
223+ throw new UnsupportedOperationException ("JNR C library not loaded or gettimeofday not available" );
224+ LibCLoader .Timeval tv = new LibCLoader .Timeval (LibCLoader .LIB_C_RUNTIME );
225+ int res = LibCLoader .LIB_C .gettimeofday (tv , null );
226+ if (res != 0 )
227+ throw new IllegalStateException ("Call to gettimeofday failed with result " + res );
106228 return tv .tv_sec .get () * 1000000 + tv .tv_usec .get ();
107229 }
108230
231+ /**
232+ * Returns the JVM's process identifier (PID)
233+ * via a system call to {@code getpid}.
234+ *
235+ * @return the JVM's process identifier (PID).
236+ * @throws UnsupportedOperationException if JNR POSIX library is not loaded or {@code getpid} is not available.
237+ */
238+ public static int processId () {
239+ if (!isGetpidAvailable ())
240+ throw new UnsupportedOperationException ("JNR POSIX library not loaded or getpid not available" );
241+ return PosixLoader .POSIX .getpid ();
242+ }
243+
109244}
0 commit comments