1 /*
  2  * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
 27  * @modules jdk.incubator.foreign/jdk.internal.foreign
 28  * @build NativeTestHelper CallGeneratorHelper TestUpcallBase
 29  *
 30  * @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
 31  *   --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17
 32  *   TestUpcallAsync
 33  */
 34 
 35 import jdk.incubator.foreign.CLinker;
 36 import jdk.incubator.foreign.FunctionDescriptor;
 37 import jdk.incubator.foreign.MemoryLayout;
 38 import jdk.incubator.foreign.MemorySegment;
 39 import jdk.incubator.foreign.NativeSymbol;
 40 import jdk.incubator.foreign.ResourceScope;
 41 import jdk.incubator.foreign.SegmentAllocator;
 42 import org.testng.annotations.Test;
 43 
 44 import java.lang.invoke.MethodHandle;
 45 import java.lang.invoke.MethodHandles;
 46 import java.util.ArrayList;
 47 import java.util.HashMap;
 48 import java.util.List;
 49 import java.util.Map;
 50 import java.util.function.Consumer;
 51 
 52 public class TestUpcallAsync extends TestUpcallBase {
 53 
 54     static {
 55         System.loadLibrary("TestUpcall");
 56         System.loadLibrary("AsyncInvokers");
 57     }
 58 
 59     @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
 60     public void testUpcallsAsync(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
 61         List<Consumer<Object>> returnChecks = new ArrayList<>();
 62         List<Consumer<Object[]>> argChecks = new ArrayList<>();
 63         NativeSymbol addr = LOOKUP.lookup(fName).get();
 64         try (ResourceScope scope = ResourceScope.newSharedScope()) {
 65             SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
 66             FunctionDescriptor descriptor = function(ret, paramTypes, fields);
 67             MethodHandle mh = downcallHandle(ABI, addr, allocator, descriptor);
 68             Object[] args = makeArgs(ResourceScope.newImplicitScope(), ret, paramTypes, fields, returnChecks, argChecks);
 69 
 70             mh = mh.asSpreader(Object[].class, args.length);
 71             mh = MethodHandles.insertArguments(mh, 0, (Object) args);
 72             FunctionDescriptor callbackDesc = descriptor.returnLayout()
 73                     .map(FunctionDescriptor::of)
 74                     .orElse(FunctionDescriptor.ofVoid());
 75             NativeSymbol callback = ABI.upcallStub(mh.asType(CLinker.upcallType(callbackDesc)), callbackDesc, scope);
 76 
 77             MethodHandle invoker = asyncInvoker(ret, ret == Ret.VOID ? null : paramTypes.get(0), fields);
 78 
 79             Object res = invoker.type().returnType() == MemorySegment.class
 80                     ? invoker.invoke(allocator, callback)
 81                     : invoker.invoke(callback);
 82             argChecks.forEach(c -> c.accept(args));
 83             if (ret == Ret.NON_VOID) {
 84                 returnChecks.forEach(c -> c.accept(res));
 85             }
 86         }
 87     }
 88 
 89     private static final Map<String, MethodHandle> INVOKERS = new HashMap<>();
 90 
 91     private MethodHandle asyncInvoker(Ret ret, ParamType returnType, List<StructFieldType> fields) {
 92         if (ret == Ret.VOID) {
 93             String name = "call_async_V";
 94             return INVOKERS.computeIfAbsent(name, symbol ->
 95                     ABI.downcallHandle(
 96                             LOOKUP.lookup(symbol).orElseThrow(),
 97                             FunctionDescriptor.ofVoid(C_POINTER)));
 98         }
 99 
100         String name = "call_async_" + returnType.name().charAt(0)
101                 + (returnType == ParamType.STRUCT ? "_" + sigCode(fields) : "");
102 
103         return INVOKERS.computeIfAbsent(name, symbol -> {
104             NativeSymbol invokerSymbol = LOOKUP.lookup(symbol).orElseThrow();
105             MemoryLayout returnLayout = returnType.layout(fields);
106             FunctionDescriptor desc = FunctionDescriptor.of(returnLayout, C_POINTER);
107 
108             return ABI.downcallHandle(invokerSymbol, desc);
109         });
110     }
111 
112 }