7static int64_t microseconds(
struct timeval time) {
8 return (int64_t)time.tv_sec * 1000000 + time.tv_usec;
11static int repeated_exit(
void) {
12 int failed = 0, status;
13 struct cw_child child = CW_CHILD_INIT;
15 struct rusage before, middle, after, first,
usage;
16 memset(&before, 0xa5,
sizeof(before));
17 memset(&middle, 0xa5,
sizeof(middle));
18 memset(&after, 0xa5,
sizeof(after));
19 memset(&first, 0xa5,
sizeof(first));
20 CHECK(getrusage(RUSAGE_CHILDREN, &before) == 0);
21 CHECK(cw_spawn(&child, -1, (uid_t)-1, 83) == 0);
22 CHECK(cw_send(child.command,
'B') == 0 && cw_receive(child.report,
'B') == 0);
23 CHECK(cw_send(child.command,
'E') == 0);
24 CHECK(cw_raw_waitid(P_PID, child.pid, &info, WEXITED | WNOWAIT, &first) == 0);
25 CHECK(cw_info(&info, child.pid, getuid(), CLD_EXITED, 83) == 0 && !cw_usage(&first));
26 for (
int i = 0; i < 4; ++i) {
28 CHECK(cw_raw_waitid(P_PID, child.pid, &info, WEXITED | WNOWAIT, &
usage) == 0);
29 CHECK(cw_info(&info, child.pid, getuid(), CLD_EXITED, 83) == 0);
30 CHECK(memcmp(&
usage, &first, CW_RUSAGE_BYTES) == 0 && !cw_usage(&
usage));
32 CHECK(getrusage(RUSAGE_CHILDREN, &middle) == 0);
33 CHECK(microseconds(before.ru_utime) == microseconds(middle.ru_utime));
34 CHECK(microseconds(before.ru_stime) == microseconds(middle.ru_stime));
36 CHECK(wait4(child.pid, &status, 0, &
usage) == child.pid);
38 CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 83);
39 CHECK(memcmp(&
usage, &first, CW_RUSAGE_BYTES) == 0 && !cw_usage(&
usage));
40 CHECK(getrusage(RUSAGE_CHILDREN, &after) == 0);
41 const int64_t user_delta = microseconds(after.ru_utime) - microseconds(before.ru_utime);
42 const int64_t system_delta = microseconds(after.ru_stime) - microseconds(before.ru_stime);
43 CHECK(user_delta >= microseconds(
usage.ru_utime) &&
44 user_delta <= microseconds(
usage.ru_utime) + 1);
45 CHECK(system_delta >= microseconds(
usage.ru_stime) &&
46 system_delta <= microseconds(
usage.ru_stime) + 1);
56 atomic_uint ready, done;
62static void* compete(
void* argument) {
64 atomic_store(&
waiter->ready, 1);
65 if (cw_atomic_wait(
waiter->start, 1)) {
73 atomic_store(&
waiter->done, 1);
77static int competing_reapers(
void) {
78 int failed = 0, created = 0;
79 struct cw_child child = CW_CHILD_INIT;
81 atomic_uint start = 0;
82 struct contender contenders[2] = {{.use_wait4 = 0, .start = &start},
83 {.use_wait4 = 1, .start = &start}};
84 CHECK(cw_spawn(&child, -1, (uid_t)-1, 87) == 0);
85 for (
int i = 0; i < 2; ++i) {
86 contenders[i].child = child.pid;
87 CHECK(pthread_create(&threads[i], NULL, compete, &contenders[i]) == 0);
89 CHECK(cw_atomic_wait(&contenders[i].ready, 1) == 0);
91 atomic_store(&start, 1);
92 CHECK(cw_send(child.command,
'E') == 0);
93 CHECK(cw_atomic_wait(&contenders[0].done, 1) == 0 && cw_atomic_wait(&contenders[1].done, 1) == 0);
94 pid_t pid = child.pid;
96 CHECK((contenders[0].result == 0) + (contenders[1].result == pid) == 1);
97 for (
int i = 0; i < 2; ++i) {
98 if (contenders[i].result < 0)
99 CHECK(contenders[i].result == -1 && contenders[i].error == ECHILD);
101 CHECK(cw_info(&contenders[i].info, pid, getuid(), CLD_EXITED, 87) == 0);
103 CHECK(WIFEXITED(contenders[i].status) && WEXITSTATUS(contenders[i].status) == 87);
105 CHECK(waitpid(pid, NULL, WNOHANG) == -1 && errno == ECHILD);
107 atomic_store(&start, 1);
109 for (
int i = 0; i < created; ++i)
110 if (pthread_join(threads[i], NULL))
120static void* sibling_fork(
void* argument) {
121 struct sibling* state = argument;
122 state->result = cw_spawn(&state->child, -1, (uid_t)-1, 91);
126static int sibling_child(
void) {
127 int failed = 0, started = 0;
129 struct sibling state = {.child = CW_CHILD_INIT, .result = -1};
131 CHECK(pthread_create(&
creator, NULL, sibling_fork, &state) == 0);
133 CHECK(pthread_join(
creator, NULL) == 0);
135 CHECK(state.result == 0 && cw_send(state.child.command,
'E') == 0);
136 CHECK(waitid(P_PID, state.child.pid, &info, WEXITED | WNOWAIT) == 0);
137 CHECK(cw_info(&info, state.child.pid, getuid(), CLD_EXITED, 91) == 0);
138 CHECK(waitid(P_ALL, 0, &info, WEXITED) == 0 && info.si_pid == state.child.pid);
139 state.child.pid = -1;
143 cw_cleanup(&state.child);
147int cw_lifetime(
void) {
148 return repeated_exit() || competing_reapers() || sibling_child();