1. https://openjdk.org/jeps/425
2. https://openjdk.org/jeps/428
3. https://medium.com/helidon/helidon-n%C3%ADma-helidon-on-virt...
1. https://openjdk.org/jeps/425
2. https://openjdk.org/jeps/428
3. https://medium.com/helidon/helidon-n%C3%ADma-helidon-on-virt...
The reason why it is a mistake is because it is essentially trying to solve the rpc problem once again even though it has been tried many times without success.
There just is a difference between making a synchronous call within your own OS thread vs. making such a call against anything else (the filesystem, the network, ...). Because you can assume that a synchronous call either succeeds and you can continue whatever you do. Or the whole thing crashes because e.g. the thread was killed due to some external effect or OOM error. But in that case, you have the guarantee that no more code on your thread is being run.
With Loom, it is not clear anymore if more code will be run based on your call or not, since there isn't an immediate difference between the two types of calls anymore (when looking at the code). This missing distinction is exactly what makes it easier to use but it also makes it very easy to use the "wrong default".
> The fundamental problem with taking a remote operation and wrapping it up so that it looks like a local operation is that the failure modes of local and remote operations are completely different.
> If that's not bad enough, the performance aspects are also completely different. A local operation that takes a few microseconds, when performed through an RPC, can suddenly take milliseconds.
http://armstrongonsoftware.blogspot.com/2008/05/road-we-didn...
> We argue that objects that interact in a distributed system need to be dealt with in ways that are intrinsically different from objects that interact in a single address space. These differences are required because distributed systems require that the programmer be aware of latency, have a different model of memory access, and take into account issues of concurrency and partial failure.
> We look at a number of distributed systems that have attempted to paper over the distinction between local and remote objects, and show that such systems fail to support basic requirements of robustness and reliability.
https://web.archive.org/web/20030310070307/http://research.s...
> The network is reliable.
[1] https://www.computing.dcu.ie/~ray/teaching/CA485/notes/falla...
The worst I anticipate coming from it is some hassles in production and a whole lot of time wasted futilely trying to explain why I don't like it to people who've had 1/10 as much time as me to become bitter and jaded from working in the profession.
There is: sync calls don't change. Async calls use the Future::fork call, all within your parent thread block scope.
Because otherwise your claim "sync calls don't change" is wrong - since this is how it currently works.
Basically, you don't have a thread context anymore, or you don't have a use for it anymore
No.
Currently, when I have code (not using futures or anything) and in the middle there is a Thread.sleep() then I know that this thread will be blocked until the sleep is over (or interrupted) and _no other code is being executed on this thread_.
So the question is: will this behaviour change? Because if it does, then Loom breaks existing code. And that's what it does. And it does it not by accident but on purpose, to retroactively improve performance of exactly those situations where OS threads are blocked.
First of all, the difference between threads is not nearly as severe as the difference between (remote) machines. There is an analogy to be made, but that's all it is - an analogy. Most applications don't care about nitty gritty thread-management performance.
Second, people said all that shit about network calls and yet... here we live in the age of distributed computing and it keeps getting more distributed. It's rapidly getting to the point where remote boundaries are something you consider as an optimization step. Most boring old business applications are "fast enough" even though the XyzService lives on another machine.
The whole reason for Loom is to improve performance of code that would otherwise be blocking. So what code is blocking? Exactly: code that waits for the OS (files, network, etc.). A call from one thread to the other isn't really a blocking call and that is not really what project Loom's main focus is about - at least as far as I can tell.
What I mean is that semantics of existing code changes (and becomes harder to understand for future code).
Erlang/Golang model of superlight "threads" exchanging messages appears to be much better at both being actually concurrent and making most code look decent and easy to see what is actually happening.
That's exactly what Loom is.
So far the reception has been very good and it works very well in practice. To me it's a pain now to work with languages that don't work like that. I'd argue it's Go's biggest advantage, really.
The Zig approach looks very good as well (If I understand it correctly, the underlying functions basically get compiled to whatever the caller wants them to be. Async or not. We'll see how that pans out in practice as the language gets more traction.).
You can think of it this way, to use terms from other languages: in Go, every function really returns a Future. But every function call has an `await` implicitly. Using the `go` keyword signals that you don't want that `await`. The call-site action doesn't change whether or not the function itself is asynchronous or not (it is).
However, if you use a mutex in Go, or wait on a channel, or try to write to a TCP connection, those calls won't block an OS thread. Additionally, if a goroutine goes on too long and others are waiting, it will be stopped and the OS thread will work on a different goroutine for a while.
I meant async in the technical underlying runtime sense of not blocking an OS thread when doing seemingly blocking operations and tried to describe it in an approachable way. Async in the sense of a python or rust function that is declared as async. If you think in terms of function colors (async and non-async) Go only has async ones.
The `await` and `go` bit are an intuition about how Go works, where waiting for the result right away is the default, while running concurrently needs to be called for.
It of course doesn't create needless goroutines on every function call.
That does not mean all Go calls are cancelable, they're not.
If, in a Scala/Python/Rust async function you use a synchronous stdlib function, that call will block the whole OS thread with it.
The following experiment will show you the difference:
In Java, run 10 futures on a threadpool of 4 workers, with each future doing a thread.Sleep(1s). That will finish after 3 seconds.
In Go, run 10 goroutines with GOMAXPROCS=4, with each doing time.Sleep(1s). That will finish after 1 second.
Functions need to be compiled in a special way and use IO in a very specific way (async io) in order for them to be properly runnable by fibers.
This specific way is commonly referred to as "async", or "nonblocking", and all functions in Go are that way, including all IO functions available in the stdlib.