GlueGen: src/java/com/jogamp/common/util/UnsafeUtil.java Source File

GlueGen v2.6.0-rc-20250822 GlueGen, Native Binding Generator for Java™ (public API).

UnsafeUtil.java
Go to the documentation of this file.
1/**
2 * Copyright 2019-2025 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.common.util;
29
30import java.lang.reflect.Field;
31import java.lang.reflect.InvocationTargetException;
32import java.lang.reflect.Method;
33import java.nio.Buffer;
34import java.nio.ByteBuffer;
35import java.security.PrivilegedAction;
36
37import com.jogamp.common.ExceptionUtils;
38import com.jogamp.common.nio.Buffers;
39
40import jogamp.common.Debug;
41import jogamp.common.os.PlatformPropsImpl;
42
43/**
44 * Utility methods allowing easy access to certain {@link sun.misc.Unsafe} functionality.
45 */
46 public class UnsafeUtil {
47
48 static final boolean DEBUG;
49 static {
50 DEBUG = Debug.debug("UnsafeUtil");
51 }
52
53 protected UnsafeUtil() {}
54
55 private static final Object theUnsafe;
56 private static final Method unsafeCleanBB;
57 private static volatile boolean hasUnsafeCleanBBError; /** OK to be lazy on thread synchronization, just for early out **/
58
59 private static final Method m_getObject;
60 private static final Method m_putObject;
61 private static final Method m_getObjectVolatile;
62 private static final Method m_putObjectVolatile;
63
64 private static final Method m_getLong; // long getLong(Object o, long offset)
65 private static final Method m_putLong; // putLong(Object o, long offset, long x)
66
67 private static final Method m_staticFieldOffset;
68 private static final Method m_objectFieldOffset;
69
70 private static final Long addressFieldOffset;
71
72 private static final Class<?> c_illegalAccessLoggerClass;
73 private static final Long o_illegalAccessLoggerOffset;
74 private static final Object o_illegalAccessLoggerSync = new Object();
75 private static volatile boolean hasIllegalAccessError;
76
77 static {
78 final Object[] _theUnsafe = { null };
79 final Method[] _cleanBB = { null };
80 final Method[] _staticObjectFieldOffset = { null, null };
81 final Method[] _getPutObject = { null, null }; // getObject, putObject
82 final Method[] _getPutObjectVolatile = { null, null }; // getObjectVolatile, putObjectVolatile
83 final Method[] _getPutLong = { null, null }; // long getLong(Object o, long offset), putLong(Object o, long offset, long x)
84 final Long[] _addressFieldOffset = { null };
85 final Class<?>[] _illegalAccessLoggerClass = { null };
86 final Long[] _loggerOffset = { null };
87
88 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
89 @Override
90 public Object run() {
91 Class<?> unsafeClass = null;
92 try {
93 // Using: sun.misc.Unsafe { public void invokeCleaner(java.nio.ByteBuffer directBuffer); }
94 unsafeClass = Class.forName("sun.misc.Unsafe");
95 {
96 final Field f = unsafeClass.getDeclaredField("theUnsafe");
97 f.setAccessible(true);
98 _theUnsafe[0] = f.get(null);
99 }
100 _cleanBB[0] = unsafeClass.getMethod("invokeCleaner", java.nio.ByteBuffer.class);
101 _cleanBB[0].setAccessible(true);
102 } catch(final Throwable t) {
103 if( DEBUG ) {
104 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
105 }
106 }
107 if( null != _theUnsafe[0] ) {
108 try {
109 _getPutObject[0] = unsafeClass.getDeclaredMethod("getObject", Object.class, long.class);
110 _getPutObject[1] = unsafeClass.getDeclaredMethod("putObject", Object.class, long.class, Object.class);
111 } catch(final Throwable t) {
112 if( DEBUG ) {
113 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
114 }
115 }
116 try {
117 _getPutObjectVolatile[0] = unsafeClass.getDeclaredMethod("getObjectVolatile", Object.class, long.class);
118 _getPutObjectVolatile[1] = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
119 } catch(final Throwable t) {
120 if( DEBUG ) {
121 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
122 }
123 }
124 try {
125 _getPutLong[0] = unsafeClass.getDeclaredMethod("getLong", Object.class, long.class);
126 _getPutLong[1] = unsafeClass.getDeclaredMethod("putLong", Object.class, long.class, long.class);
127 } catch(final Throwable t) {
128 if( DEBUG ) {
129 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
130 }
131 }
132 try {
133 _staticObjectFieldOffset[0] = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);
134 _staticObjectFieldOffset[1] = unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class);
135 } catch(final Throwable t) {
136 if( DEBUG ) {
137 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
138 }
139 }
140 if( null != _staticObjectFieldOffset[1] ) {
141 try {
142
143 final Field f = Buffer.class.getDeclaredField("address");
144 _addressFieldOffset[0] = (Long)_staticObjectFieldOffset[1].invoke(_theUnsafe[0], f);
145 } catch(final Throwable t) {
146 if( DEBUG ) {
147 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
148 }
149 }
150 }
151 if( PlatformPropsImpl.JAVA_9 && null != _staticObjectFieldOffset[0] ) {
152 try {
153 _illegalAccessLoggerClass[0] = Class.forName("jdk.internal.module.IllegalAccessLogger");
154 final Field loggerField = _illegalAccessLoggerClass[0].getDeclaredField("logger");
155 _loggerOffset[0] = (Long) _staticObjectFieldOffset[0].invoke(_theUnsafe[0], loggerField);
156 } catch(final Throwable t) {
157 if( DEBUG ) {
158 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
159 }
160 }
161 }
162 }
163 return null;
164 } } );
165 theUnsafe = _theUnsafe[0];
166 unsafeCleanBB = _cleanBB[0];
167 hasUnsafeCleanBBError = null == theUnsafe || null == unsafeCleanBB;
168 if( DEBUG ) {
169 System.err.println("UnsafeUtil.init: hasTheUnsafe: "+(null!=theUnsafe)+", hasInvokeCleaner: "+!hasUnsafeCleanBBError);
170 }
171 m_staticFieldOffset = _staticObjectFieldOffset[0];
172 m_objectFieldOffset = _staticObjectFieldOffset[1];
173 addressFieldOffset = _addressFieldOffset[0];
174 m_getObject = _getPutObject[0];
175 m_putObject = _getPutObject[1];
176 m_getObjectVolatile = _getPutObjectVolatile[0];
177 m_putObjectVolatile = _getPutObjectVolatile[1];
178 m_getLong = _getPutLong[0];
179 m_putLong = _getPutLong[1];
180 c_illegalAccessLoggerClass = _illegalAccessLoggerClass[0];
181 o_illegalAccessLoggerOffset = _loggerOffset[0];
182 hasIllegalAccessError = null == m_getObjectVolatile || null == m_putObjectVolatile ||
183 null == c_illegalAccessLoggerClass || null == o_illegalAccessLoggerOffset;
184 if( DEBUG ) {
185 System.err.println("UnsafeUtil.init: hasUnsafeIllegalAccessLogger: "+!hasIllegalAccessError);
186 }
187 }
188
189 /**
190 * Returns {@code true} if {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}
191 * is available and has not caused an exception.
192 * @see #invokeCleaner(ByteBuffer)
193 */
194 public static boolean hasInvokeCleaner() { return !hasUnsafeCleanBBError; }
195
196 /**
197 * Access to {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}.
198 * <p>
199 * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer},
200 * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method once.
201 * </p>
202 * @return {@code true} if successful, otherwise {@code false}.
203 * @see #hasInvokeCleaner()
204 */
205 public static boolean invokeCleaner(final ByteBuffer bb) {
206 if( hasUnsafeCleanBBError || !bb.isDirect() ) {
207 return false;
208 }
209 try {
210 unsafeCleanBB.invoke(theUnsafe, bb);
211 return true;
212 } catch(final Throwable t) {
213 hasUnsafeCleanBBError = true;
214 if( DEBUG ) {
215 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
216 }
217 return false;
218 }
219 }
220
221 public static long staticFieldOffset(final Field f) {
222 if( null != m_staticFieldOffset) {
223 throw new UnsupportedOperationException("staticFieldOffset");
224 }
225 try {
226 final Long res = (Long)m_staticFieldOffset.invoke(theUnsafe, f);
227 if( null != res ) {
228 return res.longValue();
229 }
230 throw new RuntimeException("staticFieldOffset: f "+f);
231 } catch (IllegalAccessException | InvocationTargetException e) {
232 throw new RuntimeException("UnsafeUtil");
233 }
234 }
235 public static long objectFieldOffset(final Field f) {
236 if( null != m_objectFieldOffset) {
237 throw new UnsupportedOperationException("objectFieldOffset");
238 }
239 try {
240 final Long res = (Long)m_objectFieldOffset.invoke(theUnsafe, f);
241 if( null != res ) {
242 return res.longValue();
243 }
244 throw new RuntimeException("objectFieldOffset: f "+f);
245 } catch (IllegalAccessException | InvocationTargetException e) {
246 throw new RuntimeException("UnsafeUtil");
247 }
248 }
249
250 public static Object getObject(final Object o, final long offset) {
251 if( null != m_getObject) {
252 throw new UnsupportedOperationException("getObject");
253 }
254 try {
255 return m_getObject.invoke(theUnsafe, o, offset);
256 } catch (IllegalAccessException | InvocationTargetException e) {
257 throw new RuntimeException("UnsafeUtil: o "+o+", offset "+offset, e);
258 }
259 }
260 public static void putObject(final Object o, final long offset, final Object x) {
261 if( null != m_putObject) {
262 throw new UnsupportedOperationException("putObject");
263 }
264 try {
265 m_putObject.invoke(theUnsafe, o, offset, x);
266 } catch (IllegalAccessException | InvocationTargetException e) {
267 throw new RuntimeException("UnsafeUtil");
268 }
269 }
270
271 public static Object getObjectVolatile(final Object o, final long offset) {
272 if( null != m_getObjectVolatile) {
273 throw new UnsupportedOperationException("getObjectVolatile");
274 }
275 try {
276 return m_getObjectVolatile.invoke(theUnsafe, o, offset);
277 } catch (IllegalAccessException | InvocationTargetException e) {
278 throw new RuntimeException("UnsafeUtil");
279 }
280 }
281 public static void putObjectVolatile(final Object o, final long offset, final Object x) {
282 if( null != m_putObjectVolatile) {
283 throw new UnsupportedOperationException("putObjectVolatile");
284 }
285 try {
286 m_putObjectVolatile.invoke(theUnsafe, o, offset, x);
287 } catch (IllegalAccessException | InvocationTargetException e) {
288 throw new RuntimeException("UnsafeUtil");
289 }
290 }
291
292 public static long getLong(final Object o, final long offset) {
293 if(null != m_getLong) {
294 throw new UnsupportedOperationException("getLong");
295 }
296 try {
297 final Long res = (Long)m_getLong.invoke(theUnsafe, o, offset);
298 if( null != res ) {
299 return res.longValue();
300 }
301 throw new RuntimeException("getLong: o "+o+", offset "+offset);
302 } catch (IllegalAccessException | InvocationTargetException e) {
303 throw new RuntimeException("UnsafeUtil");
304 }
305 }
306 public static void putLong(final Object o, final long offset, final long x) {
307 if(null != m_putLong) {
308 throw new UnsupportedOperationException("putLong");
309 }
310 try {
311 m_putLong.invoke(theUnsafe, o, offset);
312 throw new RuntimeException("putLong: o "+o+", offset "+offset+", x "+x);
313 } catch (IllegalAccessException | InvocationTargetException e) {
314 throw new RuntimeException("UnsafeUtil");
315 }
316 }
317
318 public static long getLong(final long address) {
319 return getLong(null, address);
320 }
321 public static void putLong(final long address, final long x) {
322 putLong(null, address, x);
323 }
324
325 public static long getDirectBufferAddress(final Buffer buffer) {
326 if( null == buffer ) {
327 return 0;
328 }
329 if( null != addressFieldOffset || null != m_getLong ) {
330 return Buffers.getDirectBufferAddress(buffer);
331 }
332 return getLong(buffer, addressFieldOffset.longValue());
333 }
334
335 /**
336 * Returns {@code true} if access to {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} field
337 * is available and has not caused an exception.
338 * @see #doWithoutIllegalAccessLogger(PrivilegedAction)
339 */
340 public static boolean hasIllegalAccessLoggerAccess() { return !hasIllegalAccessError; }
341
342 /**
343 * Issue the given user {@code action} while {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} has been temporarily disabled.
344 * <p>
345 * The caller shall place this call into their own {@link SecurityUtil#doPrivileged(PrivilegedAction)} block.
346 * </p>
347 * <p>
348 * In case the runtime is not {@link PlatformPropsImpl#JAVA_9} or the logger is not accessible or disabling caused an exception,
349 * the user {@code action} is just executed w/o temporary logger modifications.
350 * </p>
351 * @param action the user action task
352 * @throws RuntimeException is thrown for a caught {@link Throwable} while executing the user {@code action}
353 * @see #hasIllegalAccessLoggerAccess()
354 */
355 public static <T> T doWithoutIllegalAccessLogger(final PrivilegedAction<T> action) throws RuntimeException {
356 if( !hasIllegalAccessError ) {
357 synchronized(o_illegalAccessLoggerSync) {
358 final Object newLogger = null;
359 Object oldLogger = null;
360 try {
361 oldLogger = m_getObjectVolatile.invoke(theUnsafe, c_illegalAccessLoggerClass, o_illegalAccessLoggerOffset);
362 m_putObjectVolatile.invoke(theUnsafe, c_illegalAccessLoggerClass, o_illegalAccessLoggerOffset, newLogger);
363 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
364 // unaccessible ..
365 hasIllegalAccessError = true;
366 if( DEBUG ) {
367 ExceptionUtils.dumpThrowable("UnsafeUtil", e);
368 }
369 return action.run();
370 }
371 try {
372 return action.run();
373 } catch (final Throwable t) {
374 if( DEBUG ) {
375 t.printStackTrace();
376 }
377 throw new RuntimeException(t);
378 } finally {
379 try {
380 m_putObjectVolatile.invoke(theUnsafe, c_illegalAccessLoggerClass, o_illegalAccessLoggerOffset, oldLogger);
381 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
382 // should not happen, worked above @ logger setup
383 hasIllegalAccessError = true;
384 throw new InternalError(e);
385 }
386 }
387 }
388 } else {
389 return action.run();
390 }
391 }
392}
com.jogamp.common.ExceptionUtils
Definition: ExceptionUtils.java:35
com.jogamp.common.ExceptionUtils.dumpThrowable
static void dumpThrowable(final String additionalDescr, final Throwable t)
Dumps a Throwable to System.err in a decorating message including the current thread name,...
Definition: ExceptionUtils.java:135
com.jogamp.common.nio.Buffers
Utility methods allowing easy java.nio.Buffer manipulations.
Definition: Buffers.java:70
com.jogamp.common.nio.Buffers.getDirectBufferAddress
static long getDirectBufferAddress(final Object directBuffer)
Definition: Buffers.java:2086
com.jogamp.common.util.UnsafeUtil
Utility methods allowing easy access to certain sun.misc.Unsafe functionality.
Definition: UnsafeUtil.java:46
com.jogamp.common.util.UnsafeUtil.hasIllegalAccessLoggerAccess
static boolean hasIllegalAccessLoggerAccess()
Returns true if access to jdk.internal.module.IllegalAcessLogger's logger field is available and has ...
Definition: UnsafeUtil.java:340
com.jogamp.common.util.UnsafeUtil.putLong
static void putLong(final Object o, final long offset, final long x)
Definition: UnsafeUtil.java:306
com.jogamp.common.util.UnsafeUtil.UnsafeUtil
UnsafeUtil()
Definition: UnsafeUtil.java:53
com.jogamp.common.util.UnsafeUtil.invokeCleaner
static boolean invokeCleaner(final ByteBuffer bb)
Access to sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer).
Definition: UnsafeUtil.java:205
com.jogamp.common.util.UnsafeUtil.objectFieldOffset
static long objectFieldOffset(final Field f)
Definition: UnsafeUtil.java:235
com.jogamp.common.util.UnsafeUtil.getLong
static long getLong(final long address)
Definition: UnsafeUtil.java:318
com.jogamp.common.util.UnsafeUtil.staticFieldOffset
static long staticFieldOffset(final Field f)
Definition: UnsafeUtil.java:221
com.jogamp.common.util.UnsafeUtil.putObject
static void putObject(final Object o, final long offset, final Object x)
Definition: UnsafeUtil.java:260
com.jogamp.common.util.UnsafeUtil.getDirectBufferAddress
static long getDirectBufferAddress(final Buffer buffer)
Definition: UnsafeUtil.java:325
com.jogamp.common.util.UnsafeUtil.getObject
static Object getObject(final Object o, final long offset)
Definition: UnsafeUtil.java:250
com.jogamp.common.util.UnsafeUtil.getLong
static long getLong(final Object o, final long offset)
Definition: UnsafeUtil.java:292
com.jogamp.common.util.UnsafeUtil.doWithoutIllegalAccessLogger
static< T > T doWithoutIllegalAccessLogger(final PrivilegedAction< T > action)
Issue the given user action while jdk.internal.module.IllegalAcessLogger's logger has been temporaril...
Definition: UnsafeUtil.java:355
com.jogamp.common.util.UnsafeUtil.putLong
static void putLong(final long address, final long x)
Definition: UnsafeUtil.java:321
com.jogamp.common.util.UnsafeUtil.getObjectVolatile
static Object getObjectVolatile(final Object o, final long offset)
Definition: UnsafeUtil.java:271
com.jogamp.common.util.UnsafeUtil.putObjectVolatile
static void putObjectVolatile(final Object o, final long offset, final Object x)
Definition: UnsafeUtil.java:281
com.jogamp.common.util.UnsafeUtil.hasInvokeCleaner
static boolean hasInvokeCleaner()
Returns true if sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer) is available and has not caused an...
Definition: UnsafeUtil.java:194