├── .gitignore ├── README.md ├── jni ├── Makefile ├── ioURing.c └── ioURing.h ├── pom.xml └── src ├── main └── java │ └── org │ └── chinaxing │ ├── AsyncIO.java │ ├── IoURing.java │ ├── IoURingNative.java │ └── exception │ ├── IoURingException.java │ ├── PrepareRWException.java │ ├── SubmitException.java │ └── WaitCQEException.java └── test └── java └── org └── chinaxing ├── AsyncIOTest.java └── IoURingTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | *.iml 4 | *.so 5 | *.o 6 | TAGS 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | io_uring-java 2 | ============== 3 | 4 | java binding for : **_liburing_** (https://github.com/axboe/liburing) 5 | 6 | for more information see : **_io_uring_** 7 | 8 | Usage 9 | ----- 10 | 11 | 1. **AsyncIO interface**\ 12 | this interface wrapper IoURing and provide an interface of Asynchronouse io.\ 13 | use CompletableFuture as a async programming Object. 14 | 2. **IoURing interface**\ 15 | this interface just provide very basical io_uring interface, it straightforwardly 16 | expose each primitive to java side. 17 | 18 | TODO 19 | ---- 20 | 1. expose more io_uring function. 21 | 1. timeout 22 | 2. network 23 | 3. file open/fallocate/splice etc. 24 | 4. sqe link : SQE_OP_LINK | SQE_OP_HARD_LINK 25 | 5. iopoll : IORING_SET_IOPOLL 26 | 6. sqpoll : SQE_OP_FIXED_FILE - when enable IORING_SET_SQPOLL auto set this flag to sqe 27 | 7. init queue with params 28 | 8. expose io_uring_params to java side 29 | 2. refine exception handle. 30 | 3. refine AsyncIO interface. 31 | 4. document. 32 | 5. more comphensive test. 33 | 34 | License 35 | ------- 36 | 37 | All software contained within this repo is MIT. 38 | -------------------------------------------------------------------------------- /jni/Makefile: -------------------------------------------------------------------------------- 1 | JAVA_HOME ?= /opt/taobao/java 2 | CC ?= clang 3 | FLAGS = -D_REENTRANT 4 | INCLUDE = -I . -I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/linux/ 5 | 6 | all: liburingjni.so 7 | 8 | install: 9 | cp liburingjni.so /usr/lib/ 10 | 11 | ioURing.o: ioURing.c 12 | $(CC) $(FLAGS) $(INCLUDE) -c -fpic $< -o $@ 13 | 14 | liburingjni.so: ioURing.o 15 | $(CC) -shared -fpic -luring -o $@ $< 16 | 17 | .PHONY : clean 18 | clean: 19 | rm -f ioUring.o liburingjni.so 20 | -------------------------------------------------------------------------------- /jni/ioURing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "liburing.h" 4 | #include "ioURing.h" 5 | 6 | #define GET_RING(env, self) (struct io_uring*)(*env)->GetLongField(env, self, ring_fid) 7 | #define GET_SQE(env, self, flags) \ 8 | struct io_uring_sqe * sqe = io_uring_get_sqe(GET_RING(env, self)); \ 9 | if(!sqe) return -1; \ 10 | if (!flags) io_uring_sqe_set_flags(sqe, (unsigned)flags) 11 | 12 | #define MSEC_TO_TS(ts, msec) \ 13 | do { \ 14 | ts.tv_sec = msec / 1000; \ 15 | ts.tv_nsec = (msec % 1000) * 1000000; \ 16 | } while(0) 17 | 18 | #define SUPPORT_OP_CODE(op_code) (io_uring_probe_p && io_uring_opcode_supported((struct io_uring_probe *)io_uring_probe_p, op_code)) 19 | 20 | #define BUILD_IO_VEC_1(buf, len) \ 21 | struct iovec* vecs = malloc(sizeof(struct iovec)); \ 22 | if (!vecs) return -errno; \ 23 | vecs->iov_base = (void*)buf; \ 24 | vecs->iov_len = (unsigned)len 25 | 26 | 27 | static struct iovec * build_iovecs(JNIEnv *env, jlongArray buffers, jlongArray lens, jsize cnt) { 28 | struct iovec* vecs = malloc(cnt * sizeof(struct iovec)); 29 | if (vecs) { 30 | jlong* buffer_ptr= (*env)->GetLongArrayElements(env, buffers, NULL); 31 | jlong* len_ptr = (*env)->GetLongArrayElements(env, lens, NULL); 32 | for(int i =0 ; i < cnt; i++) { 33 | vecs[i].iov_base = (void*)buffer_ptr[i]; 34 | vecs[i].iov_len = (unsigned)len_ptr[i]; 35 | } 36 | (*env)->ReleaseLongArrayElements(env, buffers, buffer_ptr, JNI_ABORT); 37 | (*env)->ReleaseLongArrayElements(env, lens, len_ptr, JNI_ABORT); 38 | } 39 | return vecs; 40 | } 41 | 42 | jfieldID ring_fid; 43 | jlong io_uring_probe_p; 44 | 45 | JNIEXPORT void JNICALL Java_org_chinaxing_IoURingNative_initIDs(JNIEnv * env, jclass clz) 46 | { 47 | ring_fid = (*env)->GetFieldID(env, clz, "_ring", "J"); 48 | io_uring_probe_p = (jlong)io_uring_get_probe(); 49 | } 50 | 51 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_init0(JNIEnv * env, jobject self, jint entries, jlong flags) 52 | { 53 | struct io_uring * ring = malloc(sizeof(struct io_uring)); 54 | (*env)->SetLongField(env, self, ring_fid, (jlong)ring); 55 | return (jint)io_uring_queue_init(entries, ring, flags); 56 | } 57 | 58 | JNIEXPORT void JNICALL Java_org_chinaxing_IoURingNative_exit0(JNIEnv * env, jobject self) 59 | { 60 | struct io_uring * ring = GET_RING(env, self); 61 | io_uring_queue_exit(ring); 62 | free(ring); 63 | } 64 | 65 | 66 | 67 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_registerBuffers(JNIEnv * env, jobject self, jlongArray buffers, jlongArray lens) 68 | { 69 | jsize cnt = (*env)->GetArrayLength(env, buffers); 70 | struct iovec * vecs = build_iovecs(env, buffers, lens, cnt); 71 | if(!vecs) { 72 | return -1; 73 | } 74 | return io_uring_register_buffers(GET_RING(env, self), vecs, (unsigned)cnt); 75 | } 76 | 77 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_unregisterBuffers(JNIEnv * env, jobject self) 78 | { 79 | return io_uring_unregister_buffers(GET_RING(env, self)); 80 | } 81 | 82 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_registerFiles(JNIEnv * env, jobject self, jintArray fds) 83 | { 84 | jsize cnt = (*env)->GetArrayLength(env, fds); 85 | jint fds_[cnt]; 86 | (*env)->GetIntArrayRegion(env, fds, 0, cnt, fds_); 87 | return io_uring_register_files(GET_RING(env, self), (int*)fds_, cnt); 88 | } 89 | 90 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_unregisterFiles(JNIEnv * env, jobject self) 91 | { 92 | return io_uring_unregister_files(GET_RING(env, self)); 93 | } 94 | 95 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareRead(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlong bufptr, jint len, jlong offset) 96 | { 97 | GET_SQE(env, self, flags); 98 | if (SUPPORT_OP_CODE(IORING_OP_READ)) { 99 | io_uring_prep_read(sqe, (int)fd, (void*) bufptr, (unsigned)len, (off_t)offset); 100 | } else { 101 | // fallback to readv 102 | BUILD_IO_VEC_1(bufptr, len); 103 | io_uring_prep_readv(sqe, (int)fd, vecs, 1, (off_t)offset); 104 | } 105 | io_uring_sqe_set_data(sqe, (void*)reqId); 106 | return 0; 107 | } 108 | 109 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareReads(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlongArray bufptrs, jintArray lens, jlong offset) 110 | { 111 | GET_SQE(env, self, flags); 112 | jsize nr_vecs = (*env)->GetArrayLength(env, bufptrs); 113 | struct iovec * vecs = build_iovecs(env, bufptrs, lens, nr_vecs); 114 | io_uring_prep_readv(sqe, (int)fd, vecs, (unsigned)nr_vecs, (off_t)offset); 115 | io_uring_sqe_set_data(sqe, (void*)reqId); 116 | return 0; 117 | } 118 | 119 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareReadFixed(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlong bufptr, jint len, jlong offset, jint bufIndex) 120 | { 121 | GET_SQE(env, self, flags); 122 | io_uring_prep_write_fixed(sqe, (int)fd, (void*)bufptr, (unsigned)len, (off_t)offset, (int) bufIndex); 123 | io_uring_sqe_set_data(sqe, (void*)reqId); 124 | return 0; 125 | } 126 | 127 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareWrite(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlong bufptr, jint len, jlong offset) 128 | { 129 | GET_SQE(env, self, flags); 130 | if (SUPPORT_OP_CODE(IORING_OP_WRITE)) { 131 | io_uring_prep_write(sqe, (int)fd, (void*) bufptr, (unsigned)len, (off_t)offset); 132 | } else { 133 | // fallback to readv 134 | BUILD_IO_VEC_1(bufptr, len); 135 | io_uring_prep_writev(sqe, (int)fd, vecs, 1, (off_t)offset); 136 | } 137 | io_uring_sqe_set_data(sqe, (void*)reqId); 138 | return 0; 139 | } 140 | 141 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareWrites(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlongArray bufptr, jintArray len, jlong offset) 142 | { 143 | GET_SQE(env, self, flags); 144 | jsize nr_vecs = (*env)->GetArrayLength(env, bufptr); 145 | struct iovec * vecs = build_iovecs(env, bufptr, len, nr_vecs); 146 | io_uring_prep_writev(sqe, (int)fd, vecs, (unsigned)nr_vecs, (off_t)offset); 147 | io_uring_sqe_set_data(sqe, (void*)reqId); 148 | return 0; 149 | } 150 | 151 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareWriteFixed(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlong bufptr, jint len, jlong offset, jint bufIndex) 152 | { 153 | GET_SQE(env, self, flags); 154 | io_uring_prep_write_fixed(sqe, (int)fd, (void*)bufptr, (unsigned)len, (off_t)offset, (int) bufIndex); 155 | io_uring_sqe_set_data(sqe, (void*)reqId); 156 | return 0; 157 | } 158 | 159 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareFsync(JNIEnv * env, jobject self, jlong reqId, jlong flags, jint fd, jlong fsyncFlags) 160 | { 161 | GET_SQE(env, self, flags); 162 | io_uring_prep_fsync((struct io_uring_sqe *)sqe, (int)fd, (unsigned)fsyncFlags); 163 | io_uring_sqe_set_data(sqe, (void*)reqId); 164 | return 0; 165 | } 166 | 167 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_submit(JNIEnv * env, jobject self) 168 | { 169 | return io_uring_submit(GET_RING(env, self)); 170 | } 171 | 172 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_submitAndWait(JNIEnv * env, jobject self, jint n) 173 | { 174 | return io_uring_submit_and_wait(GET_RING(env, self), n); 175 | } 176 | 177 | 178 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_waitCQEntryTimeout(JNIEnv * env, jobject self, jlongArray reqIds, jlongArray retCodes, long ms) 179 | { 180 | struct __kernel_timespec ts; 181 | MSEC_TO_TS(ts, ms); 182 | struct io_uring_cqe * cqe_ptr; 183 | int ret = io_uring_wait_cqe_timeout(GET_RING(env, self), &cqe_ptr, &ts); 184 | if (!ret) { 185 | long reqIds_[1] = { (long)cqe_ptr->user_data }; 186 | long res_[1] = { (long)cqe_ptr->res }; 187 | (*env)->SetLongArrayRegion(env, reqIds, 0, 1, reqIds_); 188 | (*env)->SetLongArrayRegion(env, retCodes, 0, 1, res_); 189 | } 190 | return ret; 191 | } 192 | 193 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_waitCQEntries(JNIEnv * env, jobject self, jlongArray reqIds, jlongArray retCodes, jint n) 194 | { 195 | struct io_uring_cqe * cqe_ptr; 196 | int ret = io_uring_wait_cqe_nr(GET_RING(env, self), &cqe_ptr, n); 197 | if (!ret) { 198 | long reqIds_[1] = { (long)cqe_ptr->user_data }; 199 | long res_[1] = { (long)cqe_ptr->res }; 200 | (*env)->SetLongArrayRegion(env, reqIds, 0, 1, reqIds_); 201 | (*env)->SetLongArrayRegion(env, retCodes, 0, 1, res_); 202 | } 203 | return ret; 204 | } 205 | 206 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_peekCQEntries(JNIEnv * env, jobject self, jlongArray reqIds, jlongArray retCodes, jint n) 207 | { 208 | struct io_uring_cqe* cqe_ptr[n]; 209 | int cnt = io_uring_peek_batch_cqe(GET_RING(env, self), cqe_ptr, n); 210 | if (cnt) { 211 | long reqIds_[cnt]; 212 | long res_[cnt]; 213 | for(int i=0; iuser_data; 215 | res_[i] = cqe_ptr[i]->res; 216 | } 217 | (*env)->SetLongArrayRegion(env, reqIds, 0, cnt, reqIds_); 218 | (*env)->SetLongArrayRegion(env, retCodes, 0, cnt, res_); 219 | } 220 | return cnt; 221 | } 222 | 223 | JNIEXPORT void JNICALL Java_org_chinaxing_IoURingNative_advanceCQ(JNIEnv * env, jobject self, jint n) 224 | { 225 | struct io_uring * ring = GET_RING(env, self); 226 | io_uring_cq_advance(ring, n); 227 | } 228 | -------------------------------------------------------------------------------- /jni/ioURing.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_chinaxing_IoURingNative */ 4 | 5 | #ifndef _Included_org_chinaxing_IoURingNative 6 | #define _Included_org_chinaxing_IoURingNative 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: org_chinaxing_IoURingNative 12 | * Method: initIDs 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_org_chinaxing_IoURingNative_initIDs 16 | (JNIEnv *, jclass); 17 | 18 | /* 19 | * Class: org_chinaxing_IoURingNative 20 | * Method: init0 21 | * Signature: (IJ)I 22 | */ 23 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_init0 24 | (JNIEnv *, jobject, jint, jlong); 25 | 26 | /* 27 | * Class: org_chinaxing_IoURingNative 28 | * Method: exit0 29 | * Signature: ()V 30 | */ 31 | JNIEXPORT void JNICALL Java_org_chinaxing_IoURingNative_exit0 32 | (JNIEnv *, jobject); 33 | 34 | /* 35 | * Class: org_chinaxing_IoURingNative 36 | * Method: registerBuffers 37 | * Signature: ([J[J)I 38 | */ 39 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_registerBuffers 40 | (JNIEnv *, jobject, jlongArray, jlongArray); 41 | 42 | /* 43 | * Class: org_chinaxing_IoURingNative 44 | * Method: unregisterBuffers 45 | * Signature: ()I 46 | */ 47 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_unregisterBuffers 48 | (JNIEnv *, jobject); 49 | 50 | /* 51 | * Class: org_chinaxing_IoURingNative 52 | * Method: registerFiles 53 | * Signature: ([I)I 54 | */ 55 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_registerFiles 56 | (JNIEnv *, jobject, jintArray); 57 | 58 | /* 59 | * Class: org_chinaxing_IoURingNative 60 | * Method: unregisterFiles 61 | * Signature: ()I 62 | */ 63 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_unregisterFiles 64 | (JNIEnv *, jobject); 65 | 66 | /* 67 | * Class: org_chinaxing_IoURingNative 68 | * Method: prepareRead 69 | * Signature: (JJIJIJ)I 70 | */ 71 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareRead 72 | (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong); 73 | 74 | /* 75 | * Class: org_chinaxing_IoURingNative 76 | * Method: prepareReads 77 | * Signature: (JJI[J[IJ)I 78 | */ 79 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareReads 80 | (JNIEnv *, jobject, jlong, jlong, jint, jlongArray, jintArray, jlong); 81 | 82 | /* 83 | * Class: org_chinaxing_IoURingNative 84 | * Method: prepareReadFixed 85 | * Signature: (JJIJIJI)I 86 | */ 87 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareReadFixed 88 | (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong, jint); 89 | 90 | /* 91 | * Class: org_chinaxing_IoURingNative 92 | * Method: prepareWrite 93 | * Signature: (JJIJIJ)I 94 | */ 95 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareWrite 96 | (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong); 97 | 98 | /* 99 | * Class: org_chinaxing_IoURingNative 100 | * Method: prepareWrites 101 | * Signature: (JJI[J[IJ)I 102 | */ 103 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareWrites 104 | (JNIEnv *, jobject, jlong, jlong, jint, jlongArray, jintArray, jlong); 105 | 106 | /* 107 | * Class: org_chinaxing_IoURingNative 108 | * Method: prepareWriteFixed 109 | * Signature: (JJIJIJI)I 110 | */ 111 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareWriteFixed 112 | (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jlong, jint); 113 | 114 | /* 115 | * Class: org_chinaxing_IoURingNative 116 | * Method: prepareFsync 117 | * Signature: (JJIJ)I 118 | */ 119 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_prepareFsync 120 | (JNIEnv *, jobject, jlong, jlong, jint, jlong); 121 | 122 | /* 123 | * Class: org_chinaxing_IoURingNative 124 | * Method: submit 125 | * Signature: ()I 126 | */ 127 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_submit 128 | (JNIEnv *, jobject); 129 | 130 | /* 131 | * Class: org_chinaxing_IoURingNative 132 | * Method: submitAndWait 133 | * Signature: (I)I 134 | */ 135 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_submitAndWait 136 | (JNIEnv *, jobject, jint); 137 | 138 | /* 139 | * Class: org_chinaxing_IoURingNative 140 | * Method: waitCQEntryTimeout 141 | * Signature: ([J[JJ)I 142 | */ 143 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_waitCQEntryTimeout 144 | (JNIEnv *, jobject, jlongArray, jlongArray, jlong); 145 | 146 | /* 147 | * Class: org_chinaxing_IoURingNative 148 | * Method: waitCQEntries 149 | * Signature: ([J[JI)I 150 | */ 151 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_waitCQEntries 152 | (JNIEnv *, jobject, jlongArray, jlongArray, jint); 153 | 154 | /* 155 | * Class: org_chinaxing_IoURingNative 156 | * Method: peekCQEntries 157 | * Signature: ([J[JI)I 158 | */ 159 | JNIEXPORT jint JNICALL Java_org_chinaxing_IoURingNative_peekCQEntries 160 | (JNIEnv *, jobject, jlongArray, jlongArray, jint); 161 | 162 | /* 163 | * Class: org_chinaxing_IoURingNative 164 | * Method: advanceCQ 165 | * Signature: (I)V 166 | */ 167 | JNIEXPORT void JNICALL Java_org_chinaxing_IoURingNative_advanceCQ 168 | (JNIEnv *, jobject, jint); 169 | 170 | #ifdef __cplusplus 171 | } 172 | #endif 173 | #endif 174 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.github.chinaxing 8 | java-io-uring 9 | 1.0 10 | 11 | java-io-uring 12 | https://github.com/ChinaXing/io_uring-java 13 | 14 | 15 | UTF-8 16 | 1.7 17 | 1.7 18 | 19 | 20 | 21 | 22 | junit 23 | junit 24 | 4.11 25 | test 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | maven-clean-plugin 35 | 3.1.0 36 | 37 | 38 | 39 | maven-resources-plugin 40 | 3.0.2 41 | 42 | 43 | maven-compiler-plugin 44 | 3.8.0 45 | 46 | 47 | maven-surefire-plugin 48 | 2.22.1 49 | 50 | 51 | maven-jar-plugin 52 | 3.0.2 53 | 54 | 55 | maven-install-plugin 56 | 2.5.2 57 | 58 | 59 | maven-deploy-plugin 60 | 2.8.2 61 | 62 | 63 | 64 | maven-site-plugin 65 | 3.7.1 66 | 67 | 68 | maven-project-info-reports-plugin 69 | 3.0.0 70 | 71 | 72 | 73 | 74 | 75 | org.apache.maven.plugins 76 | maven-compiler-plugin 77 | 78 | 8 79 | 8 80 | 81 | 82 | 83 | 84 | 85 | 86 | github 87 | GitHub chinaxing Apache Maven Packages 88 | https://maven.pkg.github.com/ChinaXing 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/AsyncIO.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing; 2 | 3 | import org.chinaxing.IoURing.IOResult; 4 | import org.chinaxing.exception.IoURingException; 5 | 6 | import java.io.FileDescriptor; 7 | import java.util.Map; 8 | import java.util.concurrent.CompletableFuture; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * capabilities: 13 | * 14 | * 1) submit io request and get a Future 15 | * each Future associate a request-Id internally 16 | * 2) when sync with Future , trigger fetch io result 17 | * 3) future can be completed by internal Io-Completion-Reap EventLoop 18 | * 19 | * parameters like buffer requirement same as {@link IoURing} 20 | */ 21 | public class AsyncIO { 22 | // use Integer as Future result data Type, because most of io-result return int 23 | private final Map> ioRequestFutures = new ConcurrentHashMap<>(); 24 | private final IoURing ring; 25 | private final String name; 26 | 27 | public AsyncIO(String name, int queueDepth, int flags) { 28 | this.name = name; 29 | ring = new IoURing(queueDepth, flags); 30 | new IoCompletionLoopThread().start(); 31 | } 32 | 33 | public CompletableFuture prepareRead(FileDescriptor fd, long offset, byte[] buf, int bufPos, int len) { 34 | long reqId = ring.prepareRead(fd, offset, buf, bufPos, len); 35 | CompletableFuture future = new CompletableFuture<>(); 36 | ioRequestFutures.put(reqId, future); 37 | return future; 38 | } 39 | 40 | public CompletableFuture prepareReads(FileDescriptor fd, long offset, byte[][] buf, int[] bufPos, int[] len) { 41 | long reqId = ring.prepareReads(fd, offset, buf, bufPos, len); 42 | CompletableFuture future = new CompletableFuture<>(); 43 | ioRequestFutures.put(reqId, future); 44 | return future; 45 | } 46 | 47 | public CompletableFuture prepareWrite(FileDescriptor fd, long offset, byte[] buf, int bufPos, int len) { 48 | long reqId = ring.prepareWrite(fd, offset, buf, bufPos, len); 49 | CompletableFuture future = new CompletableFuture<>(); 50 | ioRequestFutures.put(reqId, future); 51 | return future; 52 | } 53 | 54 | public CompletableFuture prepareWrites(FileDescriptor fd, long offset, byte[][] buf, int[] bufPos, int[] len) { 55 | long reqId = ring.prepareWrites(fd, offset, buf, bufPos, len); 56 | CompletableFuture future = new CompletableFuture<>(); 57 | ioRequestFutures.put(reqId, future); 58 | return future; 59 | } 60 | 61 | public void seen(int n) { 62 | ring.seenCQEntry(n); 63 | 64 | } 65 | public int submit() { 66 | return ring.submit(); 67 | } 68 | 69 | public void shutdown() { 70 | ring.shutdown(); 71 | } 72 | 73 | /** 74 | * This eventLoop Poll completion queue 75 | * and complete the waiting futures for each request 76 | */ 77 | class IoCompletionLoopThread extends Thread { 78 | public IoCompletionLoopThread() { 79 | super(); 80 | setName("Io-URing-Completion-Loop-`" + name + "`"); 81 | } 82 | 83 | @Override 84 | public void run() { 85 | pollCQ(); 86 | } 87 | 88 | private void pollCQ() { 89 | while (true) { 90 | try { 91 | IOResult result = ring.waitCQEntry(); 92 | CompletableFuture future = ioRequestFutures.get(result.reqId); 93 | if (future != null) { 94 | future.complete(result.res); 95 | } 96 | ring.seenCQEntry(1); 97 | }catch (IoURingException e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/IoURing.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing; 2 | 3 | import org.chinaxing.exception.PrepareRWException; 4 | import org.chinaxing.exception.SubmitException; 5 | import org.chinaxing.exception.WaitCQEException; 6 | import sun.misc.Unsafe; 7 | import sun.nio.ch.DirectBuffer; 8 | 9 | import java.io.FileDescriptor; 10 | import java.lang.reflect.Field; 11 | import java.nio.ByteBuffer; 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.concurrent.atomic.AtomicLong; 15 | 16 | /** 17 | * IoURing high level interface 18 | * 19 | * capabilities: 20 | * 21 | * 0) setup and customize IOURing instance 22 | * a) io-poll for (nvme + xfs) 23 | * b) sq-poll (reduce syscall) 24 | * 25 | * 1) submit read/write 26 | * a) against heap byte[] 27 | * b) against DirectByteBuffer 28 | * 2) wait request completion 29 | * 3) batch submit read/write requests 30 | * a) multi-buffer per file 31 | * b) multi-io 32 | * 4) wait batch completion 33 | * 34 | * 5) register DirectByteBuffer to use Fixed read/write ( for performance ) 35 | * 6) register Files to use Fixed file ( for performance ) 36 | * 37 | * 7) buffer conversion: 38 | * 1) read/write base on Heap bytes array 39 | * 2) read/write base on DirectByteBuffer 40 | * 41 | *

Read/Write flow

42 | * 1) get Submission Queue entry will return the request-Id 43 | * internally, we associate the SQE with a request-Id (cookie) 44 | * 2) prepare read/write/readv/writev against request-Id 45 | * 3) goto 1) until all read/write prepared 46 | * 4) submit prepared ios in the Submission Queue 47 | * 5) query or wait completion of submitted requests in the Queue. 48 | */ 49 | public class IoURing { 50 | private final IoURingNative _native; 51 | 52 | 53 | public static class Flags { 54 | //#define IORING_SETUP_IOPOLL (1U << 0) /* io_context is polled */ 55 | //#define IORING_SETUP_SQPOLL (1U << 1) /* SQ poll thread */ 56 | //#define IORING_SETUP_SQ_AFF (1U << 2) /* sq_thread_cpu is valid */ 57 | //#define IORING_SETUP_CQSIZE (1U << 3) /* app defines CQ size */ 58 | //#define IORING_SETUP_CLAMP (1U << 4) /* clamp SQ/CQ ring sizes */ 59 | //#define IORING_SETUP_ATTACH_WQ (1U << 5) /* attach to existing wq */ 60 | public static final int IOPOLL = (1 << 0); 61 | public static final int SQPOLL = (1 << 1); 62 | public static final int SQ_AFF = (1 << 2); 63 | public static final int CQSIZE = (1 << 3); 64 | public static final int CLAMP = (1 << 4); 65 | public static final int ATTACH_WQ = (1 << 5); 66 | } 67 | 68 | public static class IOResult { 69 | public final long reqId; 70 | public final long res; 71 | 72 | public IOResult(long reqId, long res) { 73 | this.reqId = reqId; 74 | this.res = res; 75 | } 76 | } 77 | 78 | public static class ReadIOCtx { 79 | public final long reqId; 80 | // user supplied read dest byte[] 81 | public final byte[][] outputBytes; 82 | public final int[] outputOffset; 83 | // underline directBuffer io_uring used 84 | public final ByteBuffer[] directBuffer; 85 | 86 | public ReadIOCtx(long reqId, byte[][] buf, int[] offset, ByteBuffer[] dBuf) { 87 | this.reqId = reqId; 88 | this.outputBytes = buf; 89 | this.outputOffset = offset; 90 | this.directBuffer = dBuf; 91 | } 92 | 93 | public void prepareResult(long res) { 94 | long remain = res; 95 | for(int i=0; i< directBuffer.length; i++) { 96 | long dl = directBuffer[i].capacity(); 97 | long cl = Math.min(remain, dl); 98 | directBuffer[i].get(outputBytes[i], outputOffset[i], (int) cl); 99 | if((remain -= cl) <= 0) { 100 | break; 101 | } 102 | } 103 | } 104 | } 105 | 106 | private final AtomicLong requestID = new AtomicLong(0); 107 | private final Map readIOCtxMap = new ConcurrentHashMap<>(); 108 | 109 | public IoURing(int queueDepth, int flags) { 110 | this._native = new IoURingNative(queueDepth, flags); 111 | this._native.init(); 112 | } 113 | 114 | public void shutdown() { 115 | this._native.exit(); 116 | readIOCtxMap.clear(); 117 | } 118 | 119 | 120 | private static Unsafe unsafe; 121 | static { 122 | try { 123 | Field f = Unsafe.class.getDeclaredField("theUnsafe"); 124 | f.setAccessible(true); 125 | unsafe = (Unsafe) f.get(null); 126 | }catch (NoSuchFieldException | IllegalAccessException t) { 127 | throw new RuntimeException(t); 128 | } 129 | } 130 | 131 | private static final long _FD_OFFSET; 132 | static { 133 | try { 134 | _FD_OFFSET = unsafe.objectFieldOffset(FileDescriptor.class.getDeclaredField("fd")); 135 | }catch (NoSuchFieldException e) { 136 | throw new RuntimeException(e); 137 | } 138 | } 139 | 140 | 141 | private int getFd(FileDescriptor fd) { 142 | return unsafe.getInt(fd, _FD_OFFSET); 143 | } 144 | 145 | /** 146 | * high level wrappers 147 | * 148 | * READ/WRITE 149 | * 150 | * REQUEST/COMPLETION 151 | */ 152 | 153 | public long prepareRead(FileDescriptor fd, long offset, byte[] bytes, int bufPos, int len) { 154 | int fd0 = getFd(fd); 155 | long reqId = requestID.incrementAndGet(); 156 | ByteBuffer buf = ByteBuffer.allocateDirect(len); 157 | DirectBuffer dBuf = (DirectBuffer) buf; 158 | int ret = _native.prepareRead(reqId, 0, fd0, dBuf.address(), len, offset); 159 | if (ret != 0) { 160 | dBuf.cleaner().clean(); 161 | throw new PrepareRWException("ret: " + ret); 162 | } 163 | readIOCtxMap.put(reqId, new ReadIOCtx(reqId, new byte[][]{bytes}, new int[]{ bufPos} , new ByteBuffer[]{ buf })); 164 | return reqId; 165 | } 166 | 167 | public long prepareReads(FileDescriptor fd, long offset, byte[][] bytes, int[] bufPos, int[] len) { 168 | int fd0 = getFd(fd); 169 | long reqId = requestID.incrementAndGet(); 170 | long[] bas = new long[bytes.length]; 171 | ByteBuffer[] dbs = prepareDirectBuf(false, bytes, bufPos, len, bas); 172 | int ret = _native.prepareReads(reqId, 0, fd0, bas, len, offset); 173 | if(ret != 0) { 174 | freeDirectBuffer(dbs); 175 | throw new PrepareRWException("ret: " + ret); 176 | } 177 | readIOCtxMap.put(reqId, new ReadIOCtx(reqId, bytes, bufPos, dbs)); 178 | return reqId; 179 | } 180 | 181 | 182 | public long prepareWrite(FileDescriptor fd, long offset, byte[] bytes, int bufPos, int len) { 183 | int fd0 = getFd(fd); 184 | long reqId = requestID.incrementAndGet(); 185 | ByteBuffer buf = ByteBuffer.allocateDirect(len); 186 | buf.put(bytes, bufPos, len); 187 | int ret = _native.prepareWrite(reqId, 0, fd0, ((DirectBuffer) buf).address(), len, offset); 188 | if(ret != 0) { 189 | ((DirectBuffer)buf).cleaner().clean(); 190 | throw new PrepareRWException("ret: " + ret); 191 | } 192 | return reqId; 193 | } 194 | 195 | public long prepareWrites(FileDescriptor fd, long offset, byte[][] bytes, int[] bufPos, int[] len) { 196 | int fd0 = getFd(fd); 197 | long reqId = requestID.incrementAndGet(); 198 | long[] bas = new long[bytes.length]; 199 | ByteBuffer[] dbs = prepareDirectBuf(true, bytes, bufPos, len, bas); 200 | int ret = _native.prepareWrites(reqId, 0, fd0, bas, len, offset); 201 | if(ret != 0) { 202 | freeDirectBuffer(dbs); 203 | throw new PrepareRWException("ret: " + ret); 204 | } 205 | return reqId; 206 | } 207 | 208 | 209 | 210 | 211 | public int submit() { 212 | int ret = _native.submit(); 213 | if(ret < 0) { 214 | throw new SubmitException("ret: " + ret); 215 | } 216 | return ret; 217 | } 218 | 219 | public IOResult waitCQEntry() { 220 | IOResult result = waitCQEntries(1); 221 | postProcessRead(result); 222 | return result; 223 | } 224 | 225 | public IOResult waitCQEntries(int nr) { 226 | long[] reqIds = new long[1], retCodes = new long[1]; 227 | int ret = _native.waitCQEntries(reqIds, retCodes, nr); 228 | if(ret != 0) { 229 | throw new WaitCQEException("ret: " + ret); 230 | } 231 | IOResult result = new IOResult(reqIds[0], retCodes[0]); 232 | postProcessRead(result); 233 | return result; 234 | } 235 | 236 | public void seenCQEntry(int n) { 237 | _native.advanceCQ(n); 238 | } 239 | 240 | public IOResult[] peekCQEntries(int nr) { 241 | long[] reqIds = new long[nr], retCodes = new long[nr]; 242 | int cnt = _native.peekCQEntries(reqIds, retCodes, nr); 243 | IOResult[] result = new IOResult[cnt]; 244 | for (int i = 0; i < cnt; i++) { 245 | result[i] = new IOResult(reqIds[i], retCodes[i]); 246 | postProcessRead(result[i]); 247 | } 248 | return result; 249 | } 250 | 251 | public IOResult waitCQEntryTimeout(long ms) { 252 | long[] reqIds = new long[1], retCodes = new long[1]; 253 | int ret = _native.waitCQEntryTimeout(reqIds, retCodes, ms); 254 | if(ret != 0) { 255 | throw new WaitCQEException("ret: " + ret); 256 | } 257 | IOResult result = new IOResult(reqIds[0], retCodes[0]); 258 | postProcessRead(result); 259 | return result; 260 | } 261 | 262 | private void postProcessRead(IOResult ioResult) { 263 | ReadIOCtx ctx = readIOCtxMap.remove(ioResult.reqId); 264 | if(ctx != null) { 265 | if(ioResult.res > 0) { 266 | ctx.prepareResult(ioResult.res); 267 | } 268 | freeDirectBuffer(ctx.directBuffer); 269 | } 270 | } 271 | 272 | private ByteBuffer[] prepareDirectBuf(boolean copy, byte[][] bytes, int[] bufPos, int[] len, long[] bas) { 273 | int cnt = bytes.length; 274 | ByteBuffer[] dbs = new ByteBuffer[cnt]; 275 | for(int i=0; i< cnt; i++) { 276 | byte[] b = bytes[i]; 277 | int bl = len[i]; 278 | ByteBuffer db = dbs[i] = ByteBuffer.allocateDirect(bl); 279 | bas[i] = ((DirectBuffer)db).address(); 280 | if (copy) { 281 | db.put(b, bufPos[i], len[i]); 282 | } 283 | } 284 | return dbs; 285 | } 286 | 287 | private void freeDirectBuffer(ByteBuffer[] dbs) { 288 | for(int i=0; i< dbs.length; i++ ) { 289 | ((DirectBuffer)dbs[i]).cleaner().clean(); 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/IoURingNative.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing; 2 | 3 | /** 4 | * Interface of liburing 5 | * 6 | * simply expose liburing api to java 7 | * 8 | * this is a low level and raw interface 9 | */ 10 | public class IoURingNative { 11 | // holder of pointer of io_uring instance 12 | private long _ring; 13 | 14 | static { 15 | System.loadLibrary("uringjni"); 16 | initIDs(); 17 | } 18 | 19 | private native static final void initIDs(); 20 | 21 | private final int queueDepth; 22 | private final long flags; 23 | /** 24 | * Construct a IoURing instance 25 | * 26 | * IoURing underline entity will be create, and queue initialized 27 | * @param queueDepth 28 | * @param flags 29 | */ 30 | public IoURingNative(int queueDepth, long flags) { 31 | this.queueDepth = queueDepth; 32 | this.flags = flags; 33 | } 34 | 35 | // int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags) 36 | private native int init0(int queueDepth, long flags); 37 | 38 | public void init() { 39 | int ret = init0(queueDepth, flags); 40 | if (ret < 0) { 41 | throw new RuntimeException("initialize ret :" + ret); 42 | } 43 | } 44 | //void io_uring_queue_exit(struct io_uring *ring) 45 | private native void exit0(); 46 | public void exit() { 47 | exit0(); 48 | } 49 | 50 | /** -------------- REGISTER -------------------- **/ 51 | // extern int io_uring_register_buffers(struct io_uring *ring, const struct iovec *iovecs, unsigned nr_iovecs) 52 | // extern int io_uring_unregister_buffers(struct io_uring *ring) 53 | // extern int io_uring_register_files(struct io_uring *ring, const int *files, unsigned nr_files); 54 | // extern int io_uring_unregister_files(struct io_uring *ring); 55 | 56 | public native int registerBuffers(long[] buffers, long[] bufferLengths); // 0 success 57 | public native int unregisterBuffers(); // 0 success 58 | public native int registerFiles(int[] fds); 59 | public native int unregisterFiles(); 60 | 61 | /** ------------- PREPARE READ/WRITE ----------- 62 | * 63 | * Don't exchange SQE, CQE structure cross JNI: 64 | * 65 | * for prepare request, we need associate sqe with cqe 66 | * we need user pass reqId when prepare request 67 | * 68 | * when request was completed, return the reqId (and retCode) from Native side 69 | **/ 70 | // static inline void io_uring_prep_read(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, off_t offset) 71 | // static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd, const struct iovec *iovecs, unsigned nr_vecs, off_t offset) 72 | // static inline void io_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, off_t offset, int buf_index) 73 | 74 | // static inline void io_uring_prep_write(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, off_t offset) 75 | // static inline void io_uring_prep_writev(struct io_uring_sqe *sqe, int fd, const struct iovec *iovecs, unsigned nr_vecs, off_t offset) 76 | // static inline void io_uring_prep_write_fixed(struct io_uring_sqe *sqe, int fd, const void *buf, unsigned nbytes, off_t offset, int buf_index) 77 | // 78 | // static inline void io_uring_prep_fsync(struct io_uring_sqe *sqe, int fd, unsigned fsync_flags) 79 | 80 | public native int prepareRead(long reqId, long flags, int fd, long buf, int bytes, long offset); 81 | public native int prepareReads(long reqId, long flags, int fd, long[] buf, int[] bytes, long offset); 82 | public native int prepareReadFixed(long reqId, long flags, int fd, long buf, int bytes, long offset, int bufIndex); 83 | 84 | public native int prepareWrite(long reqId, long flags, int fd, long buf, int bytes, long offset); 85 | public native int prepareWrites(long reqId, long flags, int fd, long[] buf, int[] bytes, long offset); 86 | public native int prepareWriteFixed(long reqId, long flags, int fd, long buf, int bytes, long offset, int bufIndex); 87 | 88 | public native int prepareFsync(long reqId, long flags, int fd, long syncFlags); 89 | 90 | /** ------------------------ REQUEST / RESPONSE ------------------ **/ 91 | // extern struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring); 92 | // static inline void io_uring_sqe_set_flags(struct io_uring_sqe *sqe, unsigned flags) 93 | // static inline void io_uring_sqe_set_data(struct io_uring_sqe *sqe, void *data) 94 | // 95 | // static inline void *io_uring_cqe_get_data(struct io_uring_cqe *cqe) 96 | // 97 | // extern int io_uring_submit(struct io_uring *ring); 98 | // extern int io_uring_submit_and_wait(struct io_uring *ring, unsigned wait_nr); 99 | // 100 | // extern int io_uring_wait_cqe_timeout(struct io_uring *ring, struct io_uring_cqe **cqe_ptr, struct __kernel_timespec *ts); 101 | // static inline int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr) 102 | // static inline int io_uring_wait_cqe_nr(struct io_uring *ring, struct io_uring_cqe **cqe_ptr, unsigned wait_nr) 103 | // 104 | // unsigned io_uring_peek_batch_cqe(struct io_uring *ring, struct io_uring_cqe **cqes, unsigned count); 105 | // static inline int io_uring_peek_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr) 106 | // 107 | // static inline void io_uring_cqe_seen(struct io_uring *ring, struct io_uring_cqe *cqe) 108 | // static inline void io_uring_cq_advance(struct io_uring *ring, unsigned nr) 109 | 110 | public native int submit(); 111 | public native int submitAndWait(int waitNr); 112 | public native int waitCQEntryTimeout(long[] reqIds, long[] retCodes, long millis); 113 | public native int waitCQEntries(long[] reqIds, long[] retCodes, int waitNr); 114 | public native int peekCQEntries(long[] reqIds, long[] retCodes, int count); 115 | public native void advanceCQ(int nr); 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/exception/IoURingException.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing.exception; 2 | 3 | public abstract class IoURingException extends RuntimeException { 4 | public IoURingException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/exception/PrepareRWException.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing.exception; 2 | 3 | public class PrepareRWException extends IoURingException { 4 | public PrepareRWException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/exception/SubmitException.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing.exception; 2 | 3 | public class SubmitException extends IoURingException { 4 | public SubmitException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/chinaxing/exception/WaitCQEException.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing.exception; 2 | 3 | public class WaitCQEException extends IoURingException { 4 | public WaitCQEException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/org/chinaxing/AsyncIOTest.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing; 2 | 3 | import org.junit.Test; 4 | import org.junit.Assert; 5 | 6 | import java.io.File; 7 | import java.io.FileDescriptor; 8 | import java.io.RandomAccessFile; 9 | import java.util.concurrent.CompletableFuture; 10 | import java.io.IOException; 11 | 12 | public class AsyncIOTest { 13 | @Test 14 | public void copy() throws Exception { 15 | String srcFile = "/tmp/a.txt"; 16 | String dstFile = "/tmp/b.txt"; 17 | prepdata(srcFile, 1024); 18 | prepdata(dstFile, 0); 19 | File src = new File(srcFile); 20 | File dst = new File(dstFile); 21 | 22 | RandomAccessFile src0 = new RandomAccessFile(src, "r"); 23 | RandomAccessFile dst0 = new RandomAccessFile(dst, "rw"); 24 | 25 | FileDescriptor srcFd = src0.getFD(); 26 | FileDescriptor dstFd = dst0.getFD(); 27 | 28 | AsyncIO aio = new AsyncIO("test-cp", 20, 0); 29 | byte[] buf = new byte[1024]; 30 | int offset = 0; 31 | while (true) { 32 | CompletableFuture f = aio.prepareReads(srcFd, offset, new byte[][]{buf}, new int[]{0}, new int[]{1024}); 33 | aio.submit(); 34 | Long res = f.get(); 35 | System.out.println("R: " + res); 36 | if(res == 0) { 37 | break; 38 | } 39 | f = aio.prepareWrites(dstFd, offset, new byte[][]{buf}, new int[]{0}, new int[]{(int)res.intValue()}); 40 | aio.submit(); 41 | f.get(); 42 | System.out.println("W: " + res); 43 | offset += res.intValue(); 44 | } 45 | dst0.close(); 46 | src0.close(); 47 | aio.shutdown(); 48 | Assert.assertTrue(fileContentEquals(srcFile, dstFile)); 49 | } 50 | @Test 51 | public void readFully() throws Exception { 52 | int n = 2048; 53 | prepdata("/tmp/c.txt", n); // 1000 * 11 54 | File src = new File("/tmp/c.txt"); 55 | RandomAccessFile src0 = new RandomAccessFile(src, "r"); 56 | AsyncIO aio = new AsyncIO("test-cp", 20, 0); 57 | int sz = n * 11; 58 | byte[] buf = new byte[sz]; 59 | long s = readFully(aio, buf, 0, sz, src0, 0).get(); 60 | Assert.assertEquals(s, sz); 61 | src0.close(); 62 | } 63 | 64 | public CompletableFuture readFully(AsyncIO aio, byte[] out, int pos, int len, RandomAccessFile file, long offset) throws IOException { 65 | CompletableFuture f = new CompletableFuture<>(); 66 | read(aio, out, pos, len, file, offset).whenComplete((r ,e) -> { 67 | if (e != null) { 68 | System.out.println("Read ERROR : " + len + " " + offset + e.getMessage()); 69 | f.completeExceptionally(e); 70 | } else { 71 | System.out.println("Read OK : " + len + " " + offset + " " + r); 72 | if(r.intValue() == len || r.intValue() == 0) { 73 | f.complete(r); 74 | } else { 75 | try { 76 | readFully(aio, out, pos + r.intValue(), len - r.intValue(), file, offset + r.intValue()) 77 | .whenComplete((r2, e2) -> { 78 | if(e2 != null) { 79 | f.completeExceptionally(e2); 80 | } else { 81 | f.complete(r + r2); 82 | } 83 | }); 84 | }catch (IOException e1) { 85 | System.out.println("ReadFully ex :" + e1.getMessage()); 86 | f.completeExceptionally(e1); 87 | } 88 | } 89 | } 90 | }); 91 | return f; 92 | } 93 | public CompletableFuture read(AsyncIO aio, byte[] out, int pos, int len, RandomAccessFile file, long offset) throws IOException { 94 | CompletableFuture c = aio.prepareRead(file.getFD(), offset, out, pos, len); 95 | aio.submit(); 96 | return c; 97 | } 98 | 99 | private static void prepdata(String file, int round) throws Exception { 100 | String cmd = "echo -n > " + file + ";" + 101 | "for((i=0; i<" + round + "; i++)) ; do " + 102 | "echo helloworld >> " + file + 103 | "; done"; 104 | System.out.println("prepare data CMD : " + cmd); 105 | Process p = Runtime.getRuntime().exec(new String[]{"bash", "-c", cmd}); 106 | p.waitFor(); 107 | } 108 | 109 | private static boolean fileContentEquals(String file, String file2) throws Exception { 110 | return 0 == Runtime.getRuntime().exec(new String[]{"diff", file, file2}).waitFor(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/org/chinaxing/IoURingTest.java: -------------------------------------------------------------------------------- 1 | package org.chinaxing; 2 | 3 | import org.chinaxing.IoURing.IOResult; 4 | import org.junit.Test; 5 | 6 | import java.io.File; 7 | import java.io.FileDescriptor; 8 | import java.io.IOException; 9 | import java.io.RandomAccessFile; 10 | 11 | public class IoURingTest { 12 | @Test 13 | public void copy() throws IOException { 14 | IoURing ring = new IoURing(20, 0); 15 | System.out.println(System.getProperty("java.library.path")); 16 | // File src = File.createTempFile("io-uring-cp", "src"); 17 | File src = new File("/root/a.txt"); 18 | File dst = new File("/root/b.txt"); 19 | // File dst = File.createTempFile("io-uring-cp", "dst"); 20 | 21 | RandomAccessFile src0 = new RandomAccessFile(src, "r"); 22 | RandomAccessFile dst0 = new RandomAccessFile(dst, "rw"); 23 | 24 | FileDescriptor srcFd = src0.getFD(); 25 | FileDescriptor dstFd = dst0.getFD(); 26 | byte[] buf = new byte[1024]; 27 | long offset = 0; 28 | while (true) { 29 | //ring.prepareReads(srcFd, offset, new byte[][]{buf}, new int[]{0}, new int[]{1024}); 30 | ring.prepareRead(srcFd, offset, buf, 0, 1024); 31 | ring.submit(); 32 | IOResult r = ring.waitCQEntry(); 33 | System.out.println("R: " + r.reqId + " -> " + r.res); 34 | ring.seenCQEntry(1); 35 | if(r.res == 0) { 36 | break; 37 | } 38 | //ring.prepareWrites(dstFd, offset, new byte[][]{buf}, new int[]{0}, new int[]{(int)r.res}); 39 | ring.prepareWrite(dstFd, offset, buf, 0, (int)r.res); 40 | ring.submit(); 41 | r = ring.waitCQEntry(); 42 | System.out.println("W: " + r.reqId + " -> " + r.res); 43 | ring.seenCQEntry(1); 44 | offset += r.res; 45 | } 46 | src0.close(); 47 | dst0.close(); 48 | ring.shutdown(); 49 | } 50 | } 51 | --------------------------------------------------------------------------------