> I am not blocking or using event loops, that's the point. When I call read on a socket, the IO manager handles saving my context, doing the async read, yielding, and then restoring context and resuming when there's data to be read.
Doesn't this make it harder to reason about cases where multiple userland threads need to mutate the same state? You can't tell by inspection whether the function you're calling is going to yield somewhere deep inside.
With single threaded callback style you know your thread is the only thing that is executing and won't be interrupted. (Sure, other code can run between now and when your callback is invoked, but in practice I find that to be pretty easy to deal with.)
>Doesn't this make it harder to reason about cases where multiple userland threads need to mutate the same state?
Most languages make it very hard to reason about state in general. Haskell provides STM, so ensuring correct access to shared state is as simple as using an MVar. I absolutely agree that this is an important part of languages making concurrency a priority, they can't just add green threads and pretend that is good enough.
Correct me if I'm wrong but STM doesn't help you if your operations include side effects outside of STM (writing to disk, a database, the network)--exactly what async operations are usually used for. Aren't you back to locking at that point?
I don't understand the problem you're describing. If you're waiting on some external resource then you've yielded to another "thread" in either an event loop or green thread style. Are you suggesting partially modifying some state, then waiting on an external resource before finishing modifying the state? And no other "threads" can touch the state in the mean time? That is going to be a bad thing to do no matter what style you use.
Yes. My point is in the green (or native) thread style, it is not always obvious when you've stumbled into this situation (since you can't tell by looking at a function call whether it's going to yield at some point).
Comments
> I am not blocking or using event loops, that's the point. When I call read on a socket, the IO manager handles saving my context, doing the async read, yielding, and then restoring context and resuming when there's data to be read.
Doesn't this make it harder to reason about cases where multiple userland threads need to mutate the same state? You can't tell by inspection whether the function you're calling is going to yield somewhere deep inside.
With single threaded callback style you know your thread is the only thing that is executing and won't be interrupted. (Sure, other code can run between now and when your callback is invoked, but in practice I find that to be pretty easy to deal with.)
>Doesn't this make it harder to reason about cases where multiple userland threads need to mutate the same state?
Most languages make it very hard to reason about state in general. Haskell provides STM, so ensuring correct access to shared state is as simple as using an MVar. I absolutely agree that this is an important part of languages making concurrency a priority, they can't just add green threads and pretend that is good enough.
Correct me if I'm wrong but STM doesn't help you if your operations include side effects outside of STM (writing to disk, a database, the network)--exactly what async operations are usually used for. Aren't you back to locking at that point?
I don't understand the problem you're describing. If you're waiting on some external resource then you've yielded to another "thread" in either an event loop or green thread style. Are you suggesting partially modifying some state, then waiting on an external resource before finishing modifying the state? And no other "threads" can touch the state in the mean time? That is going to be a bad thing to do no matter what style you use.
Yes. My point is in the green (or native) thread style, it is not always obvious when you've stumbled into this situation (since you can't tell by looking at a function call whether it's going to yield at some point).