1 module during.tests.fsync;
2 
3 import during;
4 import during.tests.base;
5 
6 import core.stdc.stdlib;
7 import core.sys.linux.errno;
8 import core.sys.linux.fcntl;
9 import core.sys.posix.sys.uio : iovec;
10 import core.sys.posix.unistd;
11 
12 @("single")
13 unittest
14 {
15     Uring io;
16     auto res = io.setup();
17     assert(res >= 0, "Error initializing IO");
18 
19     auto fname = getTestFileName!"fsync_single";
20     auto fd = openFile(fname, O_CREAT | O_WRONLY);
21     scope (exit) unlink(&fname[0]);
22 
23     auto ret = io
24         .putWith!((ref SubmissionEntry e, int fd) => e.prepFsync(fd))(fd)
25         .submit(1);
26     assert(ret == 1);
27     assert(io.length == 1);
28     assert(io.front.res == 0);
29     io.popFront();
30 
31     close(fd);
32 }
33 
34 @("barier")
35 unittest
36 {
37     enum NUM_WRITES = 4;
38     Uring io;
39     auto res = io.setup();
40     assert(res >= 0, "Error initializing IO");
41 
42     auto fname = getTestFileName!"fsync_barier";
43     auto fd = openFile(fname, O_CREAT | O_WRONLY);
44     scope (exit) unlink(&fname[0]);
45 
46     iovec[NUM_WRITES] iovecs;
47     foreach (i; 0..NUM_WRITES)
48     {
49         iovecs[i].iov_base = malloc(4096);
50         iovecs[i].iov_len = 4096;
51     }
52 
53     int off;
54     foreach (i; 0..NUM_WRITES)
55     {
56         io.putWith!((ref SubmissionEntry e, int fd, ref iovec v, int off)
57         {
58             e.prepWritev(fd, v, off);
59             e.user_data = 1;
60         })(fd, iovecs[i], off);
61         off += 4096;
62     }
63 
64     io.putWith!((ref SubmissionEntry e, int fd)
65     {
66         e.prepFsync(fd, FsyncFlags.DATASYNC);
67         e.user_data = 2;
68         // TODO: Works with IO_LINK but not without it: See: https://github.com/axboe/liburing/issues/33
69         e.flags = cast(SubmissionEntryFlags)(SubmissionEntryFlags.IO_DRAIN | SubmissionEntryFlags.IO_LINK);
70         // e.flags = SubmissionEntryFlags.IO_DRAIN;
71     })(fd);
72 
73     auto ret = io.submit(NUM_WRITES + 1);
74     if (ret < 0)
75     {
76         if (ret == -EINVAL)
77         {
78             version (D_BetterC)
79             {
80                 errmsg = "kernel may not support barrier fsync yet";
81                 return;
82             }
83             else throw new Exception("kernel may not support barrier fsync yet");
84         }
85         assert(0, "submit failed");
86     }
87     else assert(ret == NUM_WRITES + 1);
88 
89     assert(io.length == NUM_WRITES + 1);
90     foreach (i; 0..NUM_WRITES + 1)
91     {
92         if (io.front.res == -EINVAL)
93         {
94             version (D_BetterC)
95             {
96                 errmsg = "kernel doesn't support IOSQE_IO_DRAIN";
97                 break;
98             }
99             else throw new Exception("kernel doesn't support IOSQE_IO_DRAIN");
100         }
101         assert(io.front.res >= 0);
102         if (i < NUM_WRITES) assert(io.front.user_data == 1, "unexpected op completion");
103         else assert(io.front.user_data == 2, "unexpected op completion");
104 
105         io.popFront();
106     }
107 
108     close(fd);
109 }
110 
111 @("range")
112 unittest
113 {
114     enum NUM_WRITES = 4;
115     Uring io;
116     auto res = io.setup();
117     assert(res >= 0, "Error initializing IO");
118 
119     auto fname = getTestFileName!"fsync_range";
120     auto fd = openFile(fname, O_CREAT | O_WRONLY);
121     scope (exit) unlink(&fname[0]);
122 
123     iovec[NUM_WRITES] iovecs;
124     foreach (i; 0..NUM_WRITES)
125     {
126         iovecs[i].iov_base = malloc(4096);
127         iovecs[i].iov_len = 4096;
128     }
129 
130     int off;
131     foreach (i; 0..NUM_WRITES)
132     {
133         io.putWith!((ref SubmissionEntry e, int fd, ref iovec v, int off)
134         {
135             e.prepWritev(fd, v, off);
136             e.user_data = 1;
137             e.flags = SubmissionEntryFlags.IO_LINK;
138         })(fd, iovecs[i], off);
139         off += 4096;
140     }
141 
142     io.putWith!((ref SubmissionEntry e, int fd)
143     {
144         e.prepSyncFileRange(fd, (NUM_WRITES - 1) * 4096, 4096);
145         e.user_data = 2;
146         e.flags = SubmissionEntryFlags.IO_LINK;
147     })(fd);
148 
149     auto ret = io.submit(NUM_WRITES + 1);
150     if (ret < 0)
151     {
152         if (ret == -EINVAL)
153         {
154             version (D_BetterC)
155             {
156                 errmsg = "kernel may not support range file sync yet";
157                 return;
158             }
159             else throw new Exception("kernel may not support range file sync yet");
160         }
161         assert(0, "submit failed");
162     }
163     else assert(ret == NUM_WRITES + 1);
164 
165     assert(io.length == NUM_WRITES + 1);
166     foreach (i; 0..NUM_WRITES + 1)
167     {
168         assert(io.front.res >= 0);
169         if (i < NUM_WRITES) assert(io.front.user_data == 1, "unexpected op completion");
170         else assert(io.front.user_data == 2, "unexpected op completion");
171         io.popFront();
172     }
173 
174     close(fd);
175 }