Improve implementation for the creation of the background process #277

Closed
opened 2026-10-05 04:09:26 +02:00 by adam · 11 comments
Owner

I see in a source file that async-signal-unsafe functions are used between calls of the functions "fork" and "execvp". Would you like to improve your program design if you consider information like the following?

... Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. ...
I see in [a source file](https://github.com/znc/znc/blob/b2dcad5fd49e6c0d604de38c311db0e08ed061a7/src/main.cpp#L286) that [async-signal-unsafe functions](https://www.securecoding.cert.org/confluence/display/cplusplus/SIG30-CPP.+Call+only+asynchronous-safe+functions+within+signal+handlers#SIG30-CPP.Callonlyasynchronous-safefunctionswithinsignalhandlers-ImplementationDetails) are used between calls of the functions "fork" and "execvp". Would you like to improve your program design if you consider information like [the following](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)? ``` ... Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. ... ``` <!-- github-sync-id:23154489 -->
adam closed this issue 2026-10-05 04:09:26 +02:00
Author
Owner

There is only 1 thread at that point...

There is only 1 thread at that point... <!-- github-comment-id:29097755 -->
Author
Owner

I find that the issue "async-signal-safety" is independent from the thread count if you care for better software portability, isn't it?

I find that the issue "async-signal-safety" is independent from the thread count if you care for better software portability, isn't it? <!-- github-comment-id:29098899 -->
Author
Owner

@elfring Your quote is from this paragraph from your link:

A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls.

This is about multi-threaded process calling fork(). May you elaborate what's wrong with single-threaded process calling fork() and not exec(), please?

@elfring Your quote is from this paragraph from [your link](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html): > A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls. This is about multi-threaded process calling fork(). May you elaborate what's wrong with single-threaded process calling fork() and not exec(), please? <!-- github-comment-id:29128427 -->
Author
Owner

Do you interpret the shown wording "... If a multi-threaded process calls ..." in the way that it is not specified which behaviour should be expected for the address space in an ordinary single-threaded process?

I find that the POSIX specification contains a limitation about which statements can be executed between calls of the functions "fork" and "execvp" in a portable way. More threads will increase the technical challenges to keep the process memory consistent, won't it?
Does this standard try to point that out by the wording "... including the states of mutexes ..." (and in the section "RATIONALE") here?

Do you interpret the shown wording "... If a multi-threaded process calls ..." in the way that it is not specified which behaviour should be expected for the address space in an ordinary single-threaded process? I find that the POSIX specification contains a limitation about which statements can be executed between calls of the functions "fork" and "execvp" in a portable way. More threads will increase the technical challenges to keep the process memory consistent, won't it? Does this standard try to point that out by the wording "... including the states of mutexes ..." (and in the section "RATIONALE") here? <!-- github-comment-id:29129292 -->
Author
Owner

23.11.2013 13:47, Markus Elfring пишет:

Do you interpret the shown wording "... If a multi-threaded process
calls ..." in the way that it is not specified which behaviour should
be expected for the address space in an ordinary single-threaded process?

No. I interpret this like "the paragraph applies only for multi-threaded
processes".

When fork() is called in a multi-threaded process, at that moment
other threads may be in any state. For example, it could be writing to
some structure, but didn't finish yet. In the child process, all those
threads are simply gone, leaving memory of the child in some
inconsistent state. Memory of the parent is still consistent, because in
the parent those threads are still running and they have a chance to
finish whatever they did.
That's why in a multi-threaded process only safe functions may be called
after fork(). exec() clears memory address space, and starts new
program in this process from scratch, so in the new program everything
is fine again.

This issue is not presented in a single-threaded process, because we
have only 1 thread - the one which calls fork(), and we know what the
thread is doing: it's calling fork() (surprise! ;) ). No other thread
disappears in child leaving memory in some inconsistent state, because
no other thread exists at all.

I find that the POSIX specification contains a limitation about which
statements can be executed between calls of the functions "fork" and
"execvp" in a portable way. More threads will increase the technical
challenges to keep the process memory consistent, won't it?

More threads will create those technical challenges. I don't see any
challenge with one thread.

Does this standard try to point that out by the wording "... including
the states of mutexes ..." (and in the section "RATIONALE") here?

If another thread was accessing the mutex during fork(), the mutex may
be left in an unusable state. This implies that such thread exists. If
no thread does that, there is no problem. And in a single-threaded
program we know that no other thread accesses mutexes or any other memory.

23.11.2013 13:47, Markus Elfring пишет: > Do you interpret the shown wording "... If a multi-threaded process > calls ..." in the way that it is not specified which behaviour should > be expected for the address space in an ordinary single-threaded process? No. I interpret this like "the paragraph applies only for multi-threaded processes". When `fork()` is called in a multi-threaded process, at that moment other threads may be in any state. For example, it could be writing to some structure, but didn't finish yet. In the child process, all those threads are simply gone, leaving memory of the child in some inconsistent state. Memory of the parent is still consistent, because in the parent those threads are still running and they have a chance to finish whatever they did. That's why in a multi-threaded process only safe functions may be called after `fork()`. `exec()` clears memory address space, and starts new program in this process from scratch, so in the new program everything is fine again. This issue is not presented in a single-threaded process, because we have only 1 thread - the one which calls `fork()`, and we know what the thread is doing: it's calling `fork()` (surprise! ;) ). No other thread disappears in child leaving memory in some inconsistent state, because no other thread exists at all. > I find that the POSIX specification contains a limitation about which > statements can be executed between calls of the functions "fork" and > "execvp" in a portable way. More threads will increase the technical > challenges to keep the process memory consistent, won't it? More threads will _create_ those technical challenges. I don't see any challenge with one thread. > Does this standard try to point that out by the wording "... including > the states of mutexes ..." (and in the section "RATIONALE") here? If another thread was accessing the mutex during `fork()`, the mutex may be left in an unusable state. This implies that such thread exists. If no thread does that, there is no problem. And in a single-threaded program we know that no other thread accesses mutexes or any other memory. <!-- github-comment-id:29130326 -->
Author
Owner

This issue might become more interesting if you would like to add another thread for the signal handler fix. ;-)

This issue might become more interesting if you would like to add another thread for [the signal handler fix](https://github.com/znc/znc/issues/434). ;-) <!-- github-comment-id:29131029 -->
Author
Owner

How it is even related?..

How it is even related?.. <!-- github-comment-id:29131187 -->
Author
Owner

Wait, what? Threads for signal-handling?!?

Also, what exactly are we arguing about? The file access through CFile should be safe (open, write, close, fcntl are in the list). That only leaves fprintf() and fflush() to worry about. So the fix would be to bypass the I/O library and use write() directly to output these messages and everyone is happy?

Wait, what? Threads for signal-handling?!? Also, what exactly are we arguing about? The file access through CFile should be safe (open, write, close, fcntl are in the list). That only leaves fprintf() and fflush() to worry about. So the fix would be to bypass the I/O library and use write() directly to output these messages and everyone is happy? <!-- github-comment-id:29131202 -->
Author
Owner

Oh, wait. Nothing can sanely create threads at that point anyway. The parent process is going to exit() soon and the child process survives. So anything creating threads here is already broken and agree with DarthGandalf that I can't think of any realistic problems.

Oh, wait. Nothing can sanely create threads at that point anyway. The parent process is going to exit() soon and the child process survives. So anything creating threads here is already broken and agree with DarthGandalf that I can't think of any realistic problems. <!-- github-comment-id:29131259 -->
Author
Owner

Should the function "_exit" be eventually called instead of "exit" from the single-threaded child process?

Should the function "_exit" be eventually called instead of "exit" from the single-threaded child process? <!-- github-comment-id:29150841 -->
Author
Owner

Why?

Why? <!-- github-comment-id:29159812 -->
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adam/znc#277