I wasn't a huge fan of the "single thread for everything" paradigm, then I helped design a single threaded cooperatively multitasked embedded OS in C/C++.

No overhead from context switches. Everyone who is running knows how long they have to run, and knows that they are NOT going to get interrupted. No need to worry about locks or how to share data. Want to pass data to another module? Just pass it through well defined interfaces and you darn well know there will never be a read/write conflict, and the next time that module's code runs, it'll have access to that data. (No queue!)

The only exception, and what made it all possible, was the interrupt routines from hardware[1]. Anyone who subscribed to hardware events (entire OS was subscription based, no polling reads ever) had to implement a "thunk pattern" to put data into a receive buffer. The design pattern to do this was the same everywhere in all modules, making code understandable across the entire project.

It is an incredibly freeing paradigm to write in. It becomes so much easier to prove[2] the correctness of code when you can read all the code straight through and not have to ever worry about someone stomping on your data.

It wasn't an RTOS, but even so, it becomes really easy to start providing performance guarantees.

Internal builds had a watchdog timer[3] that would crash the device if it wasn't 'kicked' every so often. Set that to 3ms, start working with the code, and the stack traces tell you instantly who is over their CPU budget. Rewrite code and break it apart into multiple chunks that are scheduled for later execution, repeat until everyone is under their CPU allotment.

For many tasks, single threaded code is nice. Getting rid of preemption is even nicer.

Preemptive multithreading is a compromise. It means that no one thread/process can bring down the system by hogging 100% of resources, but it also creates a huge overhead where important work, work that makes for a better user experience, can (will!) get interrupted for work that honestly doesn't need to be done right now.

The solution to this is just throw so much CPU at the problem that everything gets done in a reasonable amount of time. It has often been noted that "reasonable amount of time" means systems today are less responsive than a 486 running DOS from 1992.

Of course it isn't reasonable to have a modern cooperatively multithreaded consumer OS, no way would the hundreds of processes ran at any one time all cooperate with each other.

But if you ever get a chance to write code on a single threaded cooperative system, go do it. It is a lot of fun.

Now all this meant that going from embedded C to NodeJS wasn't that large of a mental leap! Not having to directly read bytes off the wire was weird (seriously, took a bit of getting use to), but it turns out that "get data, do work, schedule what needs to be done, return early" ends up being the same paradigm at both the top and the bottom of the programming stacks!

[1] All I/O was done using DMA engines, basically a fancy limited programmable piece of hardware that can read and write to all the different pieces of HW hanging off of the main chip, so for example as Bluetooth packets come in, the DMA engine shoves the packets into a buffer and when the buffer is full it raises an interrupt that lets the CPU know that data is waiting. It looks almost exactly like async I/O in any of the modern programming languages, except you have direct access to all that IO being hardware offloaded. Writing to the bare metal rocks.

[2] For a reasonable enough degree of "prove" that software is reliable and doesn't crash from threading issues

[3] A watchdog timer is a physical timer hooked across the power lines of your chip. If it isn't activated every so often (in the industry this is called "kicking the watchdog") it will, in debug builds it does a controlled crash of the CPU, and in retail builds it will reset the entire system. If you've ever had an embedded device reset itself before your very eyes, it is becomes the CPU got locked up and no one kicked the watchdog, so the entire system emergency reset itself.