4 ms·
This kind of error is a right of passage with WIN32 programming. For example, to do nontrivial i/o on Windows you have to create an OVERLAPPED object and give i
by jart 2y ago
This kind of error is a right of passage with WIN32 programming. For example, to do nontrivial i/o on Windows you have to create an OVERLAPPED object and give it to ReadFile() and WriteFile() which will return a pending status code, and write back to your OVERLAPPED object once the i/o has completed. Usually it makes the most sense to put that object on the stack. So if you return from your function without making sure WIN32 is done with that object, you're going to end up with bugs like this one. You have to call GetOverlappedResult() to do that. That means no throwing or returning until you do. Even if you call CancelIoEx() beforehand, you still need to call the result function. When you mix all that up with your WaitForMultipleObjects() call, it ends up being a whole lot of if statements you could easily get wrong if the ritual isn't burned into your brain.
UNIX system calls never do this. The kernel won't keep references to pointers you pass them and write to them later. It just isn't in the DNA. The only exceptions I can think of would be clone(), which is abstracted by the POSIX threads runtime, and Windows-inspired non-standard event i/o system calls like epoll.
- dataflow 2y ago> UNIX system calls never do this. The kernel won't keep references to pointers you pass them and write to them later. It just isn't in the DNA. The only exceptions I can think of would be clone(), which is abstracted by the POSIX threads runtime, and Windows-inspired non-standard system calls like epoll. I mean, this is because the UNIX model was based on readiness rather than completion. Which is slower. Hence the newer I/O models.
- jart 2y agoSystem calls take 10x longer on Windows than they do on UNIX. That's what i/o completion ports are working around. They solve a problem UNIX doesn't have.
- dataflow 2y agoNo, IOCP is not a workaround for syscall overhead. In fact this performance hit has nothing to do with syscall overhead. The overhead is O(1). The penalty of the readiness design is O(n). Because if the system can't copy into your n-byte buffer in the background, then you gotta block for O(n) time to memcpy it yourself.
- dataflow 2y agoWhat is the downvote here supposed to mean? Do people think this is wrong, or just not like it?
- saagarjha 2y agoYou do realize io_uring exists for a reason, right?
- cryptonector 2y agoThere are evented I/O APIs for Unix, though anything other than select(2) and poll(2) is non-standard, and while some do let you use pointer-sized user cookies to identify the events I've never seen a case where a programmer used a stack address as a cookie. I have seen cases where the address used as the cookie was freed before the event registration was deleted, or before it fired, leading to use-after-free bugs.
- ryao 2y agoThe UNIX AIO interface will for aio_read: https://www.man7.org/linux/man-pages/man7/aio.7.html https://www.man7.org/linux/man-pages/man7/aio.7.html That said, it would be insane to pass it pointers to the stack, as the only safe way to make that work would be to do `aio_suspend()` or busy wait and you might as well just use a synchronous read() function in that case.
- dataflow 2y ago> as the only safe way to make that work would be to do `aio_suspend()` or busy wait and you might as well just use a synchronous read() function in that case. Wait but read() wouldn't allow overlapping operations. Whereas if you suspend or busy wait you can do that for multiple operations executing concurrently. Also if the buffer is from a caller frame then you could also return safely no?