Concurrency

RSS for tag

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

Posts under Concurrency tag

163 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Migrating @MainActor ViewModel to @Observable causing error
I get this error while migrating from ObservableObject to @Observable. Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context My original code: struct SomeView: View { @StateObject private var viewModel = ViewModel() } After migration: @MainActor @Observable class BaseViewModel { } @MainActor class ViewModel: BaseViewModel { } struct SomeView: View { @State private var viewModel = ViewModel() } As discussed here. It seems like @StateObject is adding @MainActor compliance to my View under the hood because it's wrappedValue and projectedValue properties are marked as @MainActor, while on @State they are not. @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) @frozen @propertyWrapper public struct StateObject<ObjectType> : DynamicProperty where ObjectType : ObservableObject { ... @MainActor public var wrappedValue: ObjectType { get } .... @MainActor public var projectedValue: ObservedObject<ObjectType>.Wrapper { get } } One solution for this is to mark my View explicitly as @MainActor struct ViewModel: View but it have it side effects, for example code like: Button(action: resendButtonAction) { Text(resendButtonAttributedTitle()) } Will result a warning Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor' While could be easily solved by using instead Button(action: { resendButtonAction() } ) { Text(resendButtonAttributedTitle()) } I still feel like marking the whole View explicitly as @MainActor is not a good practice. Adding fake @StateObject property to my view also do the trick, but it's a hack (the same for @FetchRequest). Can anyone think of a more robust solution for this?
1
1
1.6k
May ’24
POSIX read/write thread safety: is sharing socket descriptors between threads on macOS correct?
Hi, Despite the following code works great on Windows and Linux (well, there is an OS layer stripped from the code), it hangs on macOS (pseudocode first): create non-blocking socketpair(AF_UNIX, SOCK_STREAM, ...); + a couple of fcntl(fd, ..., flags | O_NONBLOCK) spawn 128 pairs of threads (might be as little as 32, but will need several iterations to reproduce). Of course, there is the errno check to ensure there are no errors but EWOULDBLOCK / EAGAIN readers read a byte 10000 times: for (...) { while (read(fd[1]...) < 1) select(...); r++;} writers write a byte 10000 times: for (...) { while (write(fd[0]...) < 1) select(...); w++;} Join writers; Join readers; On Linux/Windows with the iterations number really cranked up, I'm getting a socket buffer overflow, so ::write returns EWOULDBLOCK, then I'm waiting on a socket until it's ready, continue, and after joining both sets of threads I see that bytes-read is equal to bytes-written, everything fine. However, on macOS I quickly end up in a strange lock when writers are waiting on ::select(...., &write_fds, ...) and readers on the corresponding ::select(..., &read_fds, ...); I have really no idea how that could happen except that the read/write is not thread-safe. However, it looks like POSIX docs and manpages state that it is (at least, reentrant). Could anyone point me in the right direction? Detailed code below: std::atomic<int> bytes_written(0); std::atomic<int> bytes_read(0); static constexpr int k_packets = 10000; static constexpr int k_threads = 32; std::vector<std::thread> writers; std::vector<std::thread> readers; writers.reserve(k_threads); readers.reserve(k_threads); for (int i = 0; i < k_threads; ++i) { writers.emplace_back([fd_write = fd[1], &bytes_written]() { char data = 'x'; for (int i = 0; i < k_packets; ++i) { while (::write(fd_write, &data, 1) < 1) { fd_set writefds; FD_ZERO(&writefds); FD_SET(fd_write, &writefds); assert(errno == EAGAIN || errno == EWOULDBLOCK); int retval = ::select(fd_write + 1, nullptr, &writefds, nullptr, nullptr); if (retval < 1) assert(errno == EAGAIN || errno == EWOULDBLOCK); } ++bytes_written; } }); readers.emplace_back([fd_read = fd[0], &bytes_read]() { char data; for (int i = 0; i < k_packets; ++i) { while (::read(fd_read, &data, 1) < 1) { fd_set readfds; FD_ZERO(&readfds); FD_SET(fd_read, &readfds); assert(errno == EAGAIN || errno == EWOULDBLOCK); int retval = ::select(fd_read + 1, &readfds, nullptr, nullptr, nullptr); if (retval < 1) assert(errno == EAGAIN || errno == EWOULDBLOCK); } ++bytes_read; } }); } for (auto& t : writers) t.join(); for (auto& t : readers) t.join(); assert(bytes_written == bytes_read);
0
0
561
Dec ’23
StoreKit sample project from Apple fails concurrency checks
I am trying to use code from Apple's sample project on StoreKit (Implementing a store in your app using the StoreKit API). However, even if I don't make any changes to this sample project except enable "complete" concurrency checking in the build settings, I get warnings like Capture of 'self' with non-sendable type 'Store' in a '@Sendable' closure. How worried should I be about these warnings and how can I fix them?
1
0
772
Dec ’23
The need to use Task inside the declaration block of an asynchronous function
I am following the iOS App Dev Tutorials on Adopting Swift concurrency and Persisting data. On Adopting Swift concurrency, it is mentioned that we can call an asynchronous function using the await keyword. Because the asynchronous function can suspend execution, we can use await only in asynchronous contexts, such as inside the body of another asynchronous function. Adopting Swift concurrency, it is also mentioned that we can create a new asynchronous context with a Task to call asynchronous functions from a synchronous context. Based on this information, I concluded that the main added value of a Task is to enable calling an asynchronous function in a synchronous context. On Persisting data, a Task object is created inside the declaration block of an asynchronous function: func load() async throws { let task = Task<[DailyScrum], Error> { let fileURL = try Self.fileURL() guard let data = try? Data(contentsOf: fileURL) else { return [] } let dailyScrums = try JSONDecoder().decode([DailyScrum].self, from: data) return dailyScrums } let scrums = try await task.value self.scrums = scrums } I didn't understand why it was necessary to create task. Based on what I learned from Adopting Swift concurrency, the context of load() is already asynchronous. Therefore, we can simply call asynchronous functions without the need to create a Task object. When I tried to run the code by removing the task, the app still worked the same, or at least that was my perception. I believe that there is another reason to use Task in this example other than just enabling calling asynchronous functions. However, this was not explicitly explained in the tutorials. Could you please explain why we use Task here? What is the added value and how it would improve the behavior of this app? Please keep in mind that I am a complete beginner and had no idea about what concurrency was two weeks ago. In case deleting task would not change anything, could you please confirm that my initial understanding was correct?
1
0
681
Dec ’23
A random crash on DateFormatter
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libicucore.A.dylib 0x18c500918 icu::SimpleDateFormat::subFormat(icu::UnicodeString&amp;, char16_t, int, UDisplayContext, int, char16_t, icu::FieldPositionHandler&amp;, icu::Calendar&amp;, UErrorCode&amp;) const + 532 1 libicucore.A.dylib 0x18c500520 icu::SimpleDateFormat::format(icu::Calendar&amp;, icu::UnicodeString&amp;, icu::FieldPositionHandler&amp;, UErrorCode&amp;) const + 520 2 libicucore.A.dylib 0x18c5002f8 icu::SimpleDateFormat::format(icu::Calendar&amp;, icu::UnicodeString&amp;, icu::FieldPosition&amp;) const + 84 3 libicucore.A.dylib 0x18c42c4ac icu::DateFormat::format(double, icu::UnicodeString&amp;, icu::FieldPosition&amp;) const + 176 4 libicucore.A.dylib 0x18c535c40 udat_format + 352 5 CoreFoundation 0x189349a14 __cficu_udat_format + 68 6 CoreFoundation 0x189349898 CFDateFormatterCreateStringWithAbsoluteTime + 192 7 Foundation 0x18a432bf0 -[NSDateFormatter stringForObjectValue:] + 316 8 ShiningUtiliesKit 0x10522a130 SULog.log(:logDegree:file:method:line:) + 488 9 ShiningUtiliesKit 0x105229f3c SULog.debug(:file:method:line:) + 48 10 Calibration 0x1029f56bc closure #1 in Device.setDeviceData(:) + 487 (DeviceObject.swift:293) 11 Calibration 0x1029f3de0 specialized autoreleasepool(invoking:) + 23 (:0) [inlined] 12 Calibration 0x1029f3de0 Device.setDeviceData(_:) + 23 (DeviceObject.swift:291) [inlined] 13 Calibration 0x1029f3de0 closure #1 in closure #1 in variable initialization expression of Device.callback + 403 (DeviceObject.swift:221) 14 Calibration 0x1029f3548 thunk for @escaping @callee_guaranteed () -&gt; () + 27 (:0) 15 libdispatch.dylib 0x18907dcb8 _dispatch_call_block_and_release + 32 16 libdispatch.dylib 0x18907f910 _dispatch_client_callout + 20 17 libdispatch.dylib 0x18908dfa8 _dispatch_main_queue_drain + 984 18 libdispatch.dylib 0x18908dbc0 _dispatch_main_queue_callback_4CF + 44 19 CoreFoundation 0x18934b15c CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 16 20 CoreFoundation 0x189308a80 __CFRunLoopRun + 1996 21 CoreFoundation 0x189307c5c CFRunLoopRunSpecific + 608 22 HIToolbox 0x193884448 RunCurrentEventLoopInMode + 292 23 HIToolbox 0x193884284 ReceiveNextEventCommon + 648 24 HIToolbox 0x193883fdc _BlockUntilNextEventMatchingListInModeWithFilter + 76 25 AppKit 0x18cae2c54 _DPSNextEvent + 660 26 AppKit 0x18d2b8ebc -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 716 27 AppKit 0x18cad6100 -[NSApplication run] + 476 28 AppKit 0x18caad3cc NSApplicationMain + 880 29 SwiftUI 0x1b3c14f80 0x1b3b3b000 + 892800 30 SwiftUI 0x1b44a530c 0x1b3b3b000 + 9872140 31 SwiftUI 0x1b48eb4f0 0x1b3b3b000 + 14353648 32 Calibration 0x1029fa1a0 static IntraoralScanMiniProgramApp.$main() + 51 (IntraoralScanMiniProgramApp.swift:14) [inlined] 33 Calibration 0x1029fa1a0 main + 63 (DeviceObject.swift:0) 34 dyld 0x188eb10e0 start + 2360 My mac APP has a crash about DateFormatter. mac version is 14.1.1. public class SULog { private let dateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormte = "yyy-MM-dd HH:mm:ss_sss" return dateFormatter } private var lock = NSLock() public func debug(_ message: Any...) { log(message) } public func log(_ message: Any...) { lock.lock() let date = Date() let dateString = dateFormatter.string(from:date) lock.unlock() print(dateString) } } var log:SULog! class AppDelegate: NSObject { func applicationWillFinishLaunching(_ notification: Notification) { log = SULog() } } public class Test { func test() { autoreleasepool { //.......other code log.debug("test") // crashed //........ other code } } } Any thought on this crash? Thanks
1
0
673
Dec ’23
How to prevent TSAN from reporting data-races that are intentional and considered safe?
I'm having a hard time relying on TSAN to detect problems due to its rightful insistence on reporting data-races (I know, stick with me). Picture the following implementation of a lazily-allocated property in an Obj-C class: @interface MyClass { id _myLazyValue; // starts as nil as all other Obj-C ivars } @end @implementation MyClass - (id)myLazyValue { if (_myLazyValue == nil) { @synchronized(self) { if (_myLazyValue == nil) { _myLazyValue = &lt;expensive computation&gt; } } } return _myLazyValue; } @end The first line in the method is reading a pointer-sized chunk of memory outside of the protection provided by the @synchronized(...) statement. That same value may be written by a different thread within the execution of the @synchronized block. This is what TSAN complains about, but I need it not to. The code above ensures the ivar is written by at most one thread. The read is unguarded, but it is impossible for any thread to read a non-nil value back that is invalid, uninitialized or unretained. Why go through this trouble? Such a lazily-allocated property usually locks on @synchronized once, until (at most) one thread does any work. Other threads may be temporarily waiting on the same lock but again only while the value is being initialized. The cost of allocation and initialization is guaranteed to be paid once: multiple threads cannot initialize the value multiple times (that’s the reason for the second _myLazyValue == nil check within the scope of the @synchronized block). Subsequent accesses of the initialized property skip locking altogether, which is exactly the performance we want from a lazily-allocated, immutable property that still guarantees thread-safe access. Assuming there isn't a big embarrassing hole in my logic, is there a way to decorate specific portions of our sources (akin to #pragma statements that disable certain warnings) so that you can mark any read/write access to a specific value as "safe"? Is the most granular tool for this purpose the __attribute__((no_sanitize("thread")))? Ideally one would want to ask TSAN to ignore only specific read/writes, rather than the entire body of a function. Thank you!
8
0
1.3k
Jan ’24
How to safely access Core data NSManagedObject attributes from a SwiftUI view using swift concurrency model?
How do I safely access Core data NSManagedObject attributes from SwiftUI view using the swift concurrency model. My understanding is we need to enclose any access to NSManageObject attributes in await context.perform {}. But if I use it anywhere in a view be that in the body(), or init() or .task() modifier I get the following warnings or errors: for .task{} modifier or if within any Task {}: Passing argument of non-sendable type 'NSManagedObjectContext.ScheduledTaskType' outside of main actor-isolated context may introduce data races this happens even if I create a new context solely for this access. if within body() or init(): 'await' in a function that does not support concurrency but we cant set body or init() to async an example of the code is something along the lines of: var attributeString: String = "" let context = PersistentStore.shared.persistentContainer.newBackgroundContext() await context.perform { attributeString = nsmanagedObject.attribute! }
2
1
1.3k
Dec ’23
Concurrency Resources
Swift Concurrency Resources: DevForums tags: Concurrency The Swift Programming Language > Concurrency documentation Migrating to Swift 6 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. Swift Async Algorithms package Swift Concurrency Proposal Index DevForum post Matt Massicotte’s blog 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
1k
1w
Why aren't changes to @Published variables automatically published on the main thread?
Given that SwiftUI and modern programming idioms promote asynchronous activity, and observing a data model and reacting to changes, I wonder why it's so cumbersome in Swift at this point. Like many, I have run up against the problem where you perform an asynchronous task (like fetching data from the network) and store the result in a published variable in an observed object. This would appear to be an extremely common scenario at this point, and indeed it's exactly the one posed in question after question you find online about this resulting error: Publishing changes from background threads is not allowed Then why is it done? Why aren't the changes simply published on the main thread automatically? Because it isn't, people suggest a bunch of workarounds, like making the enclosing object a MainActor. This just creates a cascade of errors in my application; but also (and I may not be interpreting the documentation correctly) I don't want the owning object to do everything on the main thread. So the go-to workaround appears to be wrapping every potentially problematic setting of a variable in a call to DispatchQueue.main. Talk about tedious and error-prone. Not to mention unmaintainable, since I or some future maintainer may be calling a function a level or two or three above where a published variable is actually set. And what if you decide to publish a variable that wasn't before, and now you have to run around checking every potential change to it? Is this not a mess?
9
0
2.5k
Oct ’24
SwiftUI Commands and StrictConcurrency Warnings Issue
I have enabled “StrictConcurrency” warnings in my project that uses SwiftUI. I have a Commands struct. It has a Button, whose action is calling an async method via Task{}. This builds without warnings within Views, but not Commands. There the compiler reports “Main actor-isolated property 'body' cannot be used to satisfy nonisolated protocol requirement”. Looking at SwiftUI: In View, body is declared @MainActor: @ViewBuilder @MainActor var body: Self.Body { get } In Commands, body is not declared @MainActor: @CommandsBuilder var body: Self.Body { get } So the common practice of making a Button action asynchronous: Button { Task { await model.load() } } label:{ Text("Async Button") } will succeed without warnings in Views, but not in Commands. Is this intentional? I've filed FB13212559. Thank you.
3
0
921
Oct ’24
Instruments - Swift Concurrency not working
Hello, I am trying to debug Swift Concurrency codes by using Swift Concurrency Instruments. But in our project which has been maintained for a long time, Swift Concurrency Instruments seems not working. Task { print("async code") await someAsyncFunction() } When I run Swift Concurrency Instruments with the above codes in our project, I could check that the print works well by checking stdout/stderr Instruments, but there are no records on Swift Concurrency Instruments. But in the small simple sample project, I checked that Swift Concurrency Instruments works well. Are there any settings that I need to do for the legacy project to debug Swift Concurrency?
10
0
1.6k
Mar ’24
Observation and MainActor
Previously, it was recommended to use the @MainActor annotation for ObservableObject implementation. @MainActor final class MyModel: ObservableObject { let session: URLSession @Published var someText = "" init(session: URLSession) { self.session = session } } We could use this as either a @StateObject or @ObservedObject: struct MyView: View { @StateObject let model = MyModel(session: .shared) } By moving to Observation, I need to the @Observable macro, remove the @Published property wrappers and Switch @StateObject to @State: @MainActor @Observable final class MyModel { let session: URLSession var someText = "" init(session: URLSession) { self.session = session } } But switching from @StateObject to @State triggers me an error due to a call to main-actor isolated initialiser in a synchronous nonisolated context. This was not the case with @StateObject of @ObservedObject. To suppress the warning I could : mark the initializer as nonisolated but it is not actually what I want Mark the View with @MainActor but this sounds odd Both solutions does not sound nice to my eye. Did I miss something here?
9
3
5.6k
Apr ’24
Why isn't the View protocol @MainActor?
Hi, It is known that if a SwiftUI view contains an @ObservedObject or @StateObject property, the View will inherit @MainActor isolation from the property wrappers. Similarly, the body view-builder is also marked @MainActor. What I'm wondering is why the whole SwiftUI View protocol isn't marked @MainActor. It seems to be a deliberate decision, but AFAICT it would make a lot of sense for all data and operations defined in a view to have main-actor isolation unless marked nonisolated. I'm currently adding @MainActor annotations to an existing codebase, and it's a bit awkward that some views automatically gain this attribute one way or another, while others need it explicitly applied. Is there a rationale that can be shared, or is this something which may be revised in future versions of the framework?
1
6
1.7k
Jun ’24
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
1.5k
Jun ’24
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
4k
Jul ’24
AsyncPublisher of KeyValueObservingPublisher doesn't work
Hi, I'm trying to use async/await for KVO and it seems something is broken. For some reason, it doesn't go inside for in body when I'm changing the observed property. import Foundation import PlaygroundSupport class TestObj: NSObject {   @objc dynamic var count = 0 } let obj = TestObj() Task {   for await value in obj.publisher(for: \.count).values {     print(value)   } } Task.detached {   try? await Task.sleep(for: .microseconds(100))   obj.count += 1 } Task.detached {   try? await Task.sleep(for: .microseconds(200))   obj.count += 1 } PlaygroundPage.current.needsIndefiniteExecution = true Expected result: 0, 1, 2 Actual result: 0 Does anyone know what is wrong here?
2
1
2.4k
Feb ’24
Task not executing at all
I have an 8th generation iPad, now updated with iPadOS 16.2 (20C65) and I have an issue that I also saw on earlier 16.* betas. Task is not executing at all. This is so frustrating because I have adopted async/await in my app, I support iOS 15+, everything was working fine but now that stuff inside Task { } is not executed my app seems to be broken. (Note: my main device is an iPhone 11, still on iOS 16.0, and it works fine there.) It is also frustrating to see no other developers are complaining about this, like it happens only with my app. I have debugged with print statements and breakpoints and I can say for sure that stuff is not executing. Does anybody have any ideas? Anything else I can try? FB11866066
17
0
3.6k
Aug ’24
How to use network sockets with async/await?
I have an application that communicates with custom external hardware on the network (using UDP). I have a thread that receives and process the UDP data and then signals a waiting thread by releasing a semaphore when data is available. A have a asyncSendAndReceive and asyncReceive function that just begs to use async/await. But I cannot simply switch because of the use of the semaphore. Various forums and discussions said that semaphores should no longer be used for signalling. If not semaphores, then what else? Note that my two async functions may not always block. If data was received before they were called, then it is queued (and the semaphore is signalled).
9
0
3.0k
Jul ’24
MainActor and NSInternalInconsistencyException: 'Call must be made on main thread'
Hello, When attempting to assign the UNNotificationResponse to a Published property on the main thread inside UNUserNotificationCenterDelegate's method func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async both Task { @MainActor in } and await MainActor.run are throwing a NSInternalInconsistencyException: 'Call must be made on main thread'. I thought both of them were essentially doing the same thing, i.e. call their closure on the main thread. So why is this exception thrown? Is my understanding of the MainActor still incorrect, or is this a bug? Thank you Note: Task { await MainActor.run { ... } } and DispatchQueue.main.async don't throw any exception.
4
5
2.9k
Sep ’24
Tasks shown "alive" in Instruments display even after they should have finished.
In my TestApp I run the following code, to calculate every pixel of a bitmap concurrently: private func generate() async {     for x in 0 ..< bitmap.width{         for y in 0 ..< bitmap.height{             let result = await Task.detached(priority:.userInitiated){                return iterate(x,y)             }.value             displayResult(result)         }     } } This works and does not give any warnings or runtime issues. After watching the WWDC talk "Visualize and optimize Swift concurrency" I used instruments to visualize the Tasks: The number of active tasks continuously raises until 2740 and stays constant at this value even after all 64000 pixels have been calculated and displayed. What am I doing wrong?
2
0
1.5k
Dec ’23