Improve processes sending messages to themselves
Processes sending messages to themselves is a bit wonky at the moment:
If you perform a synchronous send, the message does run but doesn't resume the process properly. Basically any code after the send in the "parent" message doesn't run:
import std::stdio::STDOUT
class async Main {
fn async main {
STDOUT.new.print('before bla')
bla
STDOUT.new.print('after bla') # <- This never runs, but we do stop
}
fn async bla {
STDOUT.new.print('testing')
}
}
If we perform an async
call and await it, the process deadlocks:
import std::stdio::STDOUT
class async Main {
fn async main {
STDOUT.new.print('before bla')
async { self.bla }.await # <- We'll hang here indefinitely
STDOUT.new.print('after bla')
}
fn async bla {
STDOUT.new.print('testing')
}
}
Detecting this at compile-time is tricky, because given an instance of a process we don't know if it's the same instance as the surrounding type (= the receiver of the current message).
I can think of the following solutions:
- If we detect a process sending a message to itself, we panic
- If we detect a process sending a message to itself, we automatically (in the VM) turn it into a regular virtual call. For
async
calls we'd have to somehow populate the future when returning, which may prove tricky - The compiler generates the necessary code for this. This gives the compiler full control, but I suspect the runtime cost is greater compared to just doing this in the VM
- If a process sends a synchronous message to itself, we turn it into a virtual call. If we use an asynchronous call, we allow it. If a process tries to resolve a future that isn't resolved yet and would be populated by the process itself, we panic
Option one is the easiest to implement, but can make certain patterns more annoying. For example, within a process an async
method can't reuse another async
method for the same receiver, as this requires sending a message to itself. This may lead to async
methods simply calling regular methods, just so you can reuse the code in another async
method.
Option two would be nice to have, but I suspect this will prove challenging when performing async
calls.
Option four seems the most reasonable, especially considering it's likely rare to A) perform an async call to the process itself B) try to resolve it within the same message.