1
#include "internal.h"
2
3
pid_t sched_wait(pid_t pid, int *status) {
4
return sched_waitpid(pid, status, 0);
5
}6
7
static bool waitpid_target_matches(const sched_thread_t *parent, const sched_thread_t *child, pid_t pid) {
8
if (!parent || !child || !child->user_thread) {
9
return false;
10
}11
12
if (child->ppid != parent->pid) {
13
return false;
14
}15
16
if (pid > 0) {
17
return child->pid == pid;
18
}19
20
if (!pid) {
21
// pid 0 waits within the caller's process group
22
return child->pgid == parent->pgid;
23
}24
25
if (pid == -1) {
26
return true;
27
}28
29
return child->pgid == -pid;
30
}31
32
static sched_thread_t *waitpid_find_zombie(const sched_thread_t *self, pid_t pid) {
33
ll_foreach(node, sched_state.procs.zombie_list) {
34
sched_thread_t *thread = node->data;
35
36
if (waitpid_target_matches(self, thread, pid)) {
37
return thread;
38
}39
}40
41
return NULL;
42
}43
44
static sched_thread_t *waitpid_find_stopped(const sched_thread_t *self, pid_t pid, bool *has_child) {
45
ll_foreach(node, sched_state.procs.all_list) {
46
sched_thread_t *thread = node->data;
47
48
if (!waitpid_target_matches(self, thread, pid)) {
49
continue;
50
}51
52
if (has_child) {
53
*has_child = true;
54
}55
56
bool stopped_child = (thread_get_state(thread) == THREAD_STOPPED && !thread->stop_reported);
57
58
if (stopped_child) {
59
return thread;
60
}61
}62
63
return NULL;
64
}65
66
static bool waitpid_has_child(const sched_thread_t *self, pid_t pid) {
67
ll_foreach(node, sched_state.procs.all_list) {
68
sched_thread_t *thread = node->data;
69
70
if (waitpid_target_matches(self, thread, pid)) {
71
return true;
72
}73
}74
75
return false;
76
}77
78
static int waitpid_status(const sched_thread_t *thread) {
79
if (thread->exit_signal) {
80
return thread->exit_signal & 0x7f;
81
}82
83
int code = thread->exit_code;
84
85
if (code < 0) {
86
code = 1;
87
}88
89
return (code & 0xff) << 8;
90
}91
92
static void waitpid_add_ticks(u64 *counter, u64 ticks) {
93
if (!counter || !ticks) {
94
return;
95
}96
97
u64 current = *counter;
98
u64 next = current + ticks;
99
100
if (next < current) {
101
next = UINT64_MAX;
102
}103
104
*counter = next;
105
}106
107
static u64 waitpid_sum_ticks(u64 own_ticks, u64 child_ticks) {
108
u64 total_ticks = own_ticks + child_ticks;
109
110
if (total_ticks < own_ticks) {
111
return UINT64_MAX;
112
}113
114
return total_ticks;
115
}116
117
static void waitpid_charge_child_time(sched_thread_t *parent, const sched_thread_t *child) {
118
if (!parent || !child) {
119
return;
120
}121
122
u64 own_ticks = __atomic_load_n(&child->cpu_time_ticks, __ATOMIC_RELAXED);
123
u64 child_ticks = __atomic_load_n(&child->child_cpu_time_ticks, __ATOMIC_RELAXED);
124
// waited children roll their accumulated descendants into the parent totals
125
waitpid_add_ticks(&parent->child_cpu_time_ticks, waitpid_sum_ticks(own_ticks, child_ticks));
126
127
u64 own_user = __atomic_load_n(&child->user_ticks, __ATOMIC_RELAXED);
128
u64 child_user = __atomic_load_n(&child->child_user_ticks, __ATOMIC_RELAXED);
129
waitpid_add_ticks(&parent->child_user_ticks, waitpid_sum_ticks(own_user, child_user));
130
131
u64 own_sys = __atomic_load_n(&child->sys_ticks, __ATOMIC_RELAXED);
132
u64 child_sys = __atomic_load_n(&child->child_sys_ticks, __ATOMIC_RELAXED);
133
waitpid_add_ticks(&parent->child_sys_ticks, waitpid_sum_ticks(own_sys, child_sys));
134
}135
136
pid_t sched_waitpid(pid_t pid, int *status, int options) {
137
sched_thread_t *self = sched_local_current();
138
139
if (!self || !self->user_thread) {
140
return -ECHILD;
141
}142
143
if (options & ~(WNOHANG | WUNTRACED)) {
144
return -EINVAL;
145
}146
147
for (;;) {
148
sched_thread_t *found = NULL;
149
sched_thread_t *stopped = NULL;
150
sched_thread_t *active_zombie = NULL;
151
bool has_matching_child = false;
152
153
unsigned long flags = sched_lock_save();
154
155
if (!sched_state.procs.zombie_list || !sched_state.procs.all_list) {
156
sched_lock_restore(flags);
157
return -ECHILD;
158
}159
160
found = waitpid_find_zombie(self, pid);
161
162
if (found && thread_is_owned(found)) {
163
active_zombie = found;
164
found = NULL;
165
}166
167
if (found) {
168
if (status) {
169
*status = waitpid_status(found);
170
}171
172
waitpid_charge_child_time(self, found);
173
174
list_remove(sched_state.procs.zombie_list, &found->zombie_node);
175
found->in_zombie_list = false;
176
177
sched_lock_restore(flags);
178
179
pid_t ret = found->pid;
180
thread_cleanup(found);
181
thread_put(found);
182
183
return ret;
184
}185
186
if (options & WUNTRACED) {
187
stopped = waitpid_find_stopped(self, pid, &has_matching_child);
188
189
if (stopped) {
190
stopped->stop_reported = true;
191
192
if (status) {
193
*status = 0x7f | ((stopped->stop_signal & 0xff) << 8);
194
}195
196
sched_lock_restore(flags);
197
return stopped->pid;
198
}199
} else {
200
has_matching_child = waitpid_has_child(self, pid);
201
}202
203
if (!has_matching_child) {
204
sched_lock_restore(flags);
205
return -ECHILD;
206
}207
208
if (active_zombie) {
209
if (options & WNOHANG) {
210
sched_lock_restore(flags);
211
return 0;
212
}213
214
sched_lock_restore(flags);
215
216
while (thread_is_owned(active_zombie)) {
217
force_resched();
218
sched_spin_wait();
219
}220
221
continue;
222
}223
224
if (options & WNOHANG) {
225
sched_lock_restore(flags);
226
return 0;
227
}228
229
if (!sched_running_get()) {
230
sched_lock_restore(flags);
231
return -ECHILD;
232
}233
234
u32 wait_seq = sched_wait_seq(&self->wait_queue);
235
sched_lock_restore(flags);
236
237
if (!arch_timer_ticks()) {
238
arch_cpu_wait();
239
sched_yield();
240
continue;
241
}242
243
sched_wait_result_t wait_result = sched_wait_on_queue(&self->wait_queue, wait_seq, 0, SCHED_WAIT_INTERRUPTIBLE);
244
245
if (wait_result == SCHED_WAIT_INTR) {
246
return -EINTR;
247
}248
249
if (wait_result == SCHED_WAIT_ABORTED && sched_running_get()) {
250
sched_yield();
251
}252
}253
}