| ... | @@ -268,29 +268,34 @@ pub const IO_Uring = struct { | ... | @@ -268,29 +268,34 @@ pub const IO_Uring = struct { |
| 268 | /// See https://github.com/axboe/liburing/issues/103#issuecomment-686665007. | 268 | /// See https://github.com/axboe/liburing/issues/103#issuecomment-686665007. |
| 269 | /// Matches the implementation of io_uring_peek_batch_cqe() in liburing, but supports waiting. | 269 | /// Matches the implementation of io_uring_peek_batch_cqe() in liburing, but supports waiting. |
| 270 | pub fn copy_cqes(self: *IO_Uring, cqes: []linux.io_uring_cqe, wait_nr: u32) !u32 { | 270 | pub fn copy_cqes(self: *IO_Uring, cqes: []linux.io_uring_cqe, wait_nr: u32) !u32 { |
| 271 | const count = self.copy_cqes_ready(cqes, wait_nr); | 271 | const count = self.copy_cqes_ready(cqes); |
| 272 | if (count > 0) return count; | 272 | if (count > 0) return count; |
| 273 | if (self.cq_ring_needs_flush() or wait_nr > 0) { | 273 | if (self.cq_ring_needs_flush() or wait_nr > 0) { |
| 274 | _ = try self.enter(0, wait_nr, linux.IORING_ENTER_GETEVENTS); | 274 | _ = try self.enter(0, wait_nr, linux.IORING_ENTER_GETEVENTS); |
| 275 | return self.copy_cqes_ready(cqes, wait_nr); | 275 | return self.copy_cqes_ready(cqes); |
| 276 | } | 276 | } |
| 277 | return 0; | 277 | return 0; |
| 278 | } | 278 | } |
| 279 | | 279 | |
| 280 | fn copy_cqes_ready(self: *IO_Uring, cqes: []linux.io_uring_cqe, wait_nr: u32) u32 { | 280 | fn copy_cqes_ready(self: *IO_Uring, cqes: []linux.io_uring_cqe) u32 { |
| 281 | _ = wait_nr; | | |
| 282 | const ready = self.cq_ready(); | 281 | const ready = self.cq_ready(); |
| 283 | const count = @min(cqes.len, ready); | 282 | const count = @min(cqes.len, ready); |
| 284 | var head = self.cq.head.*; | 283 | const head = self.cq.head.* & self.cq.mask; |
| 285 | const tail = head +% count; | 284 | const tail = (self.cq.head.* +% count) & self.cq.mask; |
| 286 | // TODO Optimize this by using 1 or 2 memcpy's (if the tail wraps) rather than a loop. | 285 | |
| 287 | var i: usize = 0; | 286 | if (head <= tail) { |
| 288 | // Do not use "less-than" operator since head and tail may wrap: | 287 | // head behind tail -> no wrapping |
| 289 | while (head != tail) { | 288 | @memcpy(cqes[0..count], self.cq.cqes[head..tail]); |
| 290 | cqes[i] = self.cq.cqes[head & self.cq.mask]; // Copy struct by value. | 289 | } else { |
| 291 | head +%= 1; | 290 | // head in front of tail -> buffer wraps |
| 292 | i += 1; | 291 | const two_copies_required: bool = self.cq.cqes.len - head < count; |
| | 292 | const amount_to_copy_in_first = if (two_copies_required) self.cq.cqes.len - head else count; |
| | 293 | @memcpy(cqes[0..amount_to_copy_in_first], self.cq.cqes[head .. head + amount_to_copy_in_first]); |
| | 294 | if (two_copies_required) { |
| | 295 | @memcpy(cqes[amount_to_copy_in_first..count], self.cq.cqes[0..tail]); |
| | 296 | } |
| 293 | } | 297 | } |
| | 298 | |
| 294 | self.cq_advance(count); | 299 | self.cq_advance(count); |
| 295 | return count; | 300 | return count; |
| 296 | } | 301 | } |