Hi,
We are running into issues with iOS app prewarming, where the system launches our app before the user has entered their passcode.
In our case, the app stores flags, counters, and session data in UserDefaults and the Keychain. During prewarm launches:
UserDefaults only returns default values (nil, 0, false). We have no way of knowing whether this information is valid or just a placeholder caused by prewarming.
Keychain items with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly are inaccessible, which can lead to broken business logic (the app can assume no session exists).
No special launch options or environment variables appear to be set.
We can reproduce this 100% of the time by starting a Live Activity in the app before reboot.
Here’s an example of the workaround we tried, following older recommendations:
__attribute__((constructor))
static void ModuleInitializer(void) {
char* isPrewarm = getenv("ActivePrewarm");
if (isPrewarm != NULL && isPrewarm[0] == '1') {
exit(0); // prevent prewarm launch from proceeding
}
}
On iOS 16+, the ActivePrewarm environment variable doesn’t seem to exist anymore (though older docs and SDKs such as Sentry reference it).
We also tried listening for UIApplication.protectedDataDidBecomeAvailableNotification, but this is not specific to prewarming (it also fires when the device gets unlocked) and can cause watchdog termination if we delay work too long.
Questions:
Is there a supported way to opt out of app prewarming?
What is the correct way to detect when an app is being prewarmed?
Is the ActivePrewarm environment variable still supported in iOS 16+?
Ideally, the UserDefaults API itself should indicate whether it is returning valid stored values or defaults due to the app being launched in a prewarm session.
We understand opting out may impact performance, but data security and integrity are our priority. Any guidance would be greatly appreciated.
Processes & Concurrency
RSS for tagDiscover how the operating system manages multiple applications and processes simultaneously, ensuring smooth multitasking performance.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
In the header for workloop.h there is this note:
A dispatch workloop is a "subclass" of dispatch_queue_t which can be passed to all APIs accepting a dispatch queue, except for functions from the dispatch_sync() family. dispatch_async_and_wait() must be used for workloop objects. Functions from the dispatch_sync() family on queues targeting a workloop are still permitted but discouraged for performance reasons.
I have a couple questions related to this. First, I'd like to better understand what the alluded-to 'performance reasons' are that cause this pattern to be discouraged in the 'queues targeting a workloop' scenario. From further interrogation of the headers, I've found these explicit callouts regarding differences in the dispatch_sync and dispatch_async_and_wait API:
dispatch_sync:
Work items submitted to a queue with dispatch_sync() do not observe certain queue attributes of that queue when invoked (such as autorelease frequency and QOS class).
dispatch_async_and_wait:
Work items submitted to a queue with dispatch_async_and_wait() observe all queue attributes of that queue when invoked (inluding [sic] autorelease frequency or QOS class).
Additionally, dispatch_async_and_wait has a section of the headers devoted to 'Differences with dispatch_sync()', though I can't say I entirely follow the distinctions it attempts to draw.
Based on that, my best guess is that the 'performance reasons' are something about either QoS not being properly respected/observed or some thread context switching differences that can degrade performance, but I would appreciate insight from someone with more domain knowledge.
My second question is a bit more general – taking a step back, why exactly do these two API exist? It's not clear to me from the existing documentation I've found why I would/should prefer dispatch_sync over dispatch_async_and_wait (other than the aforementioned callout noting the former is unsupported on workloops). What is the motivation for preserving both these API vs deprecating dispatch_sync in favor of dispatch_async_and_wait (or functionally subsuming one with the other)?
Credit to Luna for originally posing/inspiring these questions.
Purpose
I want to use launchd to run a shell script asynchronously every minute, but I'm encountering an issue where the job does not run, and I receive the error "Bootstrap failed: 5: Input/output error". I need help identifying the cause of this issue and how to configure launchd correctly.
What I've done
Created the shell script (test_ls_save.sh)
The script is designed to list the contents of the desktop and save the output to a specified directory.
#!/bin/bash
DATE=$(date +%Y-%m-%d_%H-%M-%S)
SAVE_DIR=/Users/test/Desktop/personal/log_gather
FILE_NAME="ls_output_$DATE.log"
ls ~/Desktop > "$SAVE_DIR/$FILE_NAME"
echo "ls output saved to $SAVE_DIR/$FILE_NAME"
File permissions (ls -l output): -rwxr-xr-x 1 test staff 1234 Feb 17 10:00 /Users/test/Desktop/personal/log_gather/exec/test_ls_save.sh
Created the launchd plist file (com.test.logTest.plist)
The plist file is configured to execute the shell script every minute.
<key>Label</key>
<string>com.test.logTest</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
/Users/test/Desktop/personal/log_gather/exec/test_ls_save.sh
</array>
<key>StartInterval</key>
<integer>60</integer> <!-- Run every minute -->
File permissions (ls -l output): -rwxr-xr-x 1 test staff 512 Feb 17 10:00 /Users/test/Library/LaunchAgents/com.test.logTest.plist
Ran the job with launchctl
I used the following command to load the plist file into launchd:
sudo launchctl bootstrap gui/$(id -u) /Users/test/Library/LaunchAgents/com.test.logTest.plist
pc spec
MacBook Pro
Apple M1
16 GB RAM
macOS 15.3 (Build 24D60)
what I know
The configuration has been set, but the launchd job is not running every minute as expected.
I don't believe there is a mistake with the path.
When I check the job using launchctl list, the job does not appear in the list.
I don't know where the error log files are supposed to be. I checked /var/log/system.log, but there are no error logs.
The .sh file runs fine by itself, but it cannot be executed via launchctl.
Want to ask
What could be the cause of the launchd job not running as expected?
Also, is there a way to check where the logs are being output?
If there is an error in the plist file configuration, which part should be modified?
Specifically, what improvements should be made regarding environment variables and path settings?
If my information is not enough, please tell me what is not enough!
If I create a BGContinuedProcessingTaskRequest, register it, and then "do work" within it appropriately reporting progress, and before my task has finished doing all the work it had to do, its expirationHandler triggers...
does the task later try again?
Or does it lose the execution opportunity until the app is next re-launched to the foreground?
In my testing, I never saw my task execute again once expired (which suggests the latter?).
I was able to easily force this expiry by starting my task, backgrounding my app, then launching the iOS Camera App. My example is just using test code inspired from https://developer.apple.com/documentation/backgroundtasks/performing-long-running-tasks-on-ios-and-ipados
let request = BGContinuedProcessingTaskRequest(identifier: taskIdentifier, title: "Video Upload", subtitle: "Starting Upload")
request.strategy = .queue
BGTaskScheduler.shared.register(forTaskWithIdentifier: taskIdentifier, using: nil) { task in
guard let task = task as? BGContinuedProcessingTask else { return }
print("i am a good task")
var wasExpired = false
task.expirationHandler = {
wasExpired = true
}
let progress = task.progress
progress.totalUnitCount = 100
while !progress.isFinished && !wasExpired {
progress.completedUnitCount += 1
let formattedProgress = String(format: "%.2f", progress.fractionCompleted * 100)
task.updateTitle(task.title, subtitle: "Completed \(formattedProgress)%")
sleep(1)
}
if progress.isFinished {
print ("i was a good task")
task.setTaskCompleted(success: true)
} else {
print("i was not a good task")
task.setTaskCompleted(success: false)
}
}
try? BGTaskScheduler.shared.submit(request)
Apologies if this is clearly stated somewhere and I'm missing it.
I have an XPC server running on macOS and want to perform comprehensive performance and load testing to evaluate its efficiency, responsiveness, and scalability. Specifically, I need to measure factors such as request latency, throughput, and how well it handles concurrent connections under different load conditions.
What are the best tools, frameworks, or methodologies for testing an XPC service? Additionally, are there any best practices for simulating real-world usage scenarios and identifying potential bottlenecks?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
XPC
Endpoint Security
Instruments
Performance
An XPC service’s process has a system-managed lifecycle: the process is launched on-demand when another process tries to connect to it, and the system can decide to kill it when system resources are low. XPC services can tell the system when they shouldn’t be killed using xpc_transaction_begin/end.
Do extensions created with ExtensionFoundation and/or ExtensionKit have the same behavior?
Will App be terminated or suspended when updating to incremental version from app store?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
App Store
Core Location
Background Tasks
我在开发 Mac应用完成 后 通过Xcode 上传二进制文件的过程中, 出现了错误, 错误提示: App里面用到的 com.apple.security.application-groups 权限里面 有 group.*** 和 开发者组ID.*** 导致校验失败, 当我单独使用 group.xxx的时候, 我的程序会崩溃 , 因为里面用到了 MachPortRende 进程间通信问题, 这里默认了 开发者组ID.*** 这个路径, 错误详情: 在尝试启动 QuickFox 应用时,程序因权限问题而崩溃。具体的错误信息 bootstrap_check_in 组ID.xxxx.MachPortRendezvousServer.82392: Permission denied (1100) 显示,应用在尝试使用 Mach 端口进行进程间通信时,没有获得足够的权限, 因此 我需要您们的帮助, 如果单独用开发者组ID.*** 我们又没有权限 将数据写入 组ID.xxx里面的文件
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Entitlements
Inter-process communication
First, our app communicates with our blood glucose monitor (CGM) using Bluetooth Low Energy (BLE).
On an iPhone 14 Pro with iOS 26.0.1, Bluetooth communication works properly even when the app is in the background and locked. Even if the phone and CGM are disconnected, the app continues to scan in the background and reconnects when the phone and CGM are back in close proximity. It won't be dormant in the background or when the screen is locked. This effectively ensures that diabetic users can monitor their blood glucose levels in real time.
However, after using iOS 26.0.1 on the iPhone 17, we've received user feedback about frequent disconnections in the background. Our logs indicate that Bluetooth communication is easily disconnected when switching to the background, and then easily dormant by the system, especially when the user's screen is locked. This situation significantly impacts users' blood glucose monitoring, and users are unacceptable. What can be done?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
IOBluetooth
Background Tasks
Core Bluetooth
Are there any plans to add RBI support (the sending keyword) to the OSAllocatedLock interface? So it could be used with non-sendable objects without surrendering to the unchecked API
After logging in to the main App, turn on screen recording, then switch to the interface of another App to perform operations. After about ten-odd minutes, when returning to the main App, it was found that the app was forcefully quit by the system, and subsequent operations could not be carried out.
i am trying to create a daemon with xpc for my app by referring to https://github.com/alienator88/HelperToolApp but i keep getting XPC remote proxy error: Couldn’t communicate with a helper application. All the identifiers all correct but the helper code is not reached.
Hi,
I have requirement in iOS where application needs to run in the background
It can be a simple hello world program running in the background.
could you shed some light on what is the expected behaviour and is it allowed in iOS.
Hi,
I'm trying to create a launch daemon that uses XPC to receive requests from an unprivileged app. Ultimately both components will be written in Go. For now I'm trying to write a PoC in Objective-C to make sure I get everything right, so I'm compiling / signing from the CLI, and writing plist files by hand -- I'm not using XCode.
My current daemon code is pretty much the same as the boilerplate code that XCode generates when creating a new 'XPC Service':
#import <stdio.h>
#include <xpc/xpc.h>
int main(int argc, char *argv[]) {
xpc_rich_error_t error;
dispatch_queue_t queue = dispatch_queue_create("com.foobar.daemon", DISPATCH_QUEUE_SERIAL);
xpc_listener_t listener = xpc_listener_create(
"com.foobar.daemon",
queue,
XPC_LISTENER_CREATE_NONE,
^(xpc_session_t _Nonnull peer) {
xpc_session_set_incoming_message_handler(peer, ^(xpc_object_t _Nonnull message) {
int64_t firstNumber = xpc_dictionary_get_int64(message, "firstNumber");
int64_t secondNumber = xpc_dictionary_get_int64(message, "secondNumber");
// Create a reply and send it back to the client.
xpc_object_t reply = xpc_dictionary_create_reply(message);
xpc_dictionary_set_int64(reply, "result", firstNumber + secondNumber);
xpc_rich_error_t replyError = xpc_session_send_message(peer, reply);
if (replyError) {
printf("Reply failed, error: %s", xpc_rich_error_copy_description(replyError));
}
});
},
&error);
if (error != NULL) {
printf("ERROR: %s\n", xpc_rich_error_copy_description(error));
exit(1);
}
printf("Created listener: %s", xpc_listener_copy_description(listener));
// Resuming the serviceListener starts this service. This method does not return.
dispatch_main();
return 0;
}
I'm compiling, signing and installing my daemon with the following commands:
build_foobar() {
clang -Wall -x objective-c -o com.foobar.daemon poc/main.m
codesign --force --verify --verbose --options=runtime \
--identifier="com.foobar.daemon" \
--sign="Mac Developer: Albin Kerouanton (XYZ)" \
--entitlements=poc/entitlements.plist \
com.foobar.daemon
}
install_foobar() {
sudo cp com.foobar.daemon /Library/PrivilegedHelperTools/com.foobar.daemon
sudo cp poc/com.foobar.daemon.plist /Library/LaunchDaemons/com.foobar.daemon.plist
sudo launchctl bootout system/com.foobar.daemon || true
sudo launchctl bootstrap system /Library/LaunchDaemons/com.foobar.daemon.plist
}
Here's the content of my entitlements.plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>ABCD.com.foobar.daemon</string>
</dict>
</plist>
And finally, here's my launchd plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.foobar.daemon</string>
<key>Program</key>
<string>/Library/PrivilegedHelperTools/com.foobar.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/Library/PrivilegedHelperTools/com.foobar.daemon</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StandardOutPath</key>
<string>/tmp/com.foobar.daemon.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/com.foobar.daemon.err.log</string>
<key>Debug</key>
<true/>
</dict>
</plist>
Whenever I start my service using sudo launchctl start com.foobar.daemon, it exits with the following error message:
ERROR: Unable to activate listener: failed at listener activation with error 1 - Operation not permitted
System logs don't show anything interesting -- they're just repeating the same error message. I tried to add / remove some properties from both the entitlement and the launchd plist file but to no avail.
Any idea what's going wrong?
I'm using flutter's inappwebview. All functions work on my simulator and phone, but when I submit for review, I get an answer saying infinite loading. How did it happen? I don't know what the problem is.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
We are seeing a strange lifecycle issue on multiple MDM-managed iPads where
application(_:didFinishLaunchingWithOptions:) is not called after the device is idle overnight.
Even if we terminate the app manually via the app switcher, the next morning the system does not perform a cold launch. Instead, the app resumes directly in:
applicationDidBecomeActive(_:)
This causes all initialization logic that depends on didFinishLaunching to be completely skipped.
This behavior is consistent across four different supervised MDM devices.
Environment
Devices: iPads enrolled in MDM (supervised)
iOS version: 18.3
Xcode: 16.4
macOS: Sequoia 15.7.2
App type: Standard UIKit iOS app
App: Salux Audiometer (App Store app)
Expected Behavior
If the app was terminated manually using the app switcher, the next launch should:
Start a new process
Trigger application(_:didFinishLaunchingWithOptions:)
Follow the normal cold-start lifecycle
Actual Behavior
After leaving the iPad idle overnight (8–12 hours):
The next launch skips didFinishLaunching
The app resumes directly in applicationDidBecomeActive
No new process is started
App behaves as if it had been suspended, even though it was manually terminated
Logs (Relevant Extracts)
Day 1 — Normal cold launch
[12:06:44.152 PM] PROCESS_STARTED
[12:06:44.214 PM] DID_FINISH_LAUNCHING_START launchOptions=[]
[12:06:44.448 PM] DID_FINISH_LAUNCHING_END
We then used the app and terminated it via app switcher.
Day 2 — Unexpected resume without cold start
[12:57:49.328 PM] APP_DID_BECOME_ACTIVE
No PROCESS_STARTED
No didFinishLaunching
No cold-start logs
This means the OS resumed the app from a previous state that should not exist.
Reproducible Steps
Use an MDM-enrolled iPad.
Launch the app normally.
Terminate it manually via the multitasking app switcher.
Leave the device idle overnight (8–12 hours).
Launch the app the next morning.
Observe that:
didFinishLaunching does not fire
applicationDidBecomeActive fires directly
Questions for Apple Engineers / Community
Is this expected behavior on MDM-supervised devices in iOS 18?
Are there any known OS-level changes where terminated apps may be revived from disk/memory?
Could MDM restrictions or background restoration policies override app termination?
How can we ensure that our app always performs a clean initialization when launched after a long idle period?
Additional Information
We have full logs from four separate MDM iPads showing identical behavior.
Happy to share a minimal reproducible sample if required.
Issue:
Background downloads using the flutter_downloader package work perfectly in debug mode and release mode when run directly from Xcode (plugged in).
However, when I create an archive build and install the app separately (via TestFlight or direct IPA install), the background download stops working as soon as the app is minimized.
✅ What I’ve already done
Info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
<string>fetch</string>
<string>processing</string>
<string>audio</string>
<string>push-to-talk</string>
</array>
AppDelegate.swift
import UIKit
import Flutter
import Firebase
import flutter_downloader
import BackgroundTasks
@main
@objc class AppDelegate: FlutterAppDelegate {
static let backgroundChannel = "com.example.app/background_service"
private var backgroundCompletionHandler: (() -> Void)?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
}
if #available(iOS 13.0, *) {
registerBackgroundTask()
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
@available(iOS 13.0, *)
private func registerBackgroundTask() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.app.process_download_queue",
using: nil
) { [weak self] task in
guard let self = self else { return }
self.handleDownloadQueueTask(task: task as! BGProcessingTask)
}
}
@available(iOS 13.0, *)
private func handleDownloadQueueTask(task: BGProcessingTask) {
scheduleNextDownloadTask()
let headlessEngine = FlutterEngine(name: "BackgroundTaskEngine", project: nil, allowHeadlessExecution: true)
headlessEngine.run()
let channel = FlutterMethodChannel(
name: AppDelegate.backgroundChannel,
binaryMessenger: headlessEngine.binaryMessenger
)
task.expirationHandler = {
channel.invokeMethod("backgroundTaskExpired", arguments: nil)
}
channel.invokeMethod("processNextInBackground", arguments: nil) { result in
task.setTaskCompleted(success: (result as? Bool) ?? false)
}
}
override func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
) {
self.backgroundCompletionHandler = completionHandler
super.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
}
override func applicationDidEnterBackground(_ application: UIApplication) {
if #available(iOS 13.0, *) {
scheduleNextDownloadTask()
}
}
@available(iOS 10.0, *)
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
if #available(iOS 14.0, *) {
completionHandler([.list, .banner, .badge, .sound])
} else {
completionHandler([.alert, .badge, .sound])
}
}
@available(iOS 10.0, *)
override func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
completionHandler()
}
}
// MARK: - Helper
@available(iOS 13.0, *)
func scheduleNextDownloadTask() {
let request = BGProcessingTaskRequest(identifier: "com.example.app.process_download_queue")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
do {
try BGTaskScheduler.shared.submit(request)
print("BGTask: Download queue processing task scheduled successfully.")
} catch {
print("BGTask: Could not schedule download queue task: \(error)")
}
}
private func registerPlugins(registry: FlutterPluginRegistry) {
if !registry.hasPlugin("FlutterDownloaderPlugin") {
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
}
}
🧩 Observations
Background download works correctly when:
The app is plugged in and run via Xcode (release/debug)
It stops working when:
The app is installed from an archived build (IPA/TestFlight) and minimized
All entitlements and background modes are properly added.
Provisioning profile includes required background modes.
❓Question
Is there any known limitation or signing difference between Xcode run and archived release builds that could cause URLSession background tasks not to trigger?
Has anyone faced a similar issue when using flutter_downloader on iOS 13+ with BGTaskScheduler or URLSession background configuration?
Any help or working setup example for production/TestFlight would be appreciated.
When I search, it's always people trying to do stuff in the background. I want my app to only do stuff when it is active. And this post https://developer.apple.com/forums/thread/685525 seems to have prevented replies from the start. Which means it's just a documentation page and does not belong in the discussion forums at all, because it prevents all discussion.
I've discovered that a system network extension can communicate with a LaunchDaemon (loaded using SMAppService) over XPC, provided that the XPC service name begins with the team ID.
If I move the launchd daemon plist to Contents/Library/LaunchAgents and swap the SMAppService.daemon calls to SMAppService.agent calls, and remove the .privileged option to NSXPCConnection, the system extension receives "Couldn't communicate with a helper application" as an error when trying to reach the LaunchAgent advertised service. Is this limitation by design?
I imagine it is, but wanted to check before I spent any more time on it.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Service Management
XPC
System Extensions
Network Extension
Hello,
We're seeing some strange crashes and noticed the following. It's unclear if related or not.
The contract for xpc_main, which internally calls dispatch_main, is This function never returns. and they are appropriately peppered with __attribute__((__noreturn__)). Documentation states:
This function “parks” the main thread and waits for blocks to be submitted to the main queue.
However, internally, dispatch_main calls pthread_exit. pthread_exit's documentation states that:
After a thread has terminated, the result of access to local (auto)
variables of the thread is undefined. Thus, references to local
variables of the exiting thread should not be used for the
pthread_exit() value_ptr parameter value.
I'd say the two contracts of This function never returns. and thread exiting with its storage released are diametrically opposed and can create nuanced issues.
Consider the following code:
struct asd {
int a;
};
struct asd* ptr;
void fff(void* ctx)
{
while(true)
{
printf("%d\n", ptr->a);
ptr->a = (ptr->a + 1);
usleep(100000);
}
}
int main(int argc, const char * argv[]) {
struct asd zxc;
zxc.a = 1;
ptr = &zxc;
dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), NULL, fff);
dispatch_main();
return 0;
}
This is a gross over-simplification of the code we have, but in the same "spirit". We have a C++ object that is created on the stack and exposes one of its members as a global pointer, with the assumption that it would never release. What I understand from This function never returns is that the calling thread remains dormant and its stack remains alive. What I understand from pthread_exit is that the thread is killed (this is verified with a debugger attached) and its stack storage is released.
Another thing that is throwing me off is that no sanitizer that is provided by clang/Xcode catches this issue. I don't see any special handling of the internal pthread_t in libdispatch to keep the stack storage alive.
Our code is more complex, but can be solved by allocating the initial object on the heap, rather than on the stack. But still I would like to understand if this is the expected behavior. Perhaps my preconception of __attribute__((__noreturn__)) is wrong, and accessing stack variables post call to a __attribute__((__noreturn__)) function is UB?
Thanks
Topic:
App & System Services
SubTopic:
Processes & Concurrency