-
Notifications
You must be signed in to change notification settings - Fork 220
Swift 6: complete concurrency checking (LLC and UIKit) #3661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
# Conflicts: # Sources/StreamChat/WebSocketClient/BackgroundTaskScheduler.swift
# Conflicts: # Sources/StreamChat/Models/CurrentUser.swift # Sources/StreamChat/Workers/CurrentUserUpdater.swift
Generated by 🚫 Danger |
if: ${{ github.event.inputs.record_snapshots != 'true' }} | ||
# if: ${{ github.event.inputs.record_snapshots != 'true' }} | ||
if: false # disable Xcode 15 builds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabled Xcode 15 builds (it is hard to support it when complete concurrency checking is enabled). Hard means avoiding compiler crashes.
SDK Size
|
@@ -26,7 +26,7 @@ final class StreamAudioWaveformAnalyser: AudioAnalysing { | |||
private let audioSamplesExtractor: AudioSamplesExtractor | |||
private let audioSamplesProcessor: AudioSamplesProcessor | |||
private let audioSamplesPercentageNormaliser: AudioValuePercentageNormaliser | |||
private let outputSettings: [String: Any] | |||
nonisolated(unsafe) private let outputSettings: [String: Any] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nonisolated(unsafe)
because value is Any
SDK Performance
|
|
||
import Foundation | ||
|
||
/// Erase type for structs which recursively contain themselves. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is easier to use a type instead of a closure when dealing with Sendable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this is a nice approach
@@ -131,15 +131,19 @@ open class StreamAudioPlayer: AudioPlaying, AppStateObserverDelegate { | |||
open func play() { | |||
do { | |||
try audioSessionConfigurator.activatePlaybackSession() | |||
player.play() | |||
MainActor.ensureIsolated { | |||
player.play() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that AVPlayer and some asset related methods require main actor
|
||
// MARK: - | ||
|
||
private static let queue = DispatchQueue(label: "io.getstream.stream-runtime-check", target: .global()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative is to skip using queue here and making everything nonisolated(unsafe), especially because these are internal runtime checkes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would do that without a queue here actually
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good in general, left some comments to discuss
|
||
// MARK: - | ||
|
||
private static let queue = DispatchQueue(label: "io.getstream.stream-runtime-check", target: .global()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would do that without a queue here actually
static var maxAttachmentSize: Int64 { 100 * 1024 * 1024 } | ||
|
||
private let decoder: RequestDecoder | ||
private let encoder: RequestEncoder | ||
private let session: URLSession | ||
/// Keeps track of uploading tasks progress | ||
@Atomic private var taskProgressObservers: [Int: NSKeyValueObservation] = [:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did it complain about Atomic?
@@ -131,15 +131,19 @@ open class StreamAudioPlayer: AudioPlaying, AppStateObserverDelegate { | |||
open func play() { | |||
do { | |||
try audioSessionConfigurator.activatePlaybackSession() | |||
player.play() | |||
MainActor.ensureIsolated { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mentioned it on the other PR as well - why do we need to use ensureIsolated
?
@@ -50,6 +50,8 @@ public class EventsController: Controller, DelegateCallable { | |||
get { multicastDelegate.mainDelegate } | |||
set { multicastDelegate.set(mainDelegate: newValue) } | |||
} | |||
|
|||
private let queue = DispatchQueue(label: "io.getstream.events-controller", target: .global()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is this used?
} | ||
} | ||
|
||
func writeConverting<T>(_ actions: @escaping @Sendable(DatabaseSession) throws -> T) async throws -> T where T: Sendable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need this?
import os | ||
|
||
@available(iOS, introduced: 13.0, deprecated: 16.0, message: "Use OSAllocatedUnfairLock instead") | ||
final class AllocatedUnfairLock<State>: @unchecked Sendable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where do we use an unfair lock?
|
||
import Foundation | ||
|
||
/// Erase type for structs which recursively contain themselves. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this is a nice approach
try action() | ||
} else { | ||
try DispatchQueue.main.sync { | ||
return try MainActor.assumeIsolated { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this needed?
_endIndex = { baseCollection.endIndex } | ||
_position = { baseCollection[$0] } | ||
_startIndex = { baseCollection.startIndex } | ||
self.baseCollection = Array(baseCollection) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we change this?
pongTimeoutTimer = timerType.schedule(timeInterval: Self.pongTimeoutTimeInterval, queue: timerQueue) { [weak self] in | ||
log.info("WebSocket Pong timeout. Reconnect") | ||
self?.delegate?.disconnectOnNoPongReceived() | ||
queue.sync { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this needed?
Important
StreamChatUI changes are in #3660 and will be merged to here. This PR can be reviewed already.
🔗 Issue Links
Resolves IOS-735
🎯 Goal
📝 Summary
Sendable
conformance to types@Sendable
to completion handlers@unchecked Sendable
types (internal classes have manual handling and found many which did not guard that state properly)@Sendable
to completion handlers creates warnings at callsites)🛠 Implementation
🧪 Manual Testing Notes
Manual regression testing round when UIKit changes are merged into this branch.
☑️ Contributor Checklist
docs-content
repo