3030import java .lang .foreign .Linker ;
3131import java .lang .foreign .MemoryLayout ;
3232import java .lang .foreign .MemorySegment ;
33- import java .lang .foreign .StructLayout ;
3433import java .lang .invoke .MethodHandle ;
3534import java .lang .invoke .MethodHandles ;
3635import java .lang .invoke .MethodType ;
3938public final class ErrnoUtil {
4039
4140 private static final Unsafe UNSAFE = Unsafe .getUnsafe ();
42- private static final MethodHandles .Lookup ARGUS = MethodHandles .lookup ();
43- private static final TerminatingThreadLocal <MemorySegment > TL = new TerminatingThreadLocal <>(){
41+ private static final MethodHandles .Lookup LOOKUP = MethodHandles .lookup ();
42+
43+ private static final TerminatingThreadLocal <MemorySegment > TL = new TerminatingThreadLocal <>() {
4444 @ Override
4545 protected void threadTerminated (MemorySegment value ) {
4646 free (value );
4747 }
4848 };
4949
50- private static final StructLayout CAPTURE_STATE_LAYOUT = Linker . Option . captureStateLayout ();
51- private static final VarHandle ERRNO_HANDLE =
52- CAPTURE_STATE_LAYOUT .varHandle (MemoryLayout .PathElement .groupElement ("errno" ));
50+ private static final VarHandle ERRNO_HANDLE =
51+ Linker . Option . captureStateLayout ()
52+ .varHandle (MemoryLayout .PathElement .groupElement ("errno" ));
5353
5454 private static final MethodHandle ACQUIRE_MH =
55- MhUtil .findStatic (ARGUS , "acquireCaptureStateSegment" ,
55+ MhUtil .findStatic (LOOKUP , "acquireCaptureStateSegment" ,
5656 MethodType .methodType (MemorySegment .class ));
57- private static final MethodHandle RETURN_FILTER_MH =
58- MhUtil .findStatic (ARGUS , "returnFilter" ,
57+
58+ private static final MethodHandle INT_RETURN_FILTER_MH =
59+ MhUtil .findStatic (LOOKUP , "returnFilter" ,
5960 MethodType .methodType (int .class , int .class ));
6061
61- private ErrnoUtil () {}
62+ private static final MethodHandle LONG_RETURN_FILTER_MH =
63+ MhUtil .findStatic (LOOKUP , "returnFilter" ,
64+ MethodType .methodType (long .class , long .class ));
65+
66+ private ErrnoUtil () {
67+ }
6268
6369 /**
64- * {@return a new MethodHandle that returns the returned int value if the
65- * returned value is non-negative, or else the negative errno}
70+ * {@return a new MethodHandle that adapts the provided {@code target} so that it
71+ * directly returns the same value as the {@code target} if it is
72+ * non-negative, otherwise returns the negated errno}
6673 * <p>
67- * As such, this method is suitable for adapting method handles for system calls
68- * (e.g. {@code open()}, {@code read()}, and {@code close()}).
74+ * This method is suitable for adapting system-call method handles(e.g.
75+ * {@code open()}, {@code read()}, and {@code close()}). Clients can check the return
76+ * value as shown in this example:
77+ * {@snippet lang = java:
78+ * static final MethodHandle OPEN = ErrnoUtil.adaptSystemCall(CAPTURING_OPEN);
6979 *
70- * @param target that returns an {@code int} and has an errno MemorySegment as
71- * the first parameter
72- * @throws IllegalArgumentException if the provided {@code target}'s return type
73- * is not {@code int}
80+ * try {
81+ * int fh = (int)OPEN.invoke(pathName, flags);
82+ * if (fh < 0) {
83+ * throw new IOException("Error opening file: errno = " + (-fh));
84+ * }
85+ * processFile(fh);
86+ * }
87+ *
88+ *}
89+ *
90+ * @param target method handle that returns an {@code int} or a {@code long} and has
91+ * an errno MemorySegment as its first parameter
92+ * @throws IllegalArgumentException if the provided {@code target}'s return type is
93+ * not {@code int} or {@code long}
7494 * @throws IllegalArgumentException if the provided {@code target}'s first parameter
75- * type is not {@linkplain MemorySegment}
95+ * type is not {@linkplain MemorySegment}
7696 */
7797 public static MethodHandle adaptSystemCall (MethodHandle target ) {
78- if (target .type ().returnType () != int .class ) {
79- throw new IllegalArgumentException ("The provided target " + target
80- + " does not return an int" );
98+ // Implicit null check
99+ final Class <?> returnType = target .type ().returnType ();
100+ if (!(returnType .equals (int .class ) || returnType .equals (long .class ))) {
101+ throw illegalArgNot (target , "return an int or a long" );
81102 }
82103 if (target .type ().parameterType (0 ) != MemorySegment .class ) {
83- throw new IllegalArgumentException ("The provided target " + target
84- + " does not have a MemorySegment as the first parameter" );
104+ throw illegalArgNot (target , "have a MemorySegment as the first parameter" );
85105 }
86- // (MemorySegment, C*)int -> (C*)int
106+ // (MemorySegment, C*)( int | long) -> (C*)( int | long)
87107 target = MethodHandles .collectArguments (target , 0 , ACQUIRE_MH );
88- // (C*)int -> (C*)int
89- target = MethodHandles .filterReturnValue (target , RETURN_FILTER_MH );
90- return target ;
108+ // (C*)( int | long) -> (C*)( int | long)
109+ return MethodHandles .filterReturnValue (target ,
110+ returnType . equals ( int . class ) ? INT_RETURN_FILTER_MH : LONG_RETURN_FILTER_MH ) ;
91111 }
92112
93113 // Used reflectively via ACQUIRE_MH
@@ -101,19 +121,32 @@ private static MemorySegment acquireCaptureStateSegment() {
101121
102122 // Used reflectively via RETURN_FILTER_MH
103123 private static int returnFilter (int result ) {
104- return result >= 0
105- ? result
106- : -(int ) ERRNO_HANDLE .get (acquireCaptureStateSegment (), 0L );
124+ return result >= 0 ? result : -errno ();
125+ }
126+
127+ // Used reflectively via RETURN_FILTER_MH
128+ private static long returnFilter (long result ) {
129+ return result >= 0 ? result : -errno ();
130+ }
131+
132+ private static int errno () {
133+ return (int ) ERRNO_HANDLE .get (acquireCaptureStateSegment (), 0L );
107134 }
108135
109136 @ SuppressWarnings ("restricted" )
110137 private static MemorySegment malloc () {
111- long address = UNSAFE .allocateMemory (CAPTURE_STATE_LAYOUT .byteSize ());
112- return MemorySegment .ofAddress (address ).reinterpret (CAPTURE_STATE_LAYOUT .byteSize ());
138+ final long size = Linker .Option .captureStateLayout ().byteSize ();
139+ final long address = UNSAFE .allocateMemory (size );
140+ return MemorySegment .ofAddress (address ).reinterpret (size );
113141 }
114142
115143 private static void free (MemorySegment segment ) {
116144 UNSAFE .freeMemory (segment .address ());
117145 }
118146
147+ private static IllegalArgumentException illegalArgNot (MethodHandle target , String info ) {
148+ return new IllegalArgumentException ("The provided target " + target
149+ + " does not " + info );
150+ }
151+
119152}
0 commit comments