Ah, of course that one is reentrant, but I meant this differently:
I think that a lot of people who are only familiar with dispatch queues would interpret the explanations about actors, isolation, and the "messages" such that isolated async functions are "split" into "messages" and enqueued when the method is called.
So this:
func work() async {
print("First print: \(state)")
await doSomeLongWork()
print("Second print: \(state)")
}
would become this:
func work() {
// as the "mailbox" processes messages sequentially an actor would have a serial queue:
theActorsSerialQueue.async {
print("First print: \(state)")
doSomeLongWork()
}
theActorsSerialQueue.async {
print("Second print: \(state)")
}
}
Of course work
is technically still reentrant, but since both "messages" it was "split into" have already been queued onto a serial queue there's no way the second call's first part can be executed before the first call's second part is done.
I think (?) a better way to think of the actor isolation and async/await mechanic if you want to mentally "wrap" it into queue-like code would be this (which is exactly what you wrote, but again, my hunch is that people don't think it actors and the way await is implemented works):
func work() {
theActorsSerialQueue.async {
print("First print: \(state)")
doSomeLongWorkNowWithCallback() {
theActorsSerialQueue.async {
print("Second print: \(state)")
}
}
}
This way, reentrant calls to work
can be processed before the second "message" is queued onto the actor's serial isolation queue.
But my hunch is that on first read people think it would do it in the first way.
"Re-entrant" in this context then is less of a way to express that you can call any function repeatedly to schedule it several times, but more that the actor's scheduling "queue" is able to "re-enter" at various points (if you "schedule" async functions, it gets more "re-entry-opportunities" than just at the function call itself).