1 module during.tests.rw; 2 3 import during; 4 import during.tests.base; 5 6 import core.stdc.stdio; 7 import core.stdc.stdlib; 8 import core.sys.linux.fcntl; 9 import core.sys.posix.sys.uio : iovec; 10 import core.sys.posix.unistd : close, read, unlink, write; 11 import std.algorithm : copy, equal, map; 12 import std.range : iota; 13 14 // NOTE that we're using direct linux/posix API to be able to run these tests in betterC too 15 16 @("readv") 17 unittest 18 { 19 // read it and check if read correctly 20 Uring io; 21 auto res = io.setup(4); 22 assert(res >= 0, "Error initializing IO"); 23 24 // prepare some file 25 auto fname = getTestFileName!"readv_test"; 26 ubyte[256] buf; 27 iota(0, 256).map!(a => cast(ubyte)a).copy(buf[]); 28 auto file = openFile(fname, O_CREAT | O_WRONLY); 29 auto wr = write(file, &buf[0], buf.length); 30 assert(wr == buf.length); 31 close(file); 32 scope (exit) unlink(&fname[0]); 33 34 file = openFile(fname, O_RDONLY); 35 scope (exit) close(file); 36 37 ubyte[32] readbuf; // small buffer to test reading in chunks 38 iovec v; 39 v.iov_base = cast(void*)&readbuf[0]; 40 v.iov_len = readbuf.length; 41 foreach (i; 0..8) // 8 operations must complete to read whole file 42 { 43 io 44 .putWith!( 45 (ref SubmissionEntry e, int f, int i, iovec* v) 46 => e.prepReadv(f, *v, i*32))(file, i, &v) 47 .submit(1); 48 assert(io.front.res == 32); // number of bytes read 49 assert(readbuf[] == buf[i*32..(i+1)*32]); 50 io.popFront(); 51 } 52 53 // try to read after the file content too 54 assert(io.empty); 55 io 56 .putWith!( 57 (ref SubmissionEntry e, int f, iovec* v) 58 => e.prepReadv(f, *v, 256))(file, &v) 59 .submit(1); 60 assert(io.front.res == 0); // ok we've reached the EOF 61 } 62 63 @("writev") 64 unittest 65 { 66 // prepare uring 67 Uring io; 68 auto res = io.setup(4); 69 assert(res >= 0, "Error initializing IO"); 70 71 // prepare file to write to 72 auto fname = getTestFileName!"writev_test"; 73 auto f = openFile(fname, O_CREAT | O_WRONLY); 74 scope (exit) unlink(&fname[0]); 75 76 // prepare chunk buffer 77 ubyte[32] buffer; 78 iovec v; 79 v.iov_base = cast(void*)&buffer[0]; 80 v.iov_len = buffer.length; 81 82 { 83 scope (exit) close(f); 84 85 // write some data to file 86 foreach (i; 0..8) 87 { 88 foreach (j; 0..32) buffer[j] = cast(ubyte)(i*32 + j); 89 SubmissionEntry entry; 90 entry.prepWritev(f, v, i*32); 91 entry.user_data = i; 92 io.put(entry).submit(1); 93 94 assert(io.front.user_data == i); 95 assert(io.front.res == 32); 96 io.popFront(); 97 } 98 assert(io.empty); 99 } 100 101 // now check back file content 102 ubyte[257] readbuf; 103 f = openFile(fname, O_RDONLY); 104 scope (exit) close(f); 105 auto r = read(f, cast(void*)&readbuf[0], 257); 106 assert(r == 256); 107 assert(readbuf[0..256].equal(iota(0, 256))); 108 } 109 110 @("read/write fixed") 111 unittest 112 { 113 static void readOp(ref SubmissionEntry e, int f, ulong off, ubyte[] buffer, int data) 114 { 115 e.prepReadFixed(f, off, buffer, 0); 116 e.user_data = data; 117 } 118 119 static void writeOp(ref SubmissionEntry e, int f, ulong off, ubyte[] buffer, int data) 120 { 121 e.prepWriteFixed(f, off, buffer, 0); 122 e.user_data = data; 123 } 124 125 // prepare uring 126 Uring io; 127 auto res = io.setup(4); 128 assert(res >= 0, "Error initializing IO"); 129 130 // prepare some file content 131 auto fname = getTestFileName!"rw_fixed_test"; 132 auto tgtFname = getTestFileName!"rw_fixed_test_copy"; 133 ubyte[256] buf; 134 iota(0, 256).map!(a => cast(ubyte)a).copy(buf[]); 135 auto file = openFile(fname, O_CREAT | O_WRONLY); 136 auto wr = write(file, &buf[0], buf.length); 137 assert(wr == buf.length); 138 close(file); 139 140 scope (exit) 141 { 142 unlink(&fname[0]); 143 unlink(&tgtFname[0]); 144 } 145 146 // register buffer 147 enum batch_size = 64; 148 ubyte* bp = (cast(ubyte*)malloc(2*batch_size)); 149 assert(bp); 150 ubyte[] buffer = bp[0..batch_size*2]; 151 auto r = io.registerBuffers(buffer); 152 assert(r == 0); 153 154 // open copy files 155 auto srcFile = openFile(fname, O_RDONLY); 156 auto tgtFile = openFile(tgtFname, O_CREAT | O_WRONLY); 157 158 // copy file 159 { 160 scope (exit) 161 { 162 close(srcFile); 163 close(tgtFile); 164 } 165 166 ulong roff, woff; 167 bool waitWrite, waitRead; 168 uint lastRead; 169 int bidx; 170 io.putWith!readOp(srcFile, roff, buffer[bidx*batch_size..bidx*batch_size+batch_size], 1).submit(1); 171 waitRead = true; 172 while (true) 173 { 174 bool isRead; 175 if (io.front.user_data == 1) // read op 176 { 177 assert(io.front.res >= 0); 178 lastRead = io.front.res; 179 isRead = true; 180 roff += io.front.res; // move read offset 181 waitRead = false; 182 if (io.front.res == 0 && !waitWrite) 183 { 184 assert(roff == woff); 185 break; // we are done 186 } 187 } 188 else if (io.front.user_data == 2) // write op 189 { 190 assert(io.front.res > 0); 191 woff += io.front.res; 192 waitWrite = false; 193 } 194 else assert(0, "unexpected user_data"); 195 io.popFront(); 196 197 if ((!waitWrite && isRead) // we've completed reading and can write current and read next 198 || !waitRead && !isRead) // we've completed writing and can read next and write current 199 { 200 if (lastRead == 0) 201 { 202 assert(roff == woff); 203 break; // we are done 204 } 205 206 // start write op (with same buffer as used in read op) 207 io.putWith!writeOp(tgtFile, woff, buffer[bidx*batch_size..bidx*batch_size+batch_size][0..lastRead], 2); 208 waitWrite = true; 209 210 // switch buffers 211 bidx = (bidx+1) % 2; 212 213 // start next read op 214 io.putWith!readOp(srcFile, roff, buffer[bidx*batch_size..bidx*batch_size+batch_size], 1); 215 waitRead = true; 216 217 // and submit both 218 io.submit(2); 219 } 220 } 221 } 222 223 r = io.unregisterBuffers(); 224 assert(r == 0); 225 226 // and check content of the copy 227 file = openFile(tgtFname, O_RDONLY); 228 scope (exit) close(file); 229 auto rd = read(file, cast(void*)&buf[0], 256); 230 assert(rd == 256); 231 assert(buf[0..256].equal(iota(0, 256))); 232 }