4 ms·
It doesn't really make sense to have an API to tell if reading from a file would block or not because page cache data can be evicted at any time. Even if there
by eklitzke 3y ago
It doesn't really make sense to have an API to tell if reading from a file would block or not because page cache data can be evicted at any time. Even if there was an API that could tell you your next read would be non-blocking, there could be a race where the page cache entry could be evicted before you could do the read.
- Filligree 3y agoThat sort of race condition is only relevant if it affects correctness. Here it means a read might be unexpectedly slow, but you'll still get the same data. 99.9% of the time the data will not be evicted prior to the read, so it's still going to be a win overall.
- sgtnoodle 3y agoIf you're writing a latency sensitive program, 99.9% might as well be 0% though; you're going to end up with an asynchronous anyway.
- mort96 3y agoRemember that a filesystem read might be a disk read in the background. You don't really want to block the main thread for 2 seconds while sshfs does its thing.
- mort96 3y agoDamn, I obviously meant "network read" not a "disk read". Oh well.
- viraptor 3y agoDepends how you define that API. It could do something like "check if available, and if so, pin it until the next read (which will happen right now)". Not saying that's a good idea, but if such API was needed, it is possible.
- plorkyeran 3y agoA nonblocking read which fails if the data isn't cached and it'd have to perform io would generally be easier to get correct.
- _nalply 3y agoPlease let me try to understand you. The nonblocking read just failed. The task initiates IO then because the IO is async it lets other tasks run while waiting for data. Later, when the IO finished, the task can read the memory. Is this what you mean? I think there must be a data race somewhere or io_uring would be superfluous. Everything consisting of at least two interruptible steps at the lowest level in the CPU but where people expect no change in state is subject to some data race. It's really difficult to get this 100% correct. Something works fine for 99.99% of the time. Such a situation can be very nasty to get it right. Perhaps the step "Later, when the IO finished, the task can read the memory" cannot be done atomically.
- quietbritishjim 3y agoI think the parent comment was talking about what you would need if you weren't using io_ring. First, in the main async thread, you attempt a non blocking read. If that succeeds, great. If not, you hand over to another thread to do a blocking read, which is what libuv currently does for all reads. Due to the race that you described, there's a chance that by the time it gets there it will be a non blocking read, but that's ok. Overall, the main thread often gets data straight away when it's available, and is never blocked when it's not. If you're using io_uring, the data is read into the user buffer by the kernel during the async call, so there's no race. There's no point having a separate fast path in that case, because the io_uring call can just return the data immediately if it's available.
- destructionator 3y agoOn Windows, the normal ReadFile function can return synchronously or async (if you pass the appropriate arguments to it) depending on presence in the cache. Since it is all one atomic operation, there's no race condition; you just call it and see in the return value if it succeeded immediately or queued the operation. (If you haven't read about Windows' overlapped i/o, do so, it is actually quite nice to use.) io_uring brings something similar to Linux.