Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,13 @@ extension NotificationCenter {
}
})
#else
return ObservationToken(center: self, token: _addObserver(Message.name, object: subject, using: observer))
nonisolated(unsafe) let observer = observer
return ObservationToken(center: self, token: _addObserver(Message.name, object: subject) { (message: Message) in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncertain about the tradeoff of introducing the runtime cost of a closure to silence a false positive concurrency warning. (We know @MainActor closures are called via @MainActor post()s, so we're not actually sending or losing main actor as the warnings imply, but the compiler doesn't know that.)

Two other options come to mind:

  1. Use unsafeBitCast to simply change the signature of the closure, e.g. let unsafeObserver = unsafeBitCast(observer, to: (@Sendable (Message) -> Void).self), then pass unsafeObserver instead of observer. I think this is safe because we're only modifying annotations with no runtime representation.
  2. A cleaner solution might be for the internal registrar that _addObserver() ultimately works with to take an enum of the two possible closure types instead. That, though, has a little bit of a memory cost that could become substantial in extreme cases where NotificationCenter has 10,000+ observers, though I don't think that's the typical case.

nonisolated(unsafe) let message = message
MainActor.assumeIsolated {
observer(message)
}
})
#endif
}

Expand Down