Remix.run Logo
nromiun 3 days ago

After several years of dealing with concurrency I have come to the same conclusion.

Cooperative multitasking is not worth the pain of manually selecting where your IO hot spots are. It is too easy to block your entire program and not easy to debug where it is getting blocked.

IMO preemptive scheduling like Goroutines and Java virtual threads are the future. As a bonus they play well with parallelism too, because you are already doing all the synchronization.

Phil_Latio 2 days ago | parent [-]

What do you mean by "IO hot spots"? You really mean IO or rather CPU bound work? Because for IO, the standard library stuff could always yield properly (like Go does).

If you mean CPU-bound-like work, then that's true. But is the Go model really the solution? I don't know. It basically replicates the OS model, where preemption is required. But in a single application, the developer should be able to coordinate..? Maybe the cooperative model just needs more tooling, like clear rules: The main coroutine shouldn't execute for longer than 1 millisecond (or better a certain number of instructions) between yields. In debug mode, you could then get the exact stack traces where "stuttering" happens. You may then insert yield points or better just offload CPU-bound work by spawning a new coroutine on a different OS thread and await it cooperatively.

nromiun 2 days ago | parent [-]

Both basically. Another beauty of M:N green threads is that you don't need to differentiate between IO and CPU bound tasks.

If you add all those rules to coroutines you are halfway to preemptive scheduling already. And maybe async is not the answer if it still needs more work and tooling after all these years.

Phil_Latio 2 days ago | parent [-]

No i meant cooperative green threads, not the stackless async/await model. My model would basically mean: No "function coloring", all functions can be called as usual. IO related functions will automatically yield, no problem. All CPU-bound work either need manual yield points (not good, I agree) or should be offloaded to a coroutine on a different thread and then awaited (yes with await keyword) cooperatively. If you want to invoke a click handler for an UI, you can launch a coroutine on the same thread (cooperative).

Go must do all sorts of trickery with the preemption. Like inserting yield points or even depend on signals so it can preempt a goroutine which didn't hit a yield point. It basically replicates what the OS does with threads.

nromiun 2 days ago | parent | next [-]

So basically like gevent. I agree that is a very good concurrency model. Much better than the current asyncio mess we have.

But if you already have a runtime I don't know why it would be big deal to make them N:M threads as well. Makes managing CPU bound tasks easy as well as IO bound tasks.

Phil_Latio 2 days ago | parent [-]

Well I see 2 cases for automatic preemption:

- You are lazy and just don't care, let the runtime do it - Or you failed to realize that what you do could block

The first case is what annoys me. I think the developer should handle obvious cases manually. While the second case would be considered a bug in my model and the language should help you with that as explained earlier. If that works out, I mean to rule out mistakes for the second case, then this model is superior and more performant I think.

gpderetta 2 days ago | parent [-]

You could make the same argument for GC, yet, outside of system languages, it is generally considered a net positive. The reality is that in a large application it not easy to find all the right preemption points.

Phil_Latio 2 days ago | parent [-]

I guess you are right. Maybe I see it too much from a system programming language perspective. After all, Go is of a different kind.

2 days ago | parent | prev [-]
[deleted]