Looking at a range of cross-platform languages, libraries and GUI toolkits, I often notice a conspicuous absence of support for asynchronous file I/O. This seems like too much of a common factor to be a coincidental oversight in all of them. However I don't know enough about individual OSs or these languages'/libraries' development to understand why they don't feature it.
Here are some examples.
- Python's asyncio (started in 3.4) contains ways to handle network and subprocess activity asynchronously, but nothing for reading files from disk.
- The Twisted library for event and protocol driven programming in Python seems to contain nothing for async file I/O.
- Qt 5's
QFile
specifically does not emit thereadyRead
orbytesWritten
signals, in contrast to otherQIODevice
implementations for networking. - All of wxWidget's file related classes are completely synchronous.
These are just the last four I looked up; it's possible that I picked four in a row without async file IO by coincidence, but they're all highly popular and stable. I know they're not the only popular libraries I've used over the last ten years in which I've missed it.
Maybe there's less demand for this than for reading data from a socket or subprocess, but is it so little as to be considered unwanted? A seemingly local file could be on the other side of a network connection (eg. a SMB share or NSF mount) making it not at all necessarily true that local disk access will be faster than a user would notice. It's not even necessarily true for a local, brand new SSD for that matter.
Common advice to roll one's own async file IO with threads seems counter to prevailing wisdom, when so much of what motivates toolkit usage is not rolling your own, especially when it comes to anything involving threading. I know it's possible, and maybe not that hard, but neither are many other things that are commonly available in these libraries and languages.
Let's take Qt as an example (extracting this from a comment on an answer). In Qt, if I want to do X
without pausing my entire program, I can use Y
:
- If X = redraw a canvas; Y = use signals and slots
- If X = read data via HTTP; Y = use signals and slots
- If X = get data from a subprocess; Y = use signals and slots
- But if X = get data from the hard drive; Y = implement something with a thread, or maybe two threads, two semaphores, special shared memory pointers, and maybe a bunch of other stuff.
Whether or not it's simple for me to implement is beside the point. The point is that file IO is an operation that can block, but it's consistently the odd one out in cross-platform toolkits and libraries by not having high-level handling.
Basically it seems that this omission demands extra complexity from the library user for a not-uncommon task, when the goal of these libraries is to absorb that kind of low-level implementation complexity. It seems odd that putting a local file on the other side of a local webserver makes it simpler to access in an event-driven program.
To be clear, and to make my question clear, this isn't a complaint about missing functionality. I want to understand cross-platform libraries and OS differences better, and so I'm genuinely curious about why this situation arose and whether there's some technical limitation or other constraint at the root of it.