I will provide the release notes for the Leopard CoreFoundation Framework. I donโt know if they are all online, because Apple is more likely to replace than extend the Core Foundation release notes.
CoreFoundation and fork ()
Due to the behavior of fork (), CoreFoundation cannot be used on the child side of fork (). If you are fork (), you must follow this with exec * (), and you should not use the CoreFoundation APIs inside the child element, before exec * (). This applies to all higher levels of the APIs that use CoreFoundation, and since you cannot know what these higher level APIs are and whether they use the CoreFoundation API, you should not use the higher level APIs either. This includes the use of the daemon () function.
In addition, for POSIX, only functions that are protected from asynchronization are safe to use on the child side of fork (), so even using a lower level libSystem / BSD / UNIX APIs should be minimized and ideally only asynchronous undo functions.
This has always been true, and notes have been made about this on Cocoa's various developer lists in the past. But CoreFoundation is now taking more drastic measures to โforceโ the restriction, so we thought it was worth adding a note to the release to call it as well. A message is written to stderr when something uses an API that is not known to be safe in CoreFoundation after fork (). However, if the file descriptor 2 has been closed, you will not receive a message or notification, which is too bad. We tried to make the processes end in a very recognizable way, and for some time it was very convenient, but backward binary compatibility prevented us from doing this.
In other words, it was never safe to do anything on the child side of fork() , except exec the new program.
In addition to the general POSIX ban, the often-cited explanation is as follows: a) almost all Cocoa programs are multithreaded these days, with GCD, etc. B) when you fork() , only the calling thread survives in the child process; other threads just disappear. Because these threads could manipulate shared resources, the child process cannot rely on a health state. For example, an implementation of malloc() may have a lock to protect common structures and that the lock could be kept by one of the currently running threads during fork() . So, if the remaining thread tries to allocate memory, it can hang indefinitely. Other common data structures may simply be in a damaged state. Etc.