git.cappig.dev / apheleiaOS.git master

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
}