But in my world (embedded systems), those I/O writes aren't just writes to a file or a network socket. They're writes to device hardware, which has to be in the written-to state the next time that another thread interacts with it. That is, the I/O operation has to be part of the transaction, not queued up to run after the transaction commits.
Still, this approach goes farther than I thought possible to solve the problem...
That's the kind of thing Haskell excels at - explicitly sequencing things that need to happen before other things, so that you can have code that does stuff in the right order without getting into the trap of "can't ever refactor this in case I change the order something runs in".
(I can't believe that threads are actually part of the problem statement unless you're doing something strictly tied to C. Concurrency or even parallelism might be a requirement, but there are other ways to achieve it than OS-level shared-memory threads)
https://simonmar.github.io/pages/pcph.html
Of course that's not to say that Haskell is optimal for your work in embedded systems!
Though you'd be surprised at how much you can do while forgoing mutable state entirely.