8

What is the expected thread on which a .collect operation in Swift's Combine will emit?

Specifically I am seeing this code crash in the second precondition, but not the first:

return Publishers.MergeMany(publishers)
    .handleEvents(receiveOutput: { _ in
        precondition(Thread.isMainThread) // <- This is fine
    })
    .collect()
    .handleEvents(receiveOutput: { _ in
        precondition(Thread.isMainThread) // <- Crash is here
    })

It is as if the .collect operation is picking a different thread to use, even though the final publisher in the MergeMany must have emitted on the main thread. I have deduced this behavior via crash logs uploaded from Firebase. Can anyone explain this observed behavior?

esilver
  • 27,713
  • 23
  • 122
  • 168

1 Answers1

1

For your check, try using dispatchPrecondition(condition: .onQueue(.main)). This is the Swift equivalent to calling dispatch_assert_queue. If you read the documentation there you will see:

Passing the result of dispatch_get_main_queue() to this function verifies that the current block was submitted to the main queue, or to a queue targeting it, or is running on the main thread (in any context).

note that it says "or is running on the main thread"

Grand Central Dispatch can execute code on a queue that targets the main queue on a thread that is not the main thread. So your precondition check explicitly comparing the current thread to the main thread may be invalid even though the code is running in a context that is, for the moment at least, the "main" execution context.

If you look at the header documentation for dispatch_get_current_queue you'll find:

When dispatch_get_current_queue() is called on the main thread, it may or may not return the same value as dispatch_get_main_queue(). Comparing the two is not a valid way to test whether code is executing on the main thread (see dispatch_assert_queue() and dispatch_assert_queue_not()).

In your case I suspect the collect events are running on a queue that targets the main queue, and the code is not running on the main thread, but the OS is ensuring your code is not running in parallel with the main thread so there is no chance of a race condition.

Scott Thompson
  • 22,629
  • 4
  • 32
  • 34