News for hackers. Stuff that matters.
Re:State of Loom by albertzeyer on Saturday May 16, 2020 @09:45AM (#23203321)
Can you maybe elaborate why this model is better than async/await? I have to admit, I don't really know Loom, but from a first glance, it looks like this is just (green) threads, and async/await is anyway an orthogonal concept to this, or not? But maybe I am confusing things here.

Can you maybe recommend a good overview over all the current concurrent approaches and concepts, like async/await, and alternatives, and their advantages and problems? I would love to get a more recent overview on this.

I had the impression that most recent languages follow on the async/await concept. At least JS and Python. Maybe also Rust? Go? Erlang? And there are probably libraries for C++ to do the same, or maybe it is already integrated? I have to admit that I did not fully follow all this development.

Re:State of Loom by GolDDranks on Saturday May 16, 2020 @10:02AM (#23203438)
Async/await split up your functions at their "blocking points" to fragments that can be run fragment-by-fragment, in an interleaved way by an executor / event loop, thus enabling high level of concurrency within a single OS thread.

The problem is that your split-up async function ceases to be a normal function. It doesn't use the stack in a similar way than normal functions. Furthermore, the executor / event loop is required for running those async functions.

So you can't call async functions from normal functions, because they aren't. You need to create an event loop and then hand the task over for it to process it. Your world splits up into sync and async functions.

Go and Erlang are going with the similar "lightweight thread" approach as Java's project Loom. On the other hand, JS, Python and C# have gone with async. I'm not sure if these languages actually need async, going with something similar as project Loom would have also fit, and would have been simpler from the user perspective, perhaps? But hindsight is 20/20.

Rust has also gone async, and I'd argue it's the only one of the pack who really have async/await as a necessity - this is because of two design requirements that differ from Java, C#, Python and JS: 1) must have native-level performance when calling foreign code that expects a C-like stack. Something like Loom doesn't provide that. 2) must not have an implicit default runtime.

Re:State of Loom by yrio on Saturday May 16, 2020 @08:03PM (#23208242)
Python is popular in data science, AI & those field depends on a lot of native libraries: NumPy, SciPy, etc. Maybe that's why they chose the async await model
Re:State of Loom by GolDDranks on Sunday May 17, 2020 @06:14AM (#23210972)
Yeah, sounds plausible. A lot of Python stuff is actually wrapped C / C++ / Fortran code, so it makes sense to optimize for FFI.
Re:State of Loom by int_19h on Saturday May 16, 2020 @10:05PM (#23208852)
Those other languages do async because they're less insular, and want to interoperate with the world outside of their own runtime.
Re:State of Loom by sk5t on Saturday May 16, 2020 @09:32PM (#23208692)
> The problem is that your split-up async function ceases to be a normal function. It doesn't use the stack in a similar way

Have you taken the CLR (a.k.a. .NET / C#) async await for a spin? Microsoft's done a great job of squaring away those details so the stack/thread discontinuity doesn't much matter.

Re:State of Loom by pjmlp on Sunday May 17, 2020 @02:37AM (#23210063)
It kind of does, specially if you don't take care with ConfigureAwait().
Re:State of Loom by ceronman on Saturday May 16, 2020 @10:42AM (#23203759)
As others have pointed out, the big problem of async/await is that it splits the world in two kinds of functions that can't call each other.

For example, in Python you have this http client library called `requests`. It's very easy to use, but it is synchronous and you can't use it in when doing async/await. Instead you have to use a similar library called aiohttp. And it's like that for every single library or function doing any sort of IO, you need two versions of everything. Same thing happens in Node.js. You have `spawn` which is asynchronous and `spawnSync` which is synchronous. And so on.

And it doesn't stop with functions, this division goes across the language it self. For example in Python you have a `for` loop and an `async for` loop. Same in JS, you have `for` and `for await`. C# also has it's own `await forech` construct. It's like splitting the world for no reason.

The great advantage of Loom is that you don't need a parallel language, you can use the same http clients, database clients, filesystem libraries that you were using in a synchronous world, but now you will be able to use concurrently in millions of virtual threads.

As the article mentions, the whole reason why async/await was invented, is because doing concurrency with kernel threads is very expensive. They are slow to create and they use a lot of memory. Using Promises and an event loop you can run multiple concurrent operations in a single OS thread. This gives you the chance to achieve much more concurrency than running one operation per OS thread. The problem is that this changes the way you program a lot. Your code is not sequential anymore, your error handling is different, even some conditional operations are different. Async/await is just some syntactic sugar created to make this looks like the regular programming model. It makes the function calls look sequentials (by adding an `await` prefix), it makes the errors look like exceptions, it makes the for loops look like regular for loops. But this is just appearance. It's something that looks similar, but it's different, and that's why you can mix it with regular code.

Loom model is closer to Go and Erlang.

Re:State of Loom by Skinney on Saturday May 16, 2020 @09:53AM (#23203378)
Project Loom provides the same benefit of async/await, but without changing the syntax in any way. In C# you now have a split API: blocking api's which rely on threads for concurrency, and async api's which require you to use async/await syntax. These api's are not compatible, so to take advantage of async/await, you have to refactor your code.

When project loom is implemented in Java, all code which makes use of threads today automatically gains all the benefits of the async/await model, without any refactoring.

Go and Erlang doesn't do the async/await thing. They only have one notion of concurrency (goroutines in Go, processes in Erlang). They don't require you to use async and await constructs, but they work similarly to async/await under the hood.

Re:State of Loom by hn_throwaway_99 on Saturday May 16, 2020 @10:32AM (#23203664)
Couple specific examples you may be familiar with:

Coming from Java to JavaScript, I really, really yearn for the equivalent of ThreadLocals. For example, most Java logging libraries let you set a "context" object that can be used for logging a request ID or user ID anywhere down the call stack. You can't do that in Node, so instead you have to explicitly pass that context object down in all your function calls, or make use of brittle, complicated, hacky, not-fully-supported features like "continuation local storage".

Debugging stack traces in an async/await world is a mess because you don't get a nice "from the start of the request down to this call" view of the stack.

Perhaps most importantly, with Node at least you need to worry about starving your main event loop because there is only a single top-level main thread (but understand that's not inherent to the async/await issue in general).

Re:State of Loom by sk5t on Saturday May 16, 2020 @09:43PM (#23208749)
> Coming from Java to JavaScript, I really, really yearn for the equivalent of ThreadLocals

While I understand why you feel that way today, you may find yourself some point in the future deeply, deeply detesting ThreadLocal. Sure it seems all convenient but all hell breaks loose when you need to use a threadpool, a future, an actor model...

Re:State of Loom by int_19h on Saturday May 16, 2020 @10:07PM (#23208868)
This a Node limitation, though. E.g. .NET has had asynchronous call contexts, which encapsulate stuff such as "async thread locals", for a very long time.

Similarly, for the stacks, it's largely a tooling issue - if the debugger understands async, and the framework retains enough information about the origin of the promise, it can re-create the original stack trace, and combine it with the current one - .NET debuggers in VS and VSCode do this, as well, and WinRT even does this for C++.

Re:State of Loom by moogly on Sunday May 17, 2020 @07:40AM (#23211292)
Re:State of Loom by closeparen on Saturday May 16, 2020 @12:20PM (#23204745)
Code that runs in goroutines looks no different from code that runs outside goroutines. You do regular blocking IO. The application code of a performant RPC / HTTP server never needs to refer to any kind of concurrency construct, unless it wants to make outbound calls in parallel. (Your HTTP or RPC library takes care of dispatching inbound requests onto goroutines).

As much as I want a more expressive language, all the explicit async/await, futures, callbacks, etc. junk needed just for your API handler to cooperate with concurrent requests (not even do its own parallel processing) keeps on sending me back to Go. I look forward to being able to write Java in the same style.

Re:State of Loom by balfirevic on Saturday May 16, 2020 @11:11AM (#23204011)
> Can you maybe elaborate why this model is better than async/await?

Others have given actual explanations, but I'll chime in with a bit of a tongue-in-cheek one: it's better than async/await because it doesn't have async/await (yet has all the benefits provided by them).

Re:State of Loom by chrisweekly on Saturday May 16, 2020 @07:58PM (#23208223)
I wish I understood this better. If you're writing code in the same style whether it's async or not, how can you manage return values and reason about flow?
Re:State of Loom by balfirevic on Saturday May 16, 2020 @08:18PM (#23208306)
The flow is sequential, as laid out in the code, the same as for any old-fashioned code. It has the same semantics as the non-async code. The only difference is that OS thread can be released to do some other work while you wait for IO operation to finish.