jaulib v1.3.0
Jau Support Library (C++, Java, ..)
UnsafeUtil.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2019 Gothel Software e.K.
5 * Copyright (c) 2019 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26package org.jau.lang;
27
28import java.lang.reflect.Field;
29import java.lang.reflect.InvocationTargetException;
30import java.lang.reflect.Method;
31import java.nio.ByteBuffer;
32import java.security.PrivilegedAction;
33
34import org.jau.sec.SecurityUtil;
35import org.jau.sys.Debug;
36import org.jau.sys.PlatformProps;
37
38/**
39 * Utility methods allowing easy access to certain {@link sun.misc.Unsafe} functionality.
40 */
41public class UnsafeUtil {
42
43 static final boolean DEBUG;
44 static {
45 DEBUG = Debug.debug("UnsafeUtil");
46 }
47
48 protected UnsafeUtil() {}
49
50 private static final Object theUnsafe;
51 private static final Method unsafeCleanBB;
52 private static volatile boolean hasUnsafeCleanBBError; /** OK to be lazy on thread synchronization, just for early out **/
53
54 private static final Method staticFieldOffset;
55 private static final Method getObjectVolatile;
56 private static final Method putObjectVolatile;
57 private static volatile boolean hasGetPutObjectVolatile;
58
59 private static final Class<?> illegalAccessLoggerClass;
60 private static final Long illegalAccessLoggerOffset;
61 private static final Object illegalAccessLoggerSync = new Object();
62 private static volatile boolean hasIllegalAccessError;
63
64 static {
65 final Object[] _theUnsafe = { null };
66 final Method[] _cleanBB = { null };
67 final Method[] _staticFieldOffset = { null };
68 final Method[] _objectVolatile = { null, null }; // unsafeGetObjectVolatile, unsafePutObjectVolatile
69 final Class<?>[] _illegalAccessLoggerClass = { null };
70 final Long[] _loggerOffset = { null };
71
72 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
73 @Override
74 public Object run() {
75 Class<?> unsafeClass = null;
76 try {
77 // Using: sun.misc.Unsafe { public void invokeCleaner(java.nio.ByteBuffer directBuffer); }
78 unsafeClass = Class.forName("sun.misc.Unsafe");
79 {
80 final Field f = unsafeClass.getDeclaredField("theUnsafe");
81 f.setAccessible(true);
82 _theUnsafe[0] = f.get(null);
83 }
84 _cleanBB[0] = unsafeClass.getMethod("invokeCleaner", java.nio.ByteBuffer.class);
85 _cleanBB[0].setAccessible(true);
86 } catch(final Throwable t) {
87 if( DEBUG ) {
88 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
89 }
90 }
91 if( null != _theUnsafe[0] && PlatformProps.JAVA_9 ) {
92 try {
93 _staticFieldOffset[0] = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);
94 _objectVolatile[0] = unsafeClass.getDeclaredMethod("getObjectVolatile", Object.class, long.class);
95 _objectVolatile[1] = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
96
97 if( PlatformProps.JAVA_9 ) {
98 _illegalAccessLoggerClass[0] = Class.forName("jdk.internal.module.IllegalAccessLogger");
99 final Field loggerField = _illegalAccessLoggerClass[0].getDeclaredField("logger");
100 _loggerOffset[0] = (Long) _staticFieldOffset[0].invoke(_theUnsafe[0], loggerField);
101 }
102 } catch(final Throwable t) {
103 if( DEBUG ) {
104 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
105 }
106 }
107 }
108 return null;
109 } } );
110 theUnsafe = _theUnsafe[0];
111 unsafeCleanBB = _cleanBB[0];
112 hasUnsafeCleanBBError = null == theUnsafe || null == unsafeCleanBB;
113 if( DEBUG ) {
114 System.err.println("UnsafeUtil.init: hasTheUnsafe: "+(null!=theUnsafe)+", hasInvokeCleaner: "+!hasUnsafeCleanBBError);
115 }
116
117 staticFieldOffset = _staticFieldOffset[0];
118 getObjectVolatile = _objectVolatile[0];
119 putObjectVolatile = _objectVolatile[1];
120 hasGetPutObjectVolatile = null != staticFieldOffset && null != getObjectVolatile && null != putObjectVolatile;
121 illegalAccessLoggerClass = _illegalAccessLoggerClass[0];
122 illegalAccessLoggerOffset = _loggerOffset[0];
123 hasIllegalAccessError = !hasGetPutObjectVolatile || null == illegalAccessLoggerClass || null == illegalAccessLoggerOffset;
124 if( DEBUG ) {
125 System.err.println("UnsafeUtil.init: hasUnsafeGetPutObjectVolatile: "+hasGetPutObjectVolatile+", hasUnsafeIllegalAccessLogger: "+!hasIllegalAccessError);
126 }
127 }
128
129 /**
130 * Returns {@code true} if {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}
131 * is available and has not caused an exception.
132 * @see #invokeCleaner(ByteBuffer)
133 */
134 public static boolean hasInvokeCleaner() { return !hasUnsafeCleanBBError; }
135
136 /**
137 * Access to {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}.
138 * <p>
139 * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer},
140 * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method once.
141 * </p>
142 * @return {@code true} if successful, otherwise {@code false}.
143 * @see #hasInvokeCleaner()
144 */
145 public static boolean invokeCleaner(final ByteBuffer bb) {
146 if( hasUnsafeCleanBBError || !bb.isDirect() ) {
147 return false;
148 }
149 try {
150 unsafeCleanBB.invoke(theUnsafe, bb);
151 return true;
152 } catch(final Throwable t) {
153 hasUnsafeCleanBBError = true;
154 if( DEBUG ) {
155 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
156 }
157 return false;
158 }
159 }
160
161 /**
162 * Returns {@code true} if access to {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} field
163 * is available and has not caused an exception.
164 * @see #doWithoutIllegalAccessLogger(PrivilegedAction)
165 */
166 public static boolean hasIllegalAccessLoggerAccess() { return !hasIllegalAccessError; }
167
168 /**
169 * Issue the given user {@code action} while {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} has been temporarily disabled.
170 * <p>
171 * The caller shall place this call into their own {@link AccessController#doPrivileged(PrivilegedAction)} block.
172 * </p>
173 * <p>
174 * In case the runtime is not {@link PlatformProps#JAVA_9} or the logger is not accessible or disabling caused an exception,
175 * the user {@code action} is just executed w/o temporary logger modifications.
176 * </p>
177 * @param action the user action task
178 * @throws RuntimeException is thrown for a caught {@link Throwable} while executing the user {@code action}
179 * @see #hasIllegalAccessLoggerAccess()
180 */
181 public static <T> T doWithoutIllegalAccessLogger(final PrivilegedAction<T> action) throws RuntimeException {
182 if( !hasIllegalAccessError ) {
183 synchronized(illegalAccessLoggerSync) {
184 final Object newLogger = null;
185 Object oldLogger = null;
186 try {
187 oldLogger = getObjectVolatile.invoke(theUnsafe, illegalAccessLoggerClass, illegalAccessLoggerOffset);
188 putObjectVolatile.invoke(theUnsafe, illegalAccessLoggerClass, illegalAccessLoggerOffset, newLogger);
189 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
190 // unaccessible ..
191 hasIllegalAccessError = true;
192 if( DEBUG ) {
193 ExceptionUtils.dumpThrowable("UnsafeUtil", e);
194 }
195 return action.run();
196 }
197 try {
198 return action.run();
199 } catch (final Throwable t) {
200 if( DEBUG ) {
201 t.printStackTrace();
202 }
203 throw new RuntimeException(t);
204 } finally {
205 try {
206 putObjectVolatile.invoke(theUnsafe, illegalAccessLoggerClass, illegalAccessLoggerOffset, oldLogger);
207 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
208 // should not happen, worked above @ logger setup
209 hasIllegalAccessError = true;
210 throw new InternalError(e);
211 }
212 }
213 }
214 } else {
215 return action.run();
216 }
217 }
218}
static void dumpThrowable(final String additionalDescr, final Throwable t)
Dumps a Throwable to System.err in a decorating message including the current thread name,...
Utility methods allowing easy access to certain sun.misc.Unsafe functionality.
Definition: UnsafeUtil.java:41
static boolean hasInvokeCleaner()
Returns true if sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer) is available and has not caused an...
static< T > T doWithoutIllegalAccessLogger(final PrivilegedAction< T > action)
Issue the given user action while jdk.internal.module.IllegalAcessLogger's logger has been temporaril...
static boolean hasIllegalAccessLoggerAccess()
Returns true if access to jdk.internal.module.IllegalAcessLogger's logger field is available and has ...
static boolean invokeCleaner(final ByteBuffer bb)
Access to sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer).
Helper routines for logging and debugging.
Definition: Debug.java:35
static final boolean debug(final String subcomponent)
Definition: Debug.java:63