1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
/*
fork on steroids to avoid SIGCHLD and waitpid
Copyright (C) Stefan Metzmacher 2010
Copyright (C) Ralph Boehme 2017
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIB_UTIL_TFORK_H
#define LIB_UTIL_TFORK_H
struct tfork;
/**
* @brief a fork() that avoids SIGCHLD and waitpid
*
* This function is a solution to the problem of fork() requiring special
* preparations in the caller to handle SIGCHLD signals and to reap the child by
* wait()ing for it.
*
* The advantage over fork() is that the child process termination is signalled
* to the caller by making a pipe fd readable returned by tfork_event_fd(), in
* which case the exit status of the child can be fetched with tfork_status()
* without blocking.
*
* The child process will start with SIGCHLD handler set to SIG_DFL.
*
* @return On success, a struct tfork. NULL on failure.
* Use tfork_worker_pid() to get the pid of the created
* child and tfork_event_fd() to get the file descriptor
* that can be used to poll for process termination and
* reading the child process exit status.
*
* @note There's one thing this thing can't protect us against and that is if a
* process installs a SIGCHLD handler from one thread while another thread is
* running inside tfork_create() or tfork_status() and the signal handler
* doesn't forward signals for exited children it didn't fork, ie our children.
**/
struct tfork *tfork_create(void);
/**
* @brief Return the child pid from tfork_create()
*
* @param[in] t Pointer to struct tfork returned by tfork_create()
*
* @return In the caller this returns the pid of the child,
* in the child this returns 0.
**/
pid_t tfork_child_pid(const struct tfork *t);
/**
* @brief Return an event fd that signals child termination
*
* @param[in] t Pointer to struct tfork returned by tfork_create()
*
* It is the callers responsibility to ensure that the event fd returned by
* tfork_event_fd() is closed. By calling tfork_event_fd() ownership of the fd
* is transferred to the caller, calling tfork_event_fd() again will trigger an
* abort().
*
* @return An fd that becomes readable when the child created with
* tfork_create() terminates. It is guaranteed that a
* subsequent call to tfork_status() will not block and return
* the exit status of the child.
**/
int tfork_event_fd(struct tfork *t);
/**
* @brief Wait for the child to terminate and return its exit status
*
* @param[in] t Pointer-pointer to a struct tfork returned by
* tfork_create(). Upon successful completion t is freed and
* set to NULL.
*
* @param[in] wait Whether to wait for the child to change state. If wait is
* false, and the child hasn't changed state, tfork_status()
* will return -1 with errno set to EAGAIN. If wait is true,
* tfork_status() will block waiting for the child to change
* runstate.
*
* @return The exit status of the child, -1 on error.
*
* @note We overload the return value a bit, but a process exit status is pretty
* much guaranteed to be a 16-bit int and can't be -1.
**/
int tfork_status(struct tfork **_t, bool wait);
/**
* @brief Terminate the child discarding the exit status
*
* @param[in] t Pointer-pointer to a struct tfork returned by
* tfork_create(). Upon successful completion t is freed and
* set to NULL.
*
* @return 0 on success, -1 on error.
**/
int tfork_destroy(struct tfork **_t);
#endif /* LIB_UTIL_TFORK_H */
|