diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/async/fd/IoUringSelectedReadableFd.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/async/fd/IoUringSelectedReadableFd.java index 3180b9c..a253897 100644 --- a/panama-uring/src/main/java/top/dreamlike/panama/uring/async/fd/IoUringSelectedReadableFd.java +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/async/fd/IoUringSelectedReadableFd.java @@ -20,7 +20,6 @@ import java.util.concurrent.CompletableFuture; public interface IoUringSelectedReadableFd extends IoUringOperator, NativeFd { - IoUringBufferRing bufferRing(); default CancelableFuture asyncSelectedRead(int len, int offset) { @@ -30,7 +29,7 @@ default CancelableFuture asyncSelectedRead(int len, int offset) sqe.setFlags((byte) (sqe.getFlags() | IoUringConstant.IOSQE_BUFFER_SELECT)); sqe.setBufGroup(bufferRing.getBufferGroupId()); }) - .thenCompose(cqe -> { + .thenComposeAsync(cqe -> { int syscallResult = cqe.getRes(); if (syscallResult < 0) { return CompletableFuture.failedFuture(new SyscallException(syscallResult)); @@ -40,7 +39,7 @@ default CancelableFuture asyncSelectedRead(int len, int offset) IoUringBufferRingElement ringElement = bufferRing.removeBuffer(bid).resultNow(); return CompletableFuture.completedFuture(borrowUringBufferRingElement(ringElement, readLen)); } - }); + }, r -> owner().runOnEventLoop(r)); } default CancelableFuture> asyncSelectedReadResult(int len, int offset) { @@ -50,7 +49,8 @@ default CancelableFuture> asyncSelectedRea sqe.setFlags((byte) (sqe.getFlags() | IoUringConstant.IOSQE_BUFFER_SELECT)); sqe.setBufGroup(bufferRing.getBufferGroupId()); }) - .thenApply(cqe -> { + .thenApplyAsync(cqe -> { + //强制制定在eventloop上 防止外部get导致切换线程 IoUringSyscallResult result; if (cqe.getRes() < 0) { result = new IoUringSyscallResult<>(cqe.getRes(), OwnershipMemory.of(MemorySegment.NULL)); @@ -61,7 +61,7 @@ default CancelableFuture> asyncSelectedRea result = new IoUringSyscallResult<>(cqe.getRes(), borrowUringBufferRingElement(ringElement, readLen)); } return result; - }); + }, r -> owner().runOnEventLoop(r)); } @@ -97,7 +97,7 @@ public MemorySegment resource() { @Override public void drop() { - IoUringBufferRingElement waitToRelease = (IoUringBufferRingElement) ELEMENT_VH.compareAndExchange(this, element, (IoUringBufferRingElement)null); + IoUringBufferRingElement waitToRelease = (IoUringBufferRingElement) ELEMENT_VH.compareAndExchange(this, element, (IoUringBufferRingElement) null); if (waitToRelease == null) { return; } diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/eventloop/IoUringEventLoop.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/eventloop/IoUringEventLoop.java index c51d02b..d5cebcb 100644 --- a/panama-uring/src/main/java/top/dreamlike/panama/uring/eventloop/IoUringEventLoop.java +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/eventloop/IoUringEventLoop.java @@ -10,6 +10,7 @@ import top.dreamlike.panama.uring.nativelib.Instance; import top.dreamlike.panama.uring.nativelib.exception.SyscallException; import top.dreamlike.panama.uring.nativelib.helper.NativeHelper; +import top.dreamlike.panama.uring.nativelib.helper.OSIoUringProbe; import top.dreamlike.panama.uring.nativelib.libs.LibUring; import top.dreamlike.panama.uring.nativelib.struct.liburing.*; import top.dreamlike.panama.uring.nativelib.struct.time.KernelTime64Type; @@ -37,6 +38,8 @@ public class IoUringEventLoop extends Thread implements AutoCloseable, Executor { + private static final OSIoUringProbe PROBE = new OSIoUringProbe(); + private static final AtomicInteger count = new AtomicInteger(0); private static final Logger log = LogManager.getLogger(IoUringEventLoop.class); @@ -108,7 +111,6 @@ private void initWakeUpFdMultiShot() { @Override public void run() { while (!hasClosed.get()) { - inWait.set(true); while (true) { ScheduledTask next = scheduledTasks.peek(); if (next != null && next.deadlineNanos <= System.nanoTime()) { @@ -132,7 +134,7 @@ public void run() { kernelTime64Type.setTv_nsec(duration % 1000000000); libUring.io_uring_submit_and_wait_timeout(internalRing, cqePtrs, cqeSize, kernelTime64Type, null); } - + inWait.set(true); processCqes(); } releaseResource(); @@ -167,7 +169,7 @@ public CompletableFuture runOnEventLoop(Supplier callable) { return future; } - private void runOnEventLoop(Runnable runnable) { + public void runOnEventLoop(Runnable runnable) { if (Thread.currentThread() == this) { runWithCatchException(runnable); } else { @@ -212,6 +214,10 @@ private CancelToken fillTemplate(Consumer sqeFunction, Consumer { IoUringSqe sqe = ioUringGetSqe(); sqeFunction.accept(sqe); + if (NativeHelper.enableOpVersionCheck && sqe.getOpcode() > PROBE.getLastOp()) { + Instance.LIB_URING.io_uring_back_sqe(internalRing); + throw new UnsupportedOperationException(sqe.getOpcode() + " is unsupported"); + } sqe.setUser_data(token); callBackMap.put(token, new IoUringCompletionCallBack(sqe.getFd(), sqe.getOpcode(), callback)); if (needSubmit) { @@ -251,6 +257,12 @@ public CancelToken asyncOperation(Consumer sqeFunction, Consumer { IoUringSqe sqe = ioUringGetSqe(); sqeFunction.accept(sqe); + + if (NativeHelper.enableOpVersionCheck && sqe.getOpcode() > PROBE.getLastOp()) { + Instance.LIB_URING.io_uring_back_sqe(internalRing); + throw new UnsupportedOperationException(sqe.getOpcode() + " is unsupported"); + } + sqe.setUser_data(token); if (enableLink) { sqe.setFlags((byte) (sqe.getFlags() | IoUringConstant.IOSQE_IO_LINK)); diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/Instance.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/Instance.java index a21e82a..b955085 100644 --- a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/Instance.java +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/Instance.java @@ -1,7 +1,7 @@ package top.dreamlike.panama.uring.nativelib; + import top.dreamlike.panama.generator.proxy.NativeCallGenerator; import top.dreamlike.panama.generator.proxy.StructProxyGenerator; -import top.dreamlike.panama.uring.nativelib.helper.NativeHelper; import top.dreamlike.panama.uring.nativelib.libs.LibEpoll; import top.dreamlike.panama.uring.nativelib.libs.LibJemalloc; import top.dreamlike.panama.uring.nativelib.libs.LibUring; @@ -20,6 +20,7 @@ public class Instance { public static final LibJemalloc LIB_JEMALLOC = new LibJemalloc() { private static final LibJemalloc FFI = NATIVE_CALL_GENERATOR.generate(LibJemalloc.class); + @Override public MemorySegment malloc(long size) { return FFI.malloc(size).reinterpret(size); @@ -43,12 +44,7 @@ public int posix_memalign(MemorySegment memptr, long alignment, long size) { static { NATIVE_CALL_GENERATOR.indyMode(); - LibUring generate = NATIVE_CALL_GENERATOR.generate(LibUring.class); - if (Boolean.parseBoolean(System.getProperty("enable-detect-os-version", "false"))) { - LIB_URING = NativeHelper.enhanceCheck(generate, LibUring.class); - } else { - LIB_URING = generate; - } + LIB_URING = NATIVE_CALL_GENERATOR.generate(LibUring.class); } diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/NativeHelper.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/NativeHelper.java index d27d56c..68e386a 100644 --- a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/NativeHelper.java +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/NativeHelper.java @@ -6,7 +6,6 @@ import top.dreamlike.panama.uring.eventloop.IoUringEventLoop; import top.dreamlike.panama.uring.helper.LambdaHelper; import top.dreamlike.panama.uring.nativelib.Instance; -import top.dreamlike.panama.uring.nativelib.exception.ErrorKernelVersionException; import top.dreamlike.panama.uring.nativelib.exception.SyscallException; import top.dreamlike.panama.uring.trait.OwnershipResource; @@ -14,17 +13,16 @@ import java.lang.foreign.ValueLayout; import java.lang.invoke.VarHandle; import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.nio.ByteOrder; import java.util.List; -import java.util.Optional; import java.util.function.IntSupplier; public class NativeHelper { public static String JAVA_IO_TMPDIR = System.getProperty("java.io.tmpdir"); + public static boolean enableOpVersionCheck = System.getProperty("enable-detect-os-version", "true").equalsIgnoreCase("true"); + private static final Logger logger = LogManager.getLogger(NativeHelper.class); private static final String[] errStr = new String[257]; @@ -116,25 +114,6 @@ public static void dropBatch(List> memories) { } } - - public static T enhanceCheck(T afterProxy, Class nativeInterface) { - return (T) Proxy.newProxyInstance(nativeInterface.getClassLoader(), new Class[]{nativeInterface}, (Object proxy, Method method, Object[] args) -> { - KernelVersionLimit annotation = Optional.ofNullable(method.getAnnotation(KernelVersionLimit.class)).orElse(nativeInterface.getAnnotation(KernelVersionLimit.class)); - if (annotation != null) { - if (!osLinux) { - logger.error("This method is only supported on Linux"); - throw new ErrorKernelVersionException(); - } - if (!allowCurrentLinuxVersion(annotation.major(), annotation.minor())) { - logger.error("This method is only supported on Linux kernel version {}.{}, current version is {}.{}", - annotation.major(), annotation.minor(), currentLinuxMajor, currentLinuxMinor); - throw new ErrorKernelVersionException(annotation.major(), annotation.minor()); - } - } - return method.invoke(afterProxy, args); - }); - } - public static boolean inSameEventLoop(IoUringEventLoop eventLoop, Object o) { if (isSkipSameEventLoopCheck) { return true; diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/OSIoUringProbe.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/OSIoUringProbe.java new file mode 100644 index 0000000..2536d1a --- /dev/null +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/helper/OSIoUringProbe.java @@ -0,0 +1,46 @@ +package top.dreamlike.panama.uring.nativelib.helper; + +import top.dreamlike.panama.generator.proxy.NativeArray; +import top.dreamlike.panama.generator.proxy.StructProxyGenerator; +import top.dreamlike.panama.uring.nativelib.Instance; + +import java.lang.foreign.MemorySegment; + +public class OSIoUringProbe { + + private final int lastOp; + + private final IoUringProbeOp[] ops; + + public OSIoUringProbe() { + var probe = Instance.LIB_URING.io_uring_get_probe(); + if (probe == null) { + throw new RuntimeException("Failed to get probe"); + } + lastOp = probe.getLastOp(); + byte len = probe.getOpsLen(); + ops = new IoUringProbeOp[len]; + MemorySegment opsBase = StructProxyGenerator.findMemorySegment(probe) + .asSlice(top.dreamlike.panama.uring.nativelib.struct.liburing.IoUringProbe.OPS_OFFSET) + .reinterpret(len * top.dreamlike.panama.uring.nativelib.struct.liburing.IoUringProbeOp.LAYOUT.byteSize()); + + NativeArray ops = Instance.STRUCT_PROXY_GENERATOR.enhanceArray(opsBase); + for (byte i = 0; i < len; i++) { + top.dreamlike.panama.uring.nativelib.struct.liburing.IoUringProbeOp op = ops.get(i); + this.ops[i] = new IoUringProbeOp(op.getOp(), op.getFlags()); + } + + Instance.LIB_URING.io_uring_free_probe(probe); + } + + public int getLastOp() { + return lastOp; + } + + public IoUringProbeOp[] getOps() { + return ops; + } + + public record IoUringProbeOp(byte op, short flags) { + } +} diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/libs/LibUring.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/libs/LibUring.java index c606f92..5b3ebe3 100644 --- a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/libs/LibUring.java +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/libs/LibUring.java @@ -27,8 +27,13 @@ @CLib("liburing-ffi.so") @KernelVersionLimit(major = 5, minor = 10) public interface LibUring { - //跟队列本身相关的操作 + @NativeFunction(returnIsPointer = true) + IoUringProbe io_uring_get_probe(); + + void io_uring_free_probe(@Pointer IoUringProbe probe); + + //跟队列本身相关的操作= int io_uring_queue_init(int entries, @Pointer IoUring ring, int flags); int io_uring_queue_init_params(int entries, @Pointer IoUring ring, @Pointer IoUringParams p); @@ -204,6 +209,13 @@ default IoUringSqe io_uring_get_sqe(@Pointer IoUring ring) { return Instance.STRUCT_PROXY_GENERATOR.enhance(currentSqe); } + @NativeFunction(fast = true) + default void io_uring_back_sqe(@Pointer IoUring ring) { + MemorySegment realMemory = StructProxyGenerator.findMemorySegment(ring); + int tail = (int) IoUringConstant.AccessShortcuts.IO_URING_SQ_SQE_TAIL_VARHANDLE.get(realMemory, 0L); + IoUringConstant.AccessShortcuts.IO_URING_SQ_SQE_TAIL_VARHANDLE.set(realMemory, 0L, tail - 1); + } + default void io_uring_prep_rw(int opcode, @Pointer IoUringSqe sqe, int fd, MemorySegment addr, int len, long offset) { if (!StructProxyGenerator.isNativeStruct(sqe)) { throw new StructException("sqe is not struct,pleace call StructProxyGenerator::enhance before calling native function"); diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/struct/liburing/IoUringProbe.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/struct/liburing/IoUringProbe.java new file mode 100644 index 0000000..0fd0a3d --- /dev/null +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/struct/liburing/IoUringProbe.java @@ -0,0 +1,66 @@ +package top.dreamlike.panama.uring.nativelib.struct.liburing; + +import top.dreamlike.panama.generator.annotation.NativeArrayMark; +import top.dreamlike.panama.uring.nativelib.Instance; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; + +public class IoUringProbe { + + public static final MemoryLayout LAYOUT = Instance.STRUCT_PROXY_GENERATOR.extract(IoUringProbe.class); + + public static final long OPS_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("ops")); + + private byte lastOp; + + private byte opsLen; + + private short resv; + + @NativeArrayMark(size = int.class, length = 3) + private MemorySegment resv2; + + @NativeArrayMark(size = IoUringProbeOp.class, length = 0) + private MemorySegment ops; + + public byte getLastOp() { + return lastOp; + } + + public void setLastOp(byte lastOp) { + this.lastOp = lastOp; + } + + public byte getOpsLen() { + return opsLen; + } + + public void setOpsLen(byte opsLen) { + this.opsLen = opsLen; + } + + public short getResv() { + return resv; + } + + public void setResv(short resv) { + this.resv = resv; + } + + public MemorySegment getResv2() { + return resv2; + } + + public void setResv2(MemorySegment resv2) { + this.resv2 = resv2; + } + + public MemorySegment getOps() { + return ops; + } + + public void setOps(MemorySegment ops) { + this.ops = ops; + } +} diff --git a/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/struct/liburing/IoUringProbeOp.java b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/struct/liburing/IoUringProbeOp.java new file mode 100644 index 0000000..1e0cc1a --- /dev/null +++ b/panama-uring/src/main/java/top/dreamlike/panama/uring/nativelib/struct/liburing/IoUringProbeOp.java @@ -0,0 +1,55 @@ +package top.dreamlike.panama.uring.nativelib.struct.liburing; + +import top.dreamlike.panama.uring.nativelib.Instance; + +import java.lang.foreign.MemoryLayout; + +public class IoUringProbeOp { + public static final MemoryLayout LAYOUT = Instance.STRUCT_PROXY_GENERATOR.extract(IoUringProbeOp.class); + + private byte op; + + private byte resv; + + private short flags; + + private int recv2; + + public byte getOp() { + return op; + } + + public void setOp(byte op) { + this.op = op; + } + + public byte getResv() { + return resv; + } + + public void setResv(byte resv) { + this.resv = resv; + } + + public short getFlags() { + return flags; + } + + public void setFlags(short flags) { + this.flags = flags; + } + + public int getRecv2() { + return recv2; + } + + public void setRecv2(int recv2) { + this.recv2 = recv2; + } +} +//struct io_uring_probe_op { +// __u8 op; +// __u8 resv; +// __u16 flags; /* IO_URING_OP_* flags */ +// __u32 resv2; +//}; \ No newline at end of file diff --git a/panama-uring/src/test/java/AdvanceLiburingTest.java b/panama-uring/src/test/java/AdvanceLiburingTest.java index 69c0b37..5611c45 100644 --- a/panama-uring/src/test/java/AdvanceLiburingTest.java +++ b/panama-uring/src/test/java/AdvanceLiburingTest.java @@ -14,6 +14,7 @@ import top.dreamlike.panama.uring.nativelib.Instance; import top.dreamlike.panama.uring.nativelib.exception.SyscallException; import top.dreamlike.panama.uring.nativelib.helper.NativeHelper; +import top.dreamlike.panama.uring.nativelib.helper.OSIoUringProbe; import top.dreamlike.panama.uring.nativelib.libs.Libc; import top.dreamlike.panama.uring.nativelib.struct.liburing.IoUringBufRingSetupResult; import top.dreamlike.panama.uring.nativelib.struct.liburing.IoUringBufferRingElement; @@ -89,7 +90,7 @@ public void testSelectedRead() { Assert.assertEquals(Libc.Error_H.ENOBUFS, syscallException.getErrorno()); bufferRing.releaseRing(); } catch (Exception e) { - e.printStackTrace(); + throw new RuntimeException(e); } } @@ -304,4 +305,12 @@ public void testLinked() throws Exception { Assert.assertEquals(Integer.valueOf(0), i); } } + + @Test + public void testProbe() { + OSIoUringProbe probe = new OSIoUringProbe(); + int lastOp = probe.getLastOp(); + Assert.assertTrue(lastOp > 0); + Assert.assertEquals(lastOp + 1, probe.getOps().length); + } } diff --git a/panama-uring/src/test/java/KernelVersionCheckTest.java b/panama-uring/src/test/java/KernelVersionCheckTest.java index 81ec81d..fee7416 100644 --- a/panama-uring/src/test/java/KernelVersionCheckTest.java +++ b/panama-uring/src/test/java/KernelVersionCheckTest.java @@ -1,38 +1,32 @@ import org.junit.Assert; import org.junit.Test; -import top.dreamlike.panama.uring.nativelib.Instance; -import top.dreamlike.panama.uring.nativelib.exception.ErrorKernelVersionException; -import top.dreamlike.panama.uring.nativelib.helper.KernelVersionLimit; -import top.dreamlike.panama.uring.nativelib.helper.NativeHelper; +import top.dreamlike.panama.uring.eventloop.IoUringEventLoop; + +import java.util.concurrent.ArrayBlockingQueue; public class KernelVersionCheckTest { @Test - public void test() { - SomeFunctionalInterface generated = Instance.NATIVE_CALL_GENERATOR.generate(SomeFunctionalInterface.class); - SomeFunctionalInterface checked = NativeHelper.enhanceCheck(generated, SomeFunctionalInterface.class); - Assert.assertThrows(ErrorKernelVersionException.class, checked::mustThrow); - Assert.assertTrue(checked.getpagesize() > 0); - - Assert.assertEquals(3, checked.defaultFunction(1, 2)); - Assert.assertThrows(ErrorKernelVersionException.class, () -> checked.defaultFunctionMustThrow(1, 2)); + public void test() throws InterruptedException { + IoUringEventLoop eventLoop = new IoUringEventLoop(params -> { + params.setSq_entries(4); + params.setFlags(0); + }); + ArrayBlockingQueue queue = new ArrayBlockingQueue<>(1); + eventLoop.setExceptionHandler(queue::add); + eventLoop.start(); + try(eventLoop) { + eventLoop.asyncOperation(sqe -> { + sqe.setOpcode(Byte.MAX_VALUE); + }); + } catch (Throwable r) { + throw new RuntimeException(r); + } + eventLoop.join(); + Assert.assertEquals(1, queue.size()); + Throwable throwable = queue.take(); + Assert.assertTrue(throwable instanceof UnsupportedOperationException); } - public interface SomeFunctionalInterface { - @KernelVersionLimit(major = 10, minor = 10) - void mustThrow(); - - @KernelVersionLimit(major = 1, minor = 1) - int getpagesize(); - @KernelVersionLimit(major = 1, minor = 1) - default int defaultFunction(int a, int b) { - return a + b; - } - - @KernelVersionLimit(major = 10, minor = 1) - default int defaultFunctionMustThrow(int a, int b) { - return a + b; - } - } } diff --git a/pom.xml b/pom.xml index 4c7571c..abea78b 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ 22 UTF-8 - 4.0.1 + 4.0.2 4.3.7 3.0.0 3.0.0