Concurrency

RSS for tag

Concurrency is the notion of multiple things happening at the same time.

Posts under Concurrency tag

102 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Concurrency Resources
Swift Concurrency Resources: DevForums tags: Concurrency The Swift Programming Language Concurrency > Concurrency documentation WWDC 2022 Session 110351 Eliminate data races using Swift Concurrency — This ‘sailing on the sea of concurrency’ talk is a great introduction to the fundamentals. WWDC 2021 Session 10134 Explore structured concurrency in Swift — The table that starts rolling out at around 25:45 is really helpful. Dispatch Resources: DevForums tags: Dispatch Dispatch documentation — Note that the Swift API and C API, while generally aligned, are different in many details. Make sure you select the right language at the top of the page. Dispatch man pages — While the standard Dispatch documentation is good, you can still find some great tidbits in the man pages. See Reading UNIX Manual Pages. Start by reading dispatch in section 3. WWDC 2015 Session 718 Building Responsive and Efficient Apps with GCD [1] WWDC 2017 Session 706 Modernizing Grand Central Dispatch Usage [1] Avoid Dispatch Global Concurrent Queues DevForums post Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] These videos may or may not be available from Apple. If not, the URL should help you locate other sources of this info.
0
0
655
Nov ’23
How to resolve SwiftUI.DynamicProperty on MainActor compiler warning on 6.0?
Hi! I'm running into a warning from a SwiftUI.DynamicProperty on a 6.0 development build (swift-6.0-DEVELOPMENT-SNAPSHOT-2024-03-26-a). I am attempting to build a type (conforming to DynamicProperty) that should also be MainActor. This type with also need a custom update function. Here is a simple custom wrapper (handwaving over the orthogonal missing pieces) that shows the warning: import SwiftUI @MainActor struct MainProperty: DynamicProperty { // Main actor-isolated instance method 'update()' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode @MainActor func update() { } } Is there anything I can do about that warning? Does the warning correctly imply that this will be a legit compiler error when 6.0 ships? I can find (at least) two examples of types adopting DynamicProperty from Apple that are also MainActor: FetchRequest and SectionedFetchRequest. What is confusing is that both FetchRequest^1 and SectionedFetchRequest^2 explicitly declare their update method to be MainActor. Is there anything missing from my Wrapper declaration that can get me what I'm looking for? Any more advice about that? Thanks!
3
0
398
4h
Multiple async lets crash the app
Usage of multiple async lets crashes the app in a nondeterministic fashion. We are experiencing this crash in production, but it is rare. 0 libswift_Concurrency.dylib 0x20a8b89b4 swift_task_create_commonImpl(unsigned long, swift::TaskOptionRecord*, swift::TargetMetadata<swift::InProcess> const*, void (swift::AsyncContext* swift_async_context) swiftasynccall*, void*, unsigned long) + 384 1 libswift_Concurrency.dylib 0x20a8b6970 swift_asyncLet_begin + 36 We managed to isolate the issue, and we submitted a technical incident (Case-ID: 8007727). However, we were completely ignored, and referred to the developer forums. To reproduce the bug you need to run the code on a physical device and under instruments (we used swift concurrency). This bug is present on iOS 17 and 18, Xcode 15.1, 15.4 and 16 beta, swift 5 and 6, including strict concurrency. Here's the code for Swift 6 / Xcode 16 / strict concurrency: (I wanted to attach the project but for some reason I am unable to) typealias VoidHandler = () -> Void enum Fetching { case inProgress, idle } protocol PersonProviding: Sendable { func getPerson() async throws -> Person } actor PersonProvider: PersonProviding { func getPerson() async throws -> Person { async let first = getFirstName() async let last = getLastName() async let age = getAge() async let role = getRole() return try await Person(firstName: first, lastName: last, age: age, familyMemberRole: role) } private func getFirstName() async throws -> String { try await Task.sleep(nanoseconds: 1_000_000_000) return ["John", "Kate", "Alex"].randomElement()! } private func getLastName() async throws -> String { try await Task.sleep(nanoseconds: 1_400_000_000) return ["Kowalski", "McMurphy", "Grimm"].randomElement()! } private func getAge() async throws -> Int { try await Task.sleep(nanoseconds: 2_100_000_000) return [56, 24, 11].randomElement()! } private func getRole() async throws -> Person.Role { try await Task.sleep(nanoseconds: 500_000_000) return Person.Role.allCases.randomElement()! } } @MainActor final class ViewModel { private let provider: PersonProviding = PersonProvider() private var fetchingTask: Task<Void, Never>? let onFetchingChanged: (Fetching) -> Void let onPersonFetched: (Person) -> Void init(onFetchingChanged: @escaping (Fetching) -> Void, onPersonFetched: @escaping (Person) -> Void) { self.onFetchingChanged = onFetchingChanged self.onPersonFetched = onPersonFetched } func fetchData() { fetchingTask?.cancel() fetchingTask = Task { do { onFetchingChanged(.inProgress) let person = try await provider.getPerson() guard !Task.isCancelled else { return } onPersonFetched(person) onFetchingChanged(.idle) } catch { print(error) } } } } struct Person { enum Role: String, CaseIterable { case mum, dad, brother, sister } let firstName: String let lastName: String let age: Int let familyMemberRole: Role init(firstName: String, lastName: String, age: Int, familyMemberRole: Person.Role) { self.firstName = firstName self.lastName = lastName self.age = age self.familyMemberRole = familyMemberRole } } import UIKit class ViewController: UIViewController { @IBOutlet private var first: UILabel! @IBOutlet private var last: UILabel! @IBOutlet private var age: UILabel! @IBOutlet private var role: UILabel! @IBOutlet private var spinner: UIActivityIndicatorView! private lazy var viewModel = ViewModel(onFetchingChanged: { [weak self] state in switch state { case .idle: self?.spinner.stopAnimating() case .inProgress: self?.spinner.startAnimating() } }, onPersonFetched: { [weak self] person in guard let self else { return } first.text = person.firstName last.text = person.lastName age.text = "\(person.age)" role.text = person.familyMemberRole.rawValue }) @IBAction private func onTap() { viewModel.fetchData() } }
1
0
89
2d
SwiftData Update Item View from Background Thread
I have a background thread that is updating a swift data model Item using a ModelActor. The background thread runs processing an Item and updates the Item's status field. I notice that if I have a view like struct ItemListView: View { @Query private var items: [Items] var body: some View { VStack { ForEach(items) { item in ItemDetailView(item) } } } } struct ItemDetailView: View { var item: Item var body: some View { // expected: item.status automatically updates when the background thread updates the `Item`'s `status`. Text(item.status) // actual: This text never changes } } Then background updates to the Item's status in SwiftData does not reflect in the ItemDetailView. However, if I inline ItemDetailView in ItemListView like this: struct ItemListView: View { @Query private var items: [Items] var body: some View { VStack { ForEach(items) { item in // Put the contents of ItemDetailView directly in ItemListView Text(item.status) // result: item.status correctly updates when the background thread updates the item. } } } } Then the item's status text updates in the UI as expected. I suspect ItemDetailView does not properly update the UI because it just takes an Item as an input. ItemDetailView would need additional understanding of SwiftData, such as a ModelContext. Is there a way I can use ItemDetailView to show the Item's status and have the UI show the status as updated in the background thread? In case details about my background thread helps solve the problem, my thread is invoked from another view's controller like @Observable class ItemCreateController { func queueProcessingTask() { Task { let itemActor = ItemActor(modelContainer: modelContainer) await itemActor.setItem(item) await itemActor.process() } } } @ModelActor actor ItemActor { var item: Item? func setItem(_ item: Item) { self.item = modelContext.model(for: item.id) as? Item } func process() async { // task that runs processing on the Item and updates the Item's status as it goes. }
4
1
284
2d
Swift Async/Await, how to bring asynchronously calculated results back to main thread
I'm relatively new to Swift, and very new to concurrency via Async/Await, so please be patient. 😀 I'm having a hard time comprehending how to do complex operations asynchronously in background threads, and then in turn bring the results back to the main thread. I'm getting various errors along the lines of "Mutation of captured var 'personName' in concurrently-executing". I've paired the issue down as simply as possible as follows, and you'll see where the compiler gives the error message. I'd appreciate any advice on how to evolve my mental model to make this work. Thanks! Bruce import Foundation actor Person {     var myName = "Thomas Jefferson"     var name: String {         get {             return myName         }     } } func main() {     let person = Person()     var personName: String     print("start")     let nameTask = Task {         return await person.name     }     Task {         do {             personName = try await nameTask.result.get()             // Error: Mutation of captured var 'personName' in concurrently-executing code         } catch {             print("error!!!")         }     }     print("The person's name is \(personName)") } RunLoop.main.run() main()
4
0
3.1k
5d
How to migrate PHPhotoLibraryChangeObserver to Swift 6?
I have the following code: extension AssetGridViewController: PHPhotoLibraryChangeObserver { func photoLibraryDidChange(_ changeInstance: PHChange) { Task { @MainActor in guard let changes = changeInstance.changeDetails(for: fetchResult) else { return } fetchResult = changes.fetchResultAfterChanges } } } With Swift 6, this generates a compilation error: Main actor-isolated instance method 'photoLibraryDidChange' cannot be used to satisfy nonisolated protocol requirement. The error includes to fix-it suggestions: Adding nonisolated to the function (nonisolated func photoLibraryDidChange(_ changeInstance: PHChange)) Adding @preconcurrency to the protocol conformance (extension AssetGridViewController: @preconcurrency PHPhotoLibraryChangeObserver {) Both options generate a runtime error: EXC_BREAKPOINT (code=1, subcode=0x105b7c400). For context, AssetGridViewController is a regular UIViewController. Any ideas on how to fix this?
2
0
176
5d
Cast Any to Sendable
I'm continuing with the migration towards Swift 6. Within one of our libraries, I want to check whether a parameter object: Any? confirms to Sendable. I tried the most obvious one: if let sendable = object as? Sendable { } But that results into the compiler error "Marker protocol 'Sendable' cannot be used in a conditional cast". Is there an other way to do this?
1
0
139
5d
Concurrency Crash - PushToTalk Framework
With the integration of Apple's pushToTalk framework - we create the PTChannelManager using its async initializer from AppDidFinishLaunching - using an actor to ensure the PTChannelManager is only created once. With this we have been seeing a lot of crashes for users in our analytics dashboards happening about ~2 seconds after app launch around a task-dealloc. Here is a simplified version of our actor and Manager - where the manager just shows the init. The init of it is an async optional init because the creation of the PTChannelManager uses an async throws. actor PushToTalkDeviceContainer { private var internalPushToTalkManagerTask: Task<PushToTalkManager?, Never>? func pushToTalkManager() async -> PushToTalkManager? { #if !os(visionOS) if let internalPushToTalkManagerTask { return await internalPushToTalkManagerTask.value } let internalPushToTalkManagerTask = Task<PushToTalkManager?, Never> { return await PushToTalkManagerImp() } self.internalPushToTalkManagerTask = internalPushToTalkManagerTask return await internalPushToTalkManagerTask.value #else return nil #endif } } public class PushToTalkManagerImp: PushToTalkManager { public let onPushToTalkDelegationEvent: AnyPublisher<PushToTalkDelegationEvent, Never> public let onPushToTalkAudioSessionChange: AnyPublisher<PushToTalkManagerAudioSessionChange, Never> public let onChannelRestoration: AnyPublisher<UUID, Never> private let ptChannelManager: PTChannelManager private let restorationDelegate: PushToTalkRestorationDelegate private let delegate: PushToTalkDelegate init?() async { self.delegate = PushToTalkDelegate() self.restorationDelegate = PushToTalkRestorationDelegate() self.onPushToTalkDelegationEvent = delegate.pushToTalkDelegationSubject.eraseToAnyPublisher() self.onPushToTalkAudioSessionChange = delegate.audioSessionSubject.eraseToAnyPublisher() self.onChannelRestoration = restorationDelegate.restorationDelegateSubject.eraseToAnyPublisher() do { ptChannelManager = try await PTChannelManager.channelManager(delegate: delegate, restorationDelegate: restorationDelegate) } catch { return nil } } } The crash stack trace is as follows: 0 libsystem_kernel.dylib 0x00000001e903342c __pthread_kill + 8 (:-1) 1 libsystem_pthread.dylib 0x00000001fcdd2c0c pthread_kill + 268 (pthread.c:1721) 2 libsystem_c.dylib 0x00000001a7ed6c34 __abort + 136 (abort.c:159) 3 libsystem_c.dylib 0x00000001a7ed6bac abort + 192 (abort.c:126) 4 libswift_Concurrency.dylib 0x00000001ab2bf7c8 swift::swift_Concurrency_fatalErrorv(unsigned int, char const*, char*) + 32 (Error.cpp:25) 5 libswift_Concurrency.dylib 0x00000001ab2bf7e8 swift::swift_Concurrency_fatalError(unsigned int, char const*, ...) + 32 (Error.cpp:35) 6 libswift_Concurrency.dylib 0x00000001ab2c39a8 swift_task_dealloc + 128 (TaskAlloc.cpp:59) 7 MyApp 0x0000000104908e04 PushToTalkManagerImp.__allocating_init() + 40 (PushToTalkManager.swift:0) 8 MyApp 0x0000000104908e04 closure #1 in PushToTalkDeviceContainer.pushToTalkManager() + 60 9 MyApp 0x00000001041882e9 specialized thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) + 1 (<compiler-generated>:0) 10 MyApp 0x0000000103a652bd partial apply for specialized thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) + 1 (<compiler-generated>:0) 11 libswift_Concurrency.dylib 0x00000001ab2c2775 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1 (Task.cpp:463)
3
0
208
1w
Using Core Data with the Swift 6 language mode
I'm starting to work on updating my code for Swift 6. I have a number of pieces of code that look like this: private func updateModel() async throws { try await context.perform { [weak self] in // do some work } } After turning on strict concurrency checking, I get warnings on blocks like that saying "Sending 'self.context' risks causing data races; this is an error in the Swift 6 language mode." What's the best way for me to update this Core Data code to work with Swift 6?
4
1
437
2w
TipKit vs. Swift 6 + Concurrency
I'm trying to convert my project to use Swift 6 with Complete Concurrency in Xcode 16 beta 1. The project uses TipKit, but I'm getting compile errors when trying to use the TipKit Parameters feature. Here is an example of the type of error I'm seeing (Note that this code from https://developer.apple.com/documentation/tipkit/highlightingappfeatureswithtipkit): struct ParameterRuleTip: Tip { // Define the app state you want to track. @Parameter static var isLoggedIn: Bool = false Static property '$isLoggedIn' is not concurrency-safe because it is non-isolated global shared mutable state. Is there a new pattern for supporting TipKit Parameters in Swift 6 with Complete Concurrency enabled? There is no obvious suggestion for how to fix this. The latest WWDC 2024 TipKit doesn't appear to have any solution(s).
6
2
341
2w
NSTextInputClient concurrency issues in Swift 6
Hello! In our codebase we have a NSView subclass that conforms to NSTextInputClient. This protocol is currently not marked as a main actor, but in the decade this has been in use here it has always been called on the main thread from AppKit. With Swift 6 (or complete concurrency checking in Swift 5) this conformance causes issues since NSView is a main actor but not this protocol. I've tried a few of the usual fixes (MainActor.assumeIsolated or prefixing the protocol conformance with @preconcurrency) but they were not able to resolve all warnings. So I dug in the AppKit headers and found that NSTextInputClient is usually implemented by the view itself, but that that is not a hard requirement (see NSTextInputContext.h the documentation for the client property or here). With that I turned my NSView subclass extension into a separate class that is not a main actor and in my NSView subclass create an instance of it and NSTextInputContext. This all seems to work fine in my initial tests, the delegate methods are called. But when the window loses and then regains key, I see a warning message in the console output. -[TUINSCursorUIController activate:]: Foo.TextInputClient isn't subclass of NSView. So my question is, am I doing it wrong with the custom class that implements the protocol? Or is the warning wrong? I would also appreciate a hint on how to better resolve the concurrency issues with NSTextInputClient. Is a main actor annotation coming at some point from your end? Thanks! Markus
0
0
165
2w
Concurrency-friendly version of `ArchiveByteStream` and `ArchiveStream`?
The AppleArchive module is pretty cool, but it relies almost entirely on two stream types, ArchiveByteStream and ArchiveStream, which don't really work well in a Swift Concurrency-based workflow since they all use thread-blocking mechanisms like pthread mutexes for synchronization, and threads in the cooperative pool should not be blocked. They also use their own thread pools for processing, independently of the cooperative thread pool, making it easy to end up with more threads than one has cores. (Perhaps even more so if you try to compress/decompress multiple files at once? The documentation isn't clear on whether separate archive operations share the same thread pool or not, but since it allows you to choose the size of the pool, it seems that these may be separate from each other as well.) Are there any plans for an interface to Apple Archive that would fit better with the structured concurrency model?
1
0
165
2w
CoreData result class has 'Sendable' warnings
In an iOS viewController, I use the NSDiffableDataSource to populate a tableView from the results of a CoreData fetchController. The result class is defined by CoreData - in this case a class named CDFilterStack. In xCode 16.0 Beta with strict concurrency checking = 'Complete' the CDFilterStack class has this warning everywhere it is referenced. "Type 'CDFilterStack' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode." The class definition of CDFilterStack is not editable because it is generated by CoreData. I think I need to mark this class (and other similar classes) as @preconcurrency... but how & where? Here's one method sample that generates three of these warnings func initialSnapShot() -> NSDiffableDataSourceSnapshot<Int, CDFilterStack> { var snapshot = NSDiffableDataSourceSnapshot<Int, CDFilterStack>() if let sections = dataProvider.fetchedResultsController.sections { for index in 0..<sections.count { let thisSection = sections[index] guard let sectionStacks = thisSection.objects as? [CDFilterStack] else { continue} snapshot.appendSections([index]) snapshot.appendItems(sectionStacks) } // for loop that will continue on error in objects } return snapshot } Careful reading of the various migration guides hasn't produced an example of how to handle this.. (btw the migration guides are nicely done:)
1
0
197
2w
`Task` at `createSampleData` function in What's new in SwiftData session
I was looking to the example code that point from What's new in SwiftData session and noticed something that I cannot understand about Swift concurrency. from this snippet: static func createSampleData(into modelContext: ModelContext) { Task { @MainActor in let sampleDataTrips: [Trip] = Trip.previewTrips let sampleDataLA: [LivingAccommodation] = LivingAccommodation.preview let sampleDataBLT: [BucketListItem] = BucketListItem.previewBLTs let sampleData: [any PersistentModel] = sampleDataTrips + sampleDataLA + sampleDataBLT sampleData.forEach { modelContext.insert($0) } if let firstTrip = sampleDataTrips.first, let firstLivingAccommodation = sampleDataLA.first, let firstBucketListItem = sampleDataBLT.first { firstTrip.livingAccommodation = firstLivingAccommodation firstTrip.bucketList.append(firstBucketListItem) } if let lastTrip = sampleDataTrips.last, let lastBucketListItem = sampleDataBLT.last { lastTrip.bucketList.append(lastBucketListItem) } try? modelContext.save() } } From the code snippet, I cannot see any task that needs to be marked with await, and it is also marked as @MainActor. My question is: why do we need to put Task here? It seems like all the code will run on the main thread anyway. I am just afraid that I might be missing some concept of Swift concurrency or SwiftData here. Thank you
2
0
266
2w
Sendability for Stream, InputStream, etc.
I have a project with some legacy networking code that uses the Stream (formerly NSStream) family of classes, including Stream, InputStream, OutputStream, and StreamDelegate. None of these are sendable, so I get a lot of warnings when implementing delegate methods in a @MainActor class. These classes seem like they could be sendable. Is this something that will happen soon? Is it a bug I should report? The networking code that uses these classes runs great, and hasn't needed changes for years, so my current solution is to just mark these unchecked: extension Stream: @unchecked Sendable { } extension InputStream: @unchecked Sendable { } extension OutputStream: @unchecked Sendable { } This makes the compiler happy, but makes me feel kind of bad. Is there something else I could do?
1
0
173
3w
Please Create a Sendable Version of CKRecord or Make CKRecord Sendable
CKRecord is a class which does not conform to the Sendable protocol. Its fields consist of NSStrings, NSData and others which are not Sendable. I understand that Apple is incrementally modifying objects to be sendable, but I am experiencing and I would assume others are experiencing a very large number of warnings (for now) about CKRecords and Sendable. It may be too much to make CKRecord Sendable and it may be too much to create a Sendable version of CKRecord, but it would be nice if it could at least be investigated. My particular situation is I have created a Protocol named CKMethods which some of my view controllers use to download and upload CKRecords. I suddenly have a large number of warnings about non-sendable types being sent from main actor-isolated context to non-isolated instance method. The CKRecords sent to and from the protocol do not get mutated and I have never had a problem with data races in the years that I have had this protocol. At some point, the warnings will probably become errors and I definitely do not want to get to that point. I am still coming up to speed on Swift Concurrency, so there may be a more simple solution than the one I am working on - creating a Sendable Struct for every CKRecord type that I have in my app and modifying all of the methods to pass the Struct instead of a CKRecord and convert the Struct to a CKRecord for upload and convert the CKRecord to the Struct for download.
3
1
1k
3w