Processes & Concurrency

RSS for tag

Discover how the operating system manages multiple applications and processes simultaneously, ensuring smooth multitasking performance.

Concurrency Documentation

Posts under Processes & Concurrency subtopic

Post

Replies

Boosts

Views

Created

best practices for communication between system extension and daemon
Hello, My team has developed a DNS proxy for macOS. We have this set up with a system extension that interacts with the OS, and an always-running daemon that does all the heavy lifting. Communication between the two is DNS request and response packet traffic. With this architecture what are best practices for how the system extension communicates with a daemon? We tried making the daemon a socket server, but the system extension could not connect to it. We tried using XPC but it did not work and we could not understand the errors that were returned. So what is the best way to do this sort of thing?
3
0
724
Dec ’24
Issue with Developer App Crashing on iPad Upon Launch
Recently, after updating the Developer app to the latest version, my iPad has been unable to open this app as it crashes immediately upon launch. Prior to the update, the app functioned normally. My device is an 11-inch iPad Pro from 2021, running iPadOS 17.3. I have tried troubleshooting steps such as reinstalling the app and restarting the device, but these actions have not resolved the issue. However, I need to use this specific version of the system, iPadOS 17.3, for software testing purposes and cannot upgrade the system. Other apps on my device work normally without any issues. Is there a solution to this problem? I have attempted to contact the developer support team in China, but they were also unable to provide a resolution. This issue is reproducible 100% of the time on my iPad.
1
0
352
Dec ’24
Can I ensure location is saved to SwiftData within Share Extension lifetime?
I am writing a SwiftData/SwiftUI app in which the user saves simple records, tagged with their current location. Core Location can take up to 10 seconds to retrieve the current location from its requestLocation() call. I the main app I have wrapped the CLLocationManager calls with async implementations. I kick off a Task when a new record is created, and write the location to my @Model on the main thread when it completes. A realistic use of the share extension doesn't give the task enough time to complete. I can use performExpiringActivity to complete background processing after the share extension closes but this needs to be a synchronous block. Is there some way of using performExpiringActivity when relying on a delegate callback from something like Core Location?
0
0
412
Dec ’24
What is ImmersiveSpaceAppModel in BOT-anist?
I would like to implement an expression that pops out from the window to Immersive based on the following WWDC video. This video introduces the new features of visionOS 2.0 in the form of refurbishing Apple's sample app BOT-anist. https://developer.apple.com/jp/videos/play/wwdc2024/10153/?time=1252 In the video, it looks like ImmersiveSpaceAppModel is newly implemented. However, the key code is not mentioned anywhere. You pass appModel.robot as the from argument to the transform method of RealityViewContent. It seems that BOT-anist has been updated once and can be downloaded from the following URL, but there is no class such as ImmersiveSpaceAppModel implemented in this app either. https://developer.apple.com/documentation/visionos/bot-anist Has it been further updated again? Frankly, I'm not sure if it is possible to proceed as per the WWDC video. Translated with DeepL.com (free version)
1
0
445
Dec ’24
WatchConnectivity Swift 6 - Incorrect actor executor assumption
I am trying to migrate a WatchConnectivity App to Swift6 and I found an Issue with my replyHandler callback for sendMessageData. I am wrapping sendMessageData in withCheckedThrowingContinuation, so that I can await the response of the reply. I then update a Main Actor ObservableObject that keeps track of the count of connections that have not replied yet, before returning the data using continuation.resume. ... @preconcurrency import WatchConnectivity actor ConnectivityManager: NSObject, WCSessionDelegate { private var session: WCSession = .default private let connectivityMetaInfoManager: ConnectivityMetaInfoManager ... private func sendMessageData(_ data: Data) async throws -> Data? { Logger.shared.debug("called on Thread \(Thread.current)") await connectivityMetaInfoManager.increaseOpenSendConnectionsCount() return try await withCheckedThrowingContinuation({ continuation in self.session.sendMessageData( data, replyHandler: { data in Task { await self.connectivityMetaInfoManager .decreaseOpenSendConnectionsCount() } continuation.resume(returning: data) }, errorHandler: { (error) in Task { await self.connectivityMetaInfoManager .decreaseOpenSendConnectionsCount() } continuation.resume(throwing: error) } ) }) } Calling sendMessageData somehow causing the app to crash and display the debug message: Incorrect actor executor assumption. The code runs on swift 5 with SWIFT_STRICT_CONCURRENCY = complete. However when I switch to swift 6 the code crashes. I rebuilt a simple version of the App. Adding bit by bit until I was able to cause the crash. See Broken App Awaiting sendMessageData and wrapping it in a task and adding the @Sendable attribute to continuation, solve the crash. See Fixed App But I do not understand why yet. Is this intended behaviour? Should the compiler warn you about this? Is it a WatchConnectivity issue? I initially posted on forums.swift.org, but was told to repost here.
3
0
1.2k
Dec ’24
How to get the full process name like Activity Monitor
I'm try to monitor all processes by ES client. But I found the process name is different from the Activity Monitor displayed. As shown in the picture below, there are ShareSheetUI(Pages) and ShareSheetUI(Finder) processes in Activity Monitor, but I can only get the same name ShareSheetUI, I thought of many ways to display the name in parentheses, but nothing worked, so there is a way to display the process name like Activity Monitor?
1
0
452
Dec ’24
Guideline 3.2.1(viii) - Business - Other Business Model Issues - Acceptable
The support URL provided in App Store Connect must direct to a support page with links to a loan services privacy policy. The support page must also reference the lender or lending license. The privacy policy provided in App Store Connect must include references to the lender. The verified email domains associated with your Apple Developer Program account must match domains for the submitting company or partnered financial institution.
0
0
485
Dec ’24
Why is flow control important?
One challenging aspect of Swift concurrency is flow control, aka backpressure. I was explaining this to someone today and thought it better to post that explanation here, for the benefit of all. If you have questions or comments, start a new thread in App & System Services > Processes & Concurrency and tag with Swift and Concurrency. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Why is flow control important? In Swift concurrency you often want to model data flows using AsyncSequence. However, that’s not without its challenges. A key issue is flow control, aka backpressure. Imagine you have a network connection with a requests property that returns an AsyncSequence of Request values. The core of your networking code might be a loop like this: func processRequests(connection: Connection) async throws { for try await request in connection.requests { let response = responseForRequest(request) try await connection.reply(with: response) } } Flow control is important in both the inbound and outbound cases. Let’s start with the inbound case. If the remote peer is generating requests very quickly, the network is fast, and responseForRequest(_:) is slow, it’s easy to fall foul of unbounded memory growth. For example, if you use AsyncStream to implement the requests property, its default buffering policy is .unbounded. So the code receiving requests from the connection will continue to receive them, buffering them in the async stream, without any bound. In the worst case scenario that might run your process out of memory. In a more typical scenario it might result in a huge memory spike. The outbound case is similar. Imagine that the remote peer keeps sending requests but stops receiving them. If the reply(with:) method isn’t implemented correctly, this might also result in unbounded memory growth. The solution to this problem is flow control. This flow control operates independently on the send and receive side: On the send side, the code sending responses should notice that the network connection has asserted flow control and stop sending responses until that flow control lifts. In an async method, like the reply(with:) example shown above, it can simply not return until the network connection has space to accept the reply. On the receive side, the code receiving requests from the connection should monitor how many are buffered. If that gets too big, it should stop receiving. That causes the requests to pile up in the connection itself. If the network connection implements flow control properly [1], this will propagate to the remote peer, which should stop generating requests. [1] TCP and QUIC both implement flow control. Use them! If you’re tempted to implement your own protocol directly on top of UDP, consider how it should handle flow control. Flow control and Network framework Network framework has built-in support for flow control. On the send side, it uses a ‘push’ model. When you call send(content:contentContext:isComplete:completion:) the connection buffers the message. However, it only calls the completion handler when it’s passed that message to the network for transmission [2]. If you send a message and don’t receive this completion callback, it’s time to stop sending more messages. On the receive side, Network framework uses a ‘pull’ model. The receiver calls a receive method, like receiveMessage(completion:), which calls a completion handler when there’s a message available. If you’ve already buffered too many messages, just stop calling this receive method. These techniques are readily adaptable to Swift concurrency using Swift’s CheckedContinuation type. That works for both send and receive, but there’s a wrinkle. If you want to model receive as an AsyncSequence, you can’t use AsyncStream. That’s because AsyncStream doesn’t support flow control. So, you’ll need to come up with your own AsyncSequence implementation [3]. [2] Note that this doesn’t mean that the data has made it to the remote peer, or has even been sent on the wire. Rather, it says that Network framework has successfully passed the data to the transport protocol implementation, which is then responsible for getting it to the remote peer. [3] There’s been a lot of discussion on Swift Evolution about providing such an implementation but none of that has come to fruition yet. Specifically: The Swift Async Algorithms package provides AsyncChannel, but my understanding is that this is not yet ready for prime time. I believe that the SwiftNIO folks have their own infrastructure for this. They’re driving this effort to build such support into Swift Async Algorithms. Avoid the need for flow control In some cases you can change your design to avoid the need for control. Imagine that your UI needs to show the state of a remote button. The network connection sends you a message every time the button is depressed or released. However, your UI only cares about the current state. If you forward every messages from the network to your UI, you have to worried about flow control. To eliminate that worry: Have your networking code translate the message to reflect the current state. Use AsyncStream with a buffering policy of .bufferingNewest(1). That way there’s only ever one value in the stream and, if the UI code is slow for some reason, while it might miss some transitions, it always knows about the latest state. 2024-12-13 Added a link to the MultiProducerSingleConsumerChannel PR. 2024-12-10 First posted.
0
0
723
Dec ’24
Background Task Scheduling (BGAppRefreshTask)
All the nuances of when and whether a background task runs aside, does launching the app cancel the currently scheduled refresh task? As an example, consider the following case: 8AM - user launches app. This launch schedules a background refresh for 12 hours later, at 8PM 12PM (noon) - user launches the app, views some content, then exits the app. Does the scheduled refresh for 8PM still exist, or does the launch at noon invalidate that task, since the refresh could conceivably be handled during that noon launch? Hopefully this is articulated clearly enough, but I'm trying to understand the specifics of background refresh behavior, since I don't want to run that refresh every time the app is opened. However, if opening the app invalidates scheduled refreshes, I will need to include logic that will reschedule the refresh accordingly.
2
0
486
Dec ’24
Communicate with running app
I've written an app that uses On Idle (RunHandler) to prompt me to get up and stretch every 30 minutes. Is there anyway to query the running app to determine how long it's been since it last woke up and prompted me? Something like thru the apps properties by right clicking on the icon in the dock?
2
0
319
Dec ’24
Launchd ran job quits early
Hey, I have a user script that I have set up to run daily with launchd, the launchd logs indicate that the script is started but it's almost immediately exited with status 0. The odd thing is that it doesn't say that the script was killed or stopped in anyway, even though that is what seems to happen. 2024-12-04 14:15:04.970214 (gui/501/com.me.test) <Notice>: internal event: WILL_SPAWN, code = 0 2024-12-04 14:15:04.970320 (gui/501/com.me.test) <Notice>: service state: spawn scheduled 2024-12-04 14:15:04.970325 (gui/501/com.me.test) <Notice>: service state: spawning 2024-12-04 14:15:04.970431 (gui/501/com.me.test) <Notice>: launching: xpc event 2024-12-04 14:15:04.972067 (gui/501/com.me.test [26446]) <Notice>: xpcproxy spawned with pid 26446 2024-12-04 14:15:04.972096 (gui/501/com.me.test [26446]) <Notice>: internal event: SPAWNED, code = 0 2024-12-04 14:15:04.972107 (gui/501/com.me.test [26446]) <Notice>: service state: xpcproxy 2024-12-04 14:15:04.972160 (gui/501/com.me.test [26446]) <Notice>: internal event: SOURCE_ATTACH, code = 0 2024-12-04 14:15:04.998157 (gui/501/com.me.test [26446]) <Notice>: service state: running 2024-12-04 14:15:04.998180 (gui/501/com.me.test [26446]) <Notice>: internal event: INIT, code = 0 2024-12-04 14:15:04.998199 (gui/501/com.me.test [26446]) <Notice>: Successfully spawned shell_wrapper.sh[26446] because xpc event 2024-12-04 14:15:05.217482 (gui/501/com.me.test [26446]) <Notice>: exited due to exit(0), ran for 246ms 2024-12-04 14:15:05.217494 (gui/501/com.me.test [26446]) <Notice>: service state: exited 2024-12-04 14:15:05.217502 (gui/501/com.me.test [26446]) <Notice>: internal event: EXITED, code = 0 2024-12-04 14:15:05.217506 (gui/501 [100003]) <Notice>: service inactive: com.me.test 2024-12-04 14:15:05.217523 (gui/501/com.me.test [26446]) <Notice>: service state: not running In the script itself I log at the end to indicate that it finished successfully, when ran with launchd I never reach this point however. The script also do two network requests and make a pause for between 5-10 seconds with time.sleep(paus) (python script). However the launchd log indicates that the script finished in 246ms. Not sure what is going on, the script itself does not require any privileges above a normal user. I have tried running the script directly, and it works as expected. I have also tried evoking the script from launchd explicitly with: launchctl kickstart which starts the job but gives the same result as the scheduled job, except the log now says: `launching: non-ipc demand' instead. I have also tried to remove the networking requests and replace them by reading data from a file without any sleep, no difference in outcome though. I have generated the launchd plist file using Lingon.app and it's placed in ~/Library/LaunchAgents Not sure what more information I can provide, but need some suggestion on how I can proceed help solving this.
4
0
514
Dec ’24
What are Dispatch workloops?
I’ve been experimenting with Dispatch, and workloops in particular. I gather that they’re similar to serial queues, except that they reorder work items by QoS. I suspect there’s more to workloops than meets the eye, though; calling dispatch_set_target_queue on them has no effect, in spite of the <dispatch/workloop.h> saying that workloops “can be passed to all APIs accepting a dispatch queue, except for functions from the dispatch_sync() family”. Workloops keep showing up in odd places like Metal and Network.framework backtraces, and <dispatch/workloop.h> includes functionality for tying workloops to os_workgroups (?!). What exactly is a workloop beyond just a serial queue with priority ordering, and why can’t I set the target queue of one?
2
0
748
Dec ’24
Background Task Execution - Doesn't Seem Consistent
I have an app, that when enters the background schedules a task to run. The earliest possible time value is set, as is the completion handler when the task eventually runs. It seems to run pretty reliably for the 1st few interations and then (from looking at the streaming Console logs), doesn't seem to reach a high CP score to execute next time around. eg '......background.task:EDBC23' CurrentScore: 0.648418, ThresholdScore: 0.808034 DecisionToRun:0 looking at the previous entries before this, I can see the breakdown... {name: Application Policy, policyWeight: 50.000, response: {0, 0.35}} {name: Device Activity Policy, policyWeight: 5.000, response: {0, 0.50}} ], Decision: CP Score: 0.648418} and I understand certain elements are outside of our control; however, is there a preferred method to get a background task (which ultimately runs an API call) to trigger consistently? The silent-push method has come up a few times - but of course, if the user disables / doesn't consent to push notifications, that fails Any suggestions?
1
0
391
Dec ’24
macOS 14 XPC vs Foundation XPC
I'm looking into a newer XPC API available starting with macOS 14. Although it's declared as a low-level API I can't figure it how to specify code signing requirement using XPCListener and XPCSession. How do I connect it with xpc_listener_set_peer_code_signing_requirement and xpc_connection_set_peer_code_signing_requirement which require xpc_listener_t and xpc_connection_t respectively? Foundation XPC is declared as a high-level API and provides easy ways to specify code signing requirements on both ends of xpc. I'm confused with all these XPC APIs and their future: Newer really high-level XPCListener and XPCSession API (in low-level framework???) Low-level xpc_listener_t & xpc_connection_t -like API. Is it being replaced by newer XPCListener and XPCSession? How is it related to High-level Foundation XPC? Are NSXPCListener and NSXPCConnection going to be deprecated and replaced by XPCListener and XPCSession??
2
0
694
Nov ’24
Background Tasks runs foreground
Hello everyone! I'm having a problem with background tasks running in the foreground. When a user enters the app, a background task is triggered. I've written some code to check if the app is in the foreground and to prevent the task from running, but it doesn't always work. Sometimes the task runs in the background as expected, but other times it runs in the foreground, as I mentioned earlier. Could it be that I'm doing something wrong? Any suggestions would be appreciated. here is code: class BackgroundTaskService { @Environment(\.scenePhase) var scenePhase static let shared = BackgroundTaskService() private init() {} // MARK: - create task func createCheckTask() { let identifier = TaskIdentifier.check BGTaskScheduler.shared.getPendingTaskRequests { requests in if requests.contains(where: { $0.identifier == identifier.rawValue }) { return } self.createByInterval(identifier: identifier.rawValue, interval: identifier.interval) } } private func createByInterval(identifier: String, interval: TimeInterval) { let request = BGProcessingTaskRequest(identifier: identifier) request.earliestBeginDate = Date(timeIntervalSinceNow: interval) scheduleTask(request: request) } // MARK: submit task private func scheduleTask(request: BGProcessingTaskRequest) { do { try BGTaskScheduler.shared.submit(request) } catch { // some actions with error } } // MARK: background actions func checkTask(task: BGProcessingTask) { let today = Calendar.current.startOfDay(for: Date()) let lastExecutionDate = UserDefaults.standard.object(forKey: "lastCheckExecutionDate") as? Date ?? Date.distantPast let notRunnedToday = !Calendar.current.isDate(today, inSameDayAs: lastExecutionDate) guard notRunnedToday else { task.setTaskCompleted(success: true) createCheckTask() return } if scenePhase == .background { TaskActionStore.shared.getAction(for: task.identifier)?() } task.setTaskCompleted(success: true) UserDefaults.standard.set(today, forKey: "lastCheckExecutionDate") createCheckTask() } } And in AppDelegate: BGTaskScheduler.shared.register(forTaskWithIdentifier: "check", using: nil) { task in guard let task = task as? BGProcessingTask else { return } BackgroundTaskService.shared.checkNodeTask(task: task) } BackgroundTaskService.shared.createCheckTask()
1
0
887
Nov ’24
Async/Await and updating state
When using conformance to ObservableObject and then doing async work in a Task, you will get a warning courtesy of Combine if you then update an @Published or @State var from anywhere but the main thread. However, if you are using @Observable there is no such warning. Also, Thread.current is unavailable in asynchronous contexts, so says the warning. And I have read that in a sense you simply aren't concerned with what thread an async task is on. So for me, that begs a question. Is the lack of a warning, which when using Combine is rather important as ignoring it could lead to crashes, a pretty major bug that Apple seemingly should have addressed long ago? Or is it just not an issue to update state from another thread, because Xcode is doing that work for us behind the scenes too, just as it manages what thread the async task is running on when we don't specify? I see a lot of posts about this from around the initial release of Async/Await talking about using await MainActor.run {} at the point the state variable is updated, usually also complaining about the lack of a warning. But ow years later there is still no warning and I have to wonder if this is actually a non issue. On some ways similar to the fact that many of the early posts I have seen related to @Observable have examples of an @Observable ViewModel instantiated in the view as an @State variable, but in fact this is not needed as that is addressed behind the scenes for all properties of an @Observable type. At least, that is my understanding now, but I am learning Swift coming from a PowerShell background so I question my understanding a lot.
5
0
1.5k
Nov ’24
How to correctly deploy bundled launchdaemons/launchagents?
I'm working on an enterprise product that's mainly a daemon (with Endpoint Security) without any GUI component. I'm looking into the update process for daemons/agents that was introduced with Ventura (Link), but I have to say that the entire process is just deeply unfun. Really can't stress this enough how unfun. Anyway... The product bundle now contains a dedicated Swift executable that calls SMAppService.register for both the daemon and agent. It registers the app in the system preferences login items menu, but I also get an error. Error registering daemon: Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted} What could be the reason? I wouldn't need to activate the items, I just need them to be added to the list, so that I can control them via launchctl. Which leads me to my next question, how can I control bundled daemons/agents via launchctl? I tried to use launchctl enable and bootstrap, just like I do with daemons under /Library/LaunchDaemons, but all I get is sudo launchctl enable system/com.identifier.daemon sudo launchctl bootstrap /Path/to/daemon/launchdplist/inside/bundle/Library/LaunchDaemons/com.blub.plist Bootstrap failed: 5: Input/output error (not super helpful error message) I'm really frustrated by the complexity of this process and all of its pitfalls.
7
0
800
Oct ’24