Task scheduling guarantees on serial executors

consider the following snippet:

func enqueueWork() {
  Task { print("1") } // call this 'Task1'
  Task { print("2") } // call this 'Task2'
}

as i understand things, in general the execution order of Task1 and Task2 is arbitrary. if, say, this method were called when running on an actor using a concurrent executor, then the print statements could occur in any order, as the code within the two Task blocks may run concurrently.

however, if we restrict consideration to execution on the MainActor specifically (or more generally any actor that runs on a serial executor), are there any guarantees that execution order will match the Task declaration order? i.e. can you basically think of this as asynchronously enqueuing work on the main thread in a 'first-in first-out' manner, a la DispatchQueue.main.async or OperationQueue.main.addOperation[1]?

in testing this type of code, it experimentally seemed to be true (as long as the Tasks have the same priority), but it's unclear if this is incidental or a formal property of the concurrency model. i'm trying to hone my intuition regarding some of concurrency APIs, so please do correct any incorrect assertions i've made – thanks in advance!


  1. i realize the analogy to DispatchQueue and OperationQueue isn't perfect, but i'm curious to see exactly where it breaks down. ↩ī¸Ž

3 Likes

In a word, no. Actors are not serial queues and do not promise to obey any happens-before relationships that order Tasks that are enqueued on them.

1 Like

Actors are more like dispatch workloops than dispatch queues, for that reason.