Thoughts on async io
Posted 2022-04-18
Blog
Articles- January 2, 2023
- More mixin tips to avoid stringof
- new textlayout class, dconf online thoughts, step by step tech progression
- Write barriers might not fix thread registration since you need to scan the stack
- Brief thought: D from Java/.net could be another write barrier benefit
- DIP DIP part 2: the user side
- new dmd, new DIP, beta dub docs, game lib announced. And politics in D
- dconf 2022 online schedule, some arsd.game work, template emission discussion with d index file proposal
- How NO_SCAN makes shorter GC pauses
- Write barriers could work
- simpledisplay custom font stream of thought
- Writing more arsd dox
- D on Arduino
- October 3, 2022
- September 26, 2022 - tip of the week to bypass IFTI
- September 19, 2022
- My thoughts on bitfields and recap of binary literals
- September 5, 2022
- preliminary design discussion of arsd.core event loop
- arsd 10.9 released
- Idea: user-extensible effect attributes
- DConf 2022 thoughts
- DConf in session, some more exception experimentation from me
- musings on pure websites
- static import object tricks
- Thoughts on inferred attributes
- I write on respect in discussions. D Vision document partial draft released.
- Exception idea: third Throwable branch. Probably won't work but written anyway.
- DIP DIP
- June 13, 2022
- June 6, 2022
- DConf 22 announced. arsd 11 not likely needed soon, 10.9 expected in another month
- May 23, 2022
- ImportC's module namespace problem
- Happy birthday, ImportC!
- Developers developers developers developers
- More dub 2.0 idea refinement
- Thoughts on async io
- April 11, 2022
- arsd bug fixes, stack overflow answer about X child windows
- dub 2.0 design discussion
- Tip of the week on justification comments
- March 14, 2022
- March 7, 2022
- February 28, 2022
- February 21, 2022
- dpldocs reindexing
- More rant on names
- Adam's thoughts on naming in code, tip from Steven about ufcs `i`
- Tip of the week: use mixin to hack around order-of-eval problems
- Tip: if code getting complicated, try rethinking the approach
- January 10, 2022
- arsd 10.5 coming this week, new midi code, a FF1 nsf player/editor application
- December 27, 2021
- my thoughts on std.socket design
- arsd work in progress updates and new "do it in D" idioms section
- December 6, 2021
- gdc sync going upstream, arsd 10.4 released, otherwise i was busy.
- working toward arsd 10.4
- DConf Online 2021 livestream
- arsd.webview work, public imports in adrdox changed
- importC released, preview of arsd web, database, gui updates, phobos2 coming, dual context tip of the week
- October 25, 2021
- My webasm updates, gdc in D
- Assorted quick thoughts
- October 4, 2021
- arsd 10.3, dmd -target, druntime.dll
- A potential GC puzzler discovered
- nanovega font improvement from me. from community: DConf Online 2021 announcement, GtkD blog back to work, DCompute update
- Rant: using Firefox is meaningless
- August 30, 2021
- Improving today's error handling
- Thoughts on error handling
- August 9, 2021
- August 2, 2021
- arsd.qrcode introduced and on my wish list: __arguments.
- July 19, 2021
- Drama on the github
- arsd 10.2 with http cookies support
- June 28, 2021
- arsd 10.1, thought on virtual functions, community announces light weight Druntime 0.3 among others
- arsd v10 tagged
- New dmd and ldc releases, ldc with druntime.dll!
- gdc 11 out with a lot of D updates
- May 24, 2021 general update
- Progress continues toward minigui 2.0
- C compiler in dmd? new string type in Phobos? brief update on my minigui overhaul
- Simpledisplay additions, minigui event changes
- April 26, 2021
- arsd 9.5 -- UPDATE: false alarm i forgot to tag it!
- April 12, 2021
- arsd 9.4 tagged, adrdox 2.5 released
- Tip of the week: using C libs from D
- working on gdc on Windows
- tip: use union to manually control struct member destructor
- Rant: people in the past weren't stupid
- Tip on DIY closures
- arsd 9.2
- Did you know about D anonymous classes?
- "Mental friction": my view on why D rox
- String interpolation DIP prototype
- terminal inline syntax highlighting, sdpy fonts improved
- January 18, 2021
- gdb debugging tips
- A little work on sdpy/terminal interop and apng debugging
- New plain tcp fiber socket class (with "how it works" docs), new arsd docs started, new dub subpackages in arsd. Also Turkish newsgroups added to forum.dlang.org
- Little audio player in D
- Thoughts on tutorial writing benefits, D marketing, and some simpledisplay.d improvements.
- arsd 9.0 rollup release, my thoughts on "google it" culture and related practices
- dpldocs.info cross-package search finally released! and more terminal getline enhancements
- I did a dconf livestream!
- New selective mouse input in terminal stack, Xft used in simpledisplay to improve TrueType font support
- simpleaudio now has playOgg, Mp3, Wav with resampling and can access multiple soundcards on Linux, adrdox gets ddoc on function params
- Weekend experiment: declarative GUI in D
- October 26, 2020
- My DConf livestream sneak preview
- Off topic jrpg video game review
- My thoughts on breakage, and I'll be in DConf Online 2020
- cgi.d hybrid server basically working, terminal.d can redirect stdout to a window if requested
- Some talk on cgi.d in benchmarks
- September 14, 2020
- New D update "dwidder" website launched, making-of post here
- white noise app in D
- More modern opengl in simpledisplay, document undocumented on dpldocs.info, tip on default template args
- Xlib taskbar in D
- D Tetris running on Webassembly
- Tetris in D
- Zero-runtime classes
- DConf online in the works for Nov 21-22, image copy/paste coming to sdpy soon
- July 13, 2020
- July 6, 2020
- simpledisplay getting dynamic loads, terminal gui gracefully degrades, i muse on scope raii classes
- Adam's dynamic link transition
- June 15, 2020
- June 8, 2020
- June 1, 2020
- foot pedal and midi fun, some dmd speed enhancements. Forum argues about @safe by default on extern.
- May 18, 2020
- simpleaudio dev work, rasp pi gpio module, static foreach rant, gcc 10's D support upped
- May 4, 2020
- my http more compatible with ssl, script+jsvar can do subclasses of D objects
- i want to make a jrpg, and have eye damage.
- Dustmite post on official blog
- What if I were dictator?
- March 30, 2020
- terminal.d with built-in emulator option releaed
- Online DConf in the works
- Dconf 2020 cancelled, Adam plays with terminal gui integration
- March 2, 2020
- some adrdox/dpldocsinfo updates
- terminal.d gets clipboard functions, ldc 1.20 out.
- DConf keynote speaker announced: Lua architect Roberto Ierusalimschy, Named args DIP discussed
- February 3, 2020
- Adam's terminal suite explained
- Understanding mixin templates, terminal.d improvements
- My attribute-by-default proposal. Also dmd 2.090 came out.
- DConf 2020 announced: June 17-20 in London. @safe by default debated. Adam did: Android, JNI, WebSocket in arsd libs
- tar.xz, --DRT tip, dom bug fixes, more Android and JNI, link to old phobos docs
- LDC 1.19 - Android, AVR. My rant on tests, update on JNI and COM.
- Walter's string interpolation proposal is OK but not great. My Android thing nearing beta release. dub downtime explained.
- Android project update, introduction to arsd.jni
- New pattern about interface contracts
- Adam shares Windows console secrets - DO NOT USE chcp!!
- Adam's rant on benchmarks
- Socket tutorial
- November 4, 2019
- October 28, 2019
- arsd package updates, forum nonsense
- Update on Android also with mtriple appendix
- Adam does iOS "goodbye world"
- September 30, 2019
- D turns 20, Adam rants on software freedom
- Named arg DIPs and my thoughts on code organization
- September 9, 2019
- I wrote about mixin templates vs string mixins on Stack Overflow
- August 26, 2019
- Bug bounty in D again - my hot take, on reusing code, a fun picture, my tentative plan for the next month
- Time invested is worth a lot
- cgi.d's new scheduler, static this tricks
- July 29, 2019
- July 22, 2019
- Solving vs managing problems
- A big week in the arsd repo
- July 1, 2019
- June 24, 2019
- June 17, 2019
- CRTP thoughts, named arguments DIP review, DConf videos now on youtube
- musings on hybrid CT/RT tests, some more progress on new web framework
- a little more webassembly
- May 20, 2019
- Adam's string interpolation proposal
- DMD 2.086 live, GCC 9 with D support formally released, DConf coming soon, links to posts on builder pattern and disallowing implicit conversions with templates, and 2d array op overloads
- template constraint error improvements coming?
- dmd 2.086 beta, dstep 1.0 released, Adam works on memory usage
- obj-c and webassembly report, tips on is expressions linked.
- new ldc, new dmd, dpp on the blog
- D's future discussed in forums
- LDC beta, DConf blog link, Adam introduces gamehelpers.d
- March 18, 2019
- LDC 1.15.0-beta1, responsive design rant
- dmd 2.085.0 released
- Obj-C interop and D without druntime code to copy/paste
- dmd beta, more info coming next time, demo of new web framework initial prototype
- automatic web interface discussion, reflection tips and tricks
- Adam busy with weather and a move, lots of community announcements
- January 28, 2019
- Working on official blog 2018 retro, C++ new wrapped, dmd reading zips?
- dmd obj-c growing, Adam static foreaches an interface to RPC
- dmd 2.084, hope for future, but busy non-D week for me
- IDE tools released, my cgi.d gets new features
- DConf announced, tip, Adam rants: mouse trap
- This Week in D is back!
I'll write a few thoughts on async io.
Core D Development Statistics
In the community
Community announcements
See more at the announce forum.
Thoughts on async io
Windows Overlapped I/O
On the forum last week, someone asked about async io in phobos, and I said the Windows Overlapped I/O functions are actually quite nice, and I'd suggest basing interfaces on it.
Of course, there was also a discussion about if it should be in Phobos or in dub. Let me just briefly say there's value in interoperability by putting interfaces in the standard library, but it indeed isn't required and comes with possible downsides, especially during the beta phase. But I want to talk more about the Windows api itself than the politics of D development today.
The way the Windows functions work is you open the file with the overlapped flag, then call the ReadFileEx/WriteFileEx (or parallels for sockets) functions to issue your command, giving them a buffer to use. The operating system tells you when it is done. I already like that basic outline - it is pretty simple to use, but we also need to get into the details.
So, how does the operating system tell you when it is done? There's a few options:
The reason I like this more than other async i/o things I've seen is that it avoids cross-thread issues. Your same thread is processing the result when you know it isn't busy with anything else, so it is pretty predictable and easier to use without bugs.
Emulating this on posix could be done with the select family of functions behind the scenes.
This option is often the easiest to use in the middle of other work.
This is also pretty nice for managing your own worker thread system. The Windows event object is another really nice facility for lightweight sync work. Druntime has a thin wrapper on Windows and emulation of it on posix already too, so that'd be usable out of the box for a hypothetical D api too.
This option gives a lot of custom flexibility. Of course, a callback function could (and sometimes must) trigger an event, but letting the OS do it directly might be quicker than waiting for the original thread to be ready to call a function when it only has this one job.
If your work can be be done by any arbitrary thread, this option gives excellent performance. It is the hardest to emulate, but something akin to a Linux epoll one shot comes close. I think this is what those event driven io libs typically do, but I haven't looked at their sources (but copying the Windows API is a good idea so no surprise if that's what they did!).
As you can see, there's a few options, which can look complicated at first sight, but each one has its uses in different circumstances, and each option is pretty easy to put into a cross-platform api and pretty easy to use, since the threads processing data are all explicit, so no surprises, and flexible to integrate into other event loops (you can trigger things on those custom events, or on Windows, the system functions all support the various options anyway - the MsgWaitForMultipleObjectsEx is particularly nice to work in some async io to an existing gui application).
Worth noting by the way that turning callbacks into things like promises or async/await syntaxes, or into pseudo-synchronous execution in fibers is all pretty trivial. See my conceptual overview of fibers here: http://arsd-official.dpldocs.info/arsd.fibersocket.html#conceptual-overview and notice how if the "on complete" callback is simply fiber.call() and you immediately fiber.yield() after issuing the read/write command, you've achieved the fiber illusion.
For this reason, I don't think making the fiber an explicit goal for the basic API. If you build the pieces correctly, the fiber is a trivially easy add-on, as well as other useful things. If you don't do the pieces right, you'll find trouble later. When in doubt, copy something successful in the real world - even if Windows API isn't ideal, it is well-known through years of experience.
Additionally, please note that Windows' pending async i/o can be cancelled as well. Cancelling async things and dealing with other interruptions is something easy to forget when doing initial designs, but important to have as things mature.
Aside - other apis
I want to briefly describe some other options and why I like the Windows way better.
One option I used once was Boost's async I/O. This spawns a thread to do some work and calls your callback from there. This is relatively hard to use because your data can easily fall to race conditions, and any objects with thread affinity (including D's TLS by default variables) might cause trouble.
But the main alternative is the lower level functions we see on Linux and friends. The key difference is that the Windows way is you ask the OS to do something, it takes care of the queuing and tells you when it is done. The Posix way is it won't accept the command until it is able to complete; it will either block until it can accept it, or you can have the OS tell you when a file descriptor is prepared to accept a command. At that point, you can issue it and it completes immediately.
So the big difference is the Windows way lets the OS manage the command queuing whereas the Posix way needs you to manage it. Since Windows manages more work for you, I say this makes it a bit easier to use. And it is a little more abstracted, so it is easier to emulate on other systems.
(There's some performance advantages to the Windows way too, merging system calls, but I'm not as concerned about that here, especially since there's comparable alternatives on the other platforms anyway, and besides, the performance difference isn't that great regardless. And the posix way might be able to use stack buffers more easily but async tends to need longer lifetime buffer anyway so meh.)
Hypothetical D wrapper
The best wrappers are often not that much different than the things they wrap. Adding things can be performance problems and removing things can harm flexibility, and besides, being similar to what is already known means it is easier to learn since the existing documentation can be adapted directly.
But, that said, you do want to use your language features to reduce the possibilities of errors using the api, and we'll want some helpers and supporting infrastructure to make it all work, especially given differences on other platforms.
I'd probably suggest making the api something along the lines of:
You might also want a sleep function, but you really shouldn't call sleep in an event-driven application anyway; instead you'd want to set a timer event with a function. Windows has functions for this too which queue the async procedure call, just like the I/O notifications, and they can be simulated in other environments.
The tricky thing is integrating with other possible existing event loops. Of course, we could just try to provide a standard loop that works for everyone, but even on Windows, there's a difference between ui message pumps and io heavy worker threads, so it is hard to make one that works for everybody, and besides, we still have outside systems and legacy code to think about.
This is really the hard part. One idea is to have a function along the lines of:
Or perhaps there could be an interface that provides all the functions the system calls, and you provide an implementation of the loop itself.
But Windows might provide inspiration for this too. The way those asynchronous procedure calls work is actually fairly similar to Phobos' std.concurrency - it posts messages to the originator thread's mailbox, which are processed when that thread checks its messages. Indeed, we could even actually use std.concurrency itself for one implementation (though I'm meh on that, I'm not a fan of it for various reasons perhaps I'll write about some other day). And other ones just need to trigger an Event when something arrives.
But Event does have one problem: it works beautifully in Windows integrations, but on Posix, it is implemented with pthread_condition which, as far as I know at least, doesn't play well with functions like epoll. On Linux, it would be much easier to use an event_fd, but that is Linux-specific, so I don't know if there's other implementations. I think we might want to switch Event over to this though if there are other ways, since triggering a single file descriptor really does simplify this kind of integration work - you'd just listen for reads when it is triggered, then call the process function.
That's how I'd most likely want to do it.