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

GlueGen v2.6.0 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.ByteBuffer;
34import java.security.PrivilegedAction;
35
36import com.jogamp.common.ExceptionUtils;
37
38import jogamp.common.Debug;
39import jogamp.common.os.PlatformPropsImpl;
40
41/**
42 * Utility methods allowing easy access to certain {@link sun.misc.Unsafe} functionality.
43 */
44 public class UnsafeUtil {
45
46 static final boolean DEBUG;
47 static {
48 DEBUG = Debug.debug("UnsafeUtil");
49 }
50
51 protected UnsafeUtil() {}
52
53 private static final Object theUnsafe;
54 private static final Method unsafeCleanBB;
55 private static volatile boolean hasUnsafeCleanBBError; /** OK to be lazy on thread synchronization, just for early out **/
56
57 private static final Method m_getObject;
58 private static final Method m_putObject;
59 private static final Method m_getObjectVolatile;
60 private static final Method m_putObjectVolatile;
61
62 private static final Method m_getLong; // long getLong(Object o, long offset)
63 private static final Method m_putLong; // putLong(Object o, long offset, long x)
64
65 private static final Method m_staticFieldOffset;
66
67 private static final Class<?> c_illegalAccessLoggerClass;
68 private static final Long o_illegalAccessLoggerOffset;
69 private static final Object o_illegalAccessLoggerSync = new Object();
70 private static volatile boolean hasIllegalAccessError;
71
72 static {
73 final Object[] _theUnsafe = { null };
74 final Method[] _cleanBB = { null };
75 final Method[] _staticFieldOffset = { null };
76 final Method[] _getPutObject = { null, null }; // getObject, putObject
77 final Method[] _getPutObjectVolatile = { null, null }; // getObjectVolatile, putObjectVolatile
78 final Method[] _getPutLong = { null, null }; // long getLong(Object o, long offset), putLong(Object o, long offset, long x)
79 final Class<?>[] _illegalAccessLoggerClass = { null };
80 final Long[] _loggerOffset = { null };
81
82 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
83 @Override
84 public Object run() {
85 Class<?> unsafeClass = null;
86 try {
87 // Using: sun.misc.Unsafe { public void invokeCleaner(java.nio.ByteBuffer directBuffer); }
88 unsafeClass = Class.forName("sun.misc.Unsafe");
89 {
90 final Field f = unsafeClass.getDeclaredField("theUnsafe");
91 f.setAccessible(true);
92 _theUnsafe[0] = f.get(null);
93 }
94 _cleanBB[0] = unsafeClass.getMethod("invokeCleaner", java.nio.ByteBuffer.class);
95 _cleanBB[0].setAccessible(true);
96 } catch(final Throwable t) {
97 if( DEBUG ) {
98 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
99 }
100 }
101 if( null != _theUnsafe[0] ) {
102 try {
103 _getPutObject[0] = unsafeClass.getDeclaredMethod("getObject", Object.class, long.class);
104 _getPutObject[1] = unsafeClass.getDeclaredMethod("putObject", Object.class, long.class, Object.class);
105 } catch(final Throwable t) {
106 if( DEBUG ) {
107 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
108 }
109 }
110 try {
111 _getPutObjectVolatile[0] = unsafeClass.getDeclaredMethod("getObjectVolatile", Object.class, long.class);
112 _getPutObjectVolatile[1] = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
113 } catch(final Throwable t) {
114 if( DEBUG ) {
115 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
116 }
117 }
118 try {
119 _getPutLong[0] = unsafeClass.getDeclaredMethod("getLong", Object.class, long.class);
120 _getPutLong[1] = unsafeClass.getDeclaredMethod("putLong", Object.class, long.class, long.class);
121 } catch(final Throwable t) {
122 if( DEBUG ) {
123 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
124 }
125 }
126 try {
127 _staticFieldOffset[0] = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);
128 } catch(final Throwable t) {
129 if( DEBUG ) {
130 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
131 }
132 }
133 if( PlatformPropsImpl.JAVA_9 && null != _staticFieldOffset[0] ) {
134 try {
135 _illegalAccessLoggerClass[0] = Class.forName("jdk.internal.module.IllegalAccessLogger");
136 final Field loggerField = _illegalAccessLoggerClass[0].getDeclaredField("logger");
137 _loggerOffset[0] = (Long) _staticFieldOffset[0].invoke(_theUnsafe[0], loggerField);
138 } catch(final Throwable t) {
139 if( DEBUG ) {
140 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
141 }
142 }
143 }
144 }
145 return null;
146 } } );
147 theUnsafe = _theUnsafe[0];
148 unsafeCleanBB = _cleanBB[0];
149 hasUnsafeCleanBBError = null == theUnsafe || null == unsafeCleanBB;
150 if( DEBUG ) {
151 System.err.println("UnsafeUtil.init: hasTheUnsafe: "+(null!=theUnsafe)+", hasInvokeCleaner: "+!hasUnsafeCleanBBError);
152 }
153 m_staticFieldOffset = _staticFieldOffset[0];
154 m_getObject = _getPutObject[0];
155 m_putObject = _getPutObject[1];
156 m_getObjectVolatile = _getPutObjectVolatile[0];
157 m_putObjectVolatile = _getPutObjectVolatile[1];
158 m_getLong = _getPutLong[0];
159 m_putLong = _getPutLong[1];
160 c_illegalAccessLoggerClass = _illegalAccessLoggerClass[0];
161 o_illegalAccessLoggerOffset = _loggerOffset[0];
162 hasIllegalAccessError = null == m_getObjectVolatile || null == m_putObjectVolatile ||
163 null == c_illegalAccessLoggerClass || null == o_illegalAccessLoggerOffset;
164 if( DEBUG ) {
165 System.err.println("UnsafeUtil.init: hasUnsafeIllegalAccessLogger: "+!hasIllegalAccessError);
166 }
167 }
168
169 /**
170 * Returns {@code true} if {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}
171 * is available and has not caused an exception.
172 * @see #invokeCleaner(ByteBuffer)
173 */
174 public static boolean hasInvokeCleaner() { return !hasUnsafeCleanBBError; }
175
176 /**
177 * Access to {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}.
178 * <p>
179 * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer},
180 * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method once.
181 * </p>
182 * @return {@code true} if successful, otherwise {@code false}.
183 * @see #hasInvokeCleaner()
184 */
185 public static boolean invokeCleaner(final ByteBuffer bb) {
186 if( hasUnsafeCleanBBError || !bb.isDirect() ) {
187 return false;
188 }
189 try {
190 unsafeCleanBB.invoke(theUnsafe, bb);
191 return true;
192 } catch(final Throwable t) {
193 hasUnsafeCleanBBError = true;
194 if( DEBUG ) {
195 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
196 }
197 return false;
198 }
199 }
200
201 public static long staticFieldOffset(final Field f) {
202 if( null != m_staticFieldOffset) {
203 throw new UnsupportedOperationException("staticFieldOffset");
204 }
205 try {
206 final Long res = (Long)m_staticFieldOffset.invoke(theUnsafe, f);
207 if( null != res ) {
208 return res.longValue();
209 }
210 throw new RuntimeException("staticFieldOffset: f "+f);
211 } catch (IllegalAccessException | InvocationTargetException e) {
212 throw new RuntimeException("UnsafeUtil");
213 }
214 }
215
216 public static Object getObject(final Object o, final long offset) {
217 if( null != m_getObject) {
218 throw new UnsupportedOperationException("getObject");
219 }
220 try {
221 return m_getObject.invoke(theUnsafe, o, offset);
222 } catch (IllegalAccessException | InvocationTargetException e) {
223 throw new RuntimeException("UnsafeUtil: o "+o+", offset "+offset, e);
224 }
225 }
226 public static void putObject(final Object o, final long offset, final Object x) {
227 if( null != m_putObject) {
228 throw new UnsupportedOperationException("putObject");
229 }
230 try {
231 m_putObject.invoke(theUnsafe, o, offset, x);
232 } catch (IllegalAccessException | InvocationTargetException e) {
233 throw new RuntimeException("UnsafeUtil");
234 }
235 }
236
237 public static Object getObjectVolatile(final Object o, final long offset) {
238 if( null != m_getObjectVolatile) {
239 throw new UnsupportedOperationException("getObjectVolatile");
240 }
241 try {
242 return m_getObjectVolatile.invoke(theUnsafe, o, offset);
243 } catch (IllegalAccessException | InvocationTargetException e) {
244 throw new RuntimeException("UnsafeUtil");
245 }
246 }
247 public static void putObjectVolatile(final Object o, final long offset, final Object x) {
248 if( null != m_putObjectVolatile) {
249 throw new UnsupportedOperationException("putObjectVolatile");
250 }
251 try {
252 m_putObjectVolatile.invoke(theUnsafe, o, offset, x);
253 } catch (IllegalAccessException | InvocationTargetException e) {
254 throw new RuntimeException("UnsafeUtil");
255 }
256 }
257
258 public static long getLong(final Object o, final long offset) {
259 if(null != m_getLong) {
260 throw new UnsupportedOperationException("getLong");
261 }
262 try {
263 final Long res = (Long)m_getLong.invoke(theUnsafe, o, offset);
264 if( null != res ) {
265 return res.longValue();
266 }
267 throw new RuntimeException("getLong: o "+o+", offset "+offset);
268 } catch (IllegalAccessException | InvocationTargetException e) {
269 throw new RuntimeException("UnsafeUtil");
270 }
271 }
272 public static void putLong(final Object o, final long offset, final long x) {
273 if(null != m_putLong) {
274 throw new UnsupportedOperationException("putLong");
275 }
276 try {
277 m_putLong.invoke(theUnsafe, o, offset);
278 throw new RuntimeException("putLong: o "+o+", offset "+offset+", x "+x);
279 } catch (IllegalAccessException | InvocationTargetException e) {
280 throw new RuntimeException("UnsafeUtil");
281 }
282 }
283
284 public static long getLong(final long address) {
285 return getLong(null, address);
286 }
287 public static void putLong(final long address, final long x) {
288 putLong(null, address, x);
289 }
290
291 /**
292 * Returns {@code true} if access to {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} field
293 * is available and has not caused an exception.
294 * @see #doWithoutIllegalAccessLogger(PrivilegedAction)
295 */
296 public static boolean hasIllegalAccessLoggerAccess() { return !hasIllegalAccessError; }
297
298 /**
299 * Issue the given user {@code action} while {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} has been temporarily disabled.
300 * <p>
301 * The caller shall place this call into their own {@link SecurityUtil#doPrivileged(PrivilegedAction)} block.
302 * </p>
303 * <p>
304 * In case the runtime is not {@link PlatformPropsImpl#JAVA_9} or the logger is not accessible or disabling caused an exception,
305 * the user {@code action} is just executed w/o temporary logger modifications.
306 * </p>
307 * @param action the user action task
308 * @throws RuntimeException is thrown for a caught {@link Throwable} while executing the user {@code action}
309 * @see #hasIllegalAccessLoggerAccess()
310 */
311 public static <T> T doWithoutIllegalAccessLogger(final PrivilegedAction<T> action) throws RuntimeException {
312 if( !hasIllegalAccessError ) {
313 synchronized(o_illegalAccessLoggerSync) {
314 final Object newLogger = null;
315 Object oldLogger = null;
316 try {
317 oldLogger = m_getObjectVolatile.invoke(theUnsafe, c_illegalAccessLoggerClass, o_illegalAccessLoggerOffset);
318 m_putObjectVolatile.invoke(theUnsafe, c_illegalAccessLoggerClass, o_illegalAccessLoggerOffset, newLogger);
319 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
320 // unaccessible ..
321 hasIllegalAccessError = true;
322 if( DEBUG ) {
323 ExceptionUtils.dumpThrowable("UnsafeUtil", e);
324 }
325 return action.run();
326 }
327 try {
328 return action.run();
329 } catch (final Throwable t) {
330 if( DEBUG ) {
331 t.printStackTrace();
332 }
333 throw new RuntimeException(t);
334 } finally {
335 try {
336 m_putObjectVolatile.invoke(theUnsafe, c_illegalAccessLoggerClass, o_illegalAccessLoggerOffset, oldLogger);
337 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
338 // should not happen, worked above @ logger setup
339 hasIllegalAccessError = true;
340 throw new InternalError(e);
341 }
342 }
343 }
344 } else {
345 return action.run();
346 }
347 }
348}
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.util.UnsafeUtil
Utility methods allowing easy access to certain sun.misc.Unsafe functionality.
Definition: UnsafeUtil.java:44
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:296
com.jogamp.common.util.UnsafeUtil.putLong
static void putLong(final Object o, final long offset, final long x)
Definition: UnsafeUtil.java:272
com.jogamp.common.util.UnsafeUtil.UnsafeUtil
UnsafeUtil()
Definition: UnsafeUtil.java:51
com.jogamp.common.util.UnsafeUtil.invokeCleaner
static boolean invokeCleaner(final ByteBuffer bb)
Access to sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer).
Definition: UnsafeUtil.java:185
com.jogamp.common.util.UnsafeUtil.getLong
static long getLong(final long address)
Definition: UnsafeUtil.java:284
com.jogamp.common.util.UnsafeUtil.staticFieldOffset
static long staticFieldOffset(final Field f)
Definition: UnsafeUtil.java:201
com.jogamp.common.util.UnsafeUtil.putObject
static void putObject(final Object o, final long offset, final Object x)
Definition: UnsafeUtil.java:226
com.jogamp.common.util.UnsafeUtil.getObject
static Object getObject(final Object o, final long offset)
Definition: UnsafeUtil.java:216
com.jogamp.common.util.UnsafeUtil.getLong
static long getLong(final Object o, final long offset)
Definition: UnsafeUtil.java:258
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:311
com.jogamp.common.util.UnsafeUtil.putLong
static void putLong(final long address, final long x)
Definition: UnsafeUtil.java:287
com.jogamp.common.util.UnsafeUtil.getObjectVolatile
static Object getObjectVolatile(final Object o, final long offset)
Definition: UnsafeUtil.java:237
com.jogamp.common.util.UnsafeUtil.putObjectVolatile
static void putObjectVolatile(final Object o, final long offset, final Object x)
Definition: UnsafeUtil.java:247
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:174