1 /*
2 * ivykis, an event handling library
3 * Copyright (C) 2010 Lennert Buytenhek
4 * Dedicated to Marija Kulikova.
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License version
8 * 2.1 as published by the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License version 2.1 for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License version 2.1 along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <iv.h>
24 #include <iv_signal.h>
25 #include <iv_tls.h>
26 #include <iv_wait.h>
27 #include "iv_private.h"
28 #include "mutex.h"
29
30 #ifndef WCONTINUED
31 #define WCONTINUED 0
32 #define WIFCONTINUED(x) 0
33 #endif
34
35 #define IV_WAIT_STATUS_DEAD 1
36
37 struct wait_event {
38 struct iv_list_head list;
39 int status;
40 #ifdef HAVE_WAIT4
41 struct rusage rusage;
42 #endif
43 };
44
iv_wait_interest_compare(const struct iv_avl_node * _a,const struct iv_avl_node * _b)45 static int iv_wait_interest_compare(const struct iv_avl_node *_a,
46 const struct iv_avl_node *_b)
47 {
48 const struct iv_wait_interest *a =
49 iv_container_of(_a, struct iv_wait_interest, avl_node);
50 const struct iv_wait_interest *b =
51 iv_container_of(_b, struct iv_wait_interest, avl_node);
52
53 if (a->pid < b->pid)
54 return -1;
55 if (a->pid > b->pid)
56 return 1;
57
58 return 0;
59 }
60
61 static ___mutex_t iv_wait_lock;
62 static struct iv_avl_tree iv_wait_interests =
63 IV_AVL_TREE_INIT(iv_wait_interest_compare);
64
__iv_wait_interest_find(int pid)65 static struct iv_wait_interest *__iv_wait_interest_find(int pid)
66 {
67 struct iv_avl_node *an;
68
69 an = iv_wait_interests.root;
70 while (an != NULL) {
71 struct iv_wait_interest *p;
72
73 p = iv_container_of(an, struct iv_wait_interest, avl_node);
74 if (pid == p->pid)
75 return p;
76
77 if (pid < p->pid)
78 an = an->left;
79 else
80 an = an->right;
81 }
82
83 return NULL;
84 }
85
iv_wait_status_dead(int status)86 static int iv_wait_status_dead(int status)
87 {
88 /*
89 * On FreeBSD, WIFCONTINUED(status) => WIFSIGNALED(status).
90 */
91 if (WIFSIGNALED(status) && !WIFCONTINUED(status))
92 return 1;
93
94 if (WIFEXITED(status))
95 return 1;
96
97 return 0;
98 }
99
iv_wait_got_sigchld(void * _dummy)100 static void iv_wait_got_sigchld(void *_dummy)
101 {
102 ___mutex_lock(&iv_wait_lock);
103 while (1) {
104 pid_t pid;
105 int status;
106 struct wait_event *we;
107 struct iv_wait_interest *p;
108
109 #ifdef HAVE_WAIT4
110 struct rusage rusage;
111
112 #ifdef __digital__
113 union wait w;
114
115 pid = wait4(-1, &w, WNOHANG | WUNTRACED | WCONTINUED, &rusage);
116 status = w.w_status;
117 #else
118 pid = wait4(-1, &status,
119 WNOHANG | WUNTRACED | WCONTINUED, &rusage);
120 #endif
121 if (pid <= 0) {
122 if (pid < 0 && errno != ECHILD)
123 perror("wait4");
124 break;
125 }
126 #else
127 pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);
128 if (pid <= 0) {
129 if (pid < 0 && errno != ECHILD)
130 perror("waitpid");
131 break;
132 }
133 #endif
134
135 we = malloc(sizeof(*we));
136 if (we == NULL)
137 iv_fatal("iv_wait_got_sigchld: out of memory");
138
139 we->status = status;
140 #ifdef HAVE_WAIT4
141 we->rusage = rusage;
142 #endif
143
144 p = __iv_wait_interest_find(pid);
145 if (p != NULL) {
146 iv_list_add_tail(&we->list, &p->events_pending);
147 iv_event_post(&p->ev);
148 } else {
149 free(we);
150 }
151
152 /*
153 * If this pid is now dead, avoid queueing subsequent
154 * process status change events to this interest, as the
155 * pid might be reused between us queueing this event to
156 * the interest and the interest being unregistered,
157 * and events for the new user of this pid would then end
158 * up at the wrong interest.
159 */
160 if (iv_wait_status_dead(status)) {
161 iv_avl_tree_delete(&iv_wait_interests, &p->avl_node);
162 p->flags = IV_WAIT_STATUS_DEAD;
163 }
164 }
165 ___mutex_unlock(&iv_wait_lock);
166 }
167
168 struct iv_wait_thr_info {
169 int wait_count;
170 struct iv_signal sigchld_interest;
171 struct iv_wait_interest *handled_wait_interest;
172 };
173
iv_wait_tls_init_thread(void * _tinfo)174 static void iv_wait_tls_init_thread(void *_tinfo)
175 {
176 struct iv_wait_thr_info *tinfo = _tinfo;
177
178 tinfo->wait_count = 0;
179
180 IV_SIGNAL_INIT(&tinfo->sigchld_interest);
181 tinfo->sigchld_interest.signum = SIGCHLD;
182 tinfo->sigchld_interest.flags = IV_SIGNAL_FLAG_EXCLUSIVE;
183 tinfo->sigchld_interest.handler = iv_wait_got_sigchld;
184
185 tinfo->handled_wait_interest = NULL;
186 }
187
188 static struct iv_tls_user iv_wait_tls_user = {
189 .sizeof_state = sizeof(struct iv_wait_thr_info),
190 .init_thread = iv_wait_tls_init_thread,
191 };
192
193 static void iv_wait_tls_init(void) __attribute__((constructor));
iv_wait_tls_init(void)194 static void iv_wait_tls_init(void)
195 {
196 ___mutex_init(&iv_wait_lock);
197
198 iv_tls_user_register(&iv_wait_tls_user);
199 }
200
iv_wait_completion(void * _this)201 static void iv_wait_completion(void *_this)
202 {
203 struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
204 struct iv_wait_interest *this = _this;
205 struct iv_list_head events;
206
207 ___mutex_lock(&iv_wait_lock);
208 __iv_list_steal_elements(&this->events_pending, &events);
209 ___mutex_unlock(&iv_wait_lock);
210
211 tinfo->handled_wait_interest = this;
212
213 while (!iv_list_empty(&events)) {
214 struct wait_event *we;
215
216 we = iv_container_of(events.next, struct wait_event, list);
217 iv_list_del(&we->list);
218
219 if (tinfo->handled_wait_interest != NULL) {
220 #ifdef HAVE_WAIT4
221 this->handler(this->cookie, we->status, &we->rusage);
222 #else
223 this->handler(this->cookie, we->status, NULL);
224 #endif
225 }
226
227 free(we);
228 }
229
230 tinfo->handled_wait_interest = NULL;
231 }
232
__iv_wait_interest_register(struct iv_wait_thr_info * tinfo,struct iv_wait_interest * this)233 static void __iv_wait_interest_register(struct iv_wait_thr_info *tinfo,
234 struct iv_wait_interest *this)
235 {
236 if (!tinfo->wait_count++)
237 iv_signal_register(&tinfo->sigchld_interest);
238
239 IV_EVENT_INIT(&this->ev);
240 this->ev.handler = iv_wait_completion;
241 this->ev.cookie = this;
242 iv_event_register(&this->ev);
243
244 INIT_IV_LIST_HEAD(&this->events_pending);
245
246 this->dummy = NULL;
247
248 this->flags = 0;
249 }
250
iv_wait_interest_register(struct iv_wait_interest * this)251 void iv_wait_interest_register(struct iv_wait_interest *this)
252 {
253 struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
254
255 __iv_wait_interest_register(tinfo, this);
256
257 ___mutex_lock(&iv_wait_lock);
258 iv_avl_tree_insert(&iv_wait_interests, &this->avl_node);
259 ___mutex_unlock(&iv_wait_lock);
260 }
261
__iv_wait_interest_unregister(struct iv_wait_thr_info * tinfo,struct iv_wait_interest * this)262 static void __iv_wait_interest_unregister(struct iv_wait_thr_info *tinfo,
263 struct iv_wait_interest *this)
264 {
265 iv_event_unregister(&this->ev);
266
267 while (!iv_list_empty(&this->events_pending)) {
268 struct wait_event *we;
269
270 we = iv_container_of(this->events_pending.next,
271 struct wait_event, list);
272 iv_list_del(&we->list);
273 free(we);
274 }
275
276 if (tinfo->handled_wait_interest == this)
277 tinfo->handled_wait_interest = NULL;
278
279 if (!--tinfo->wait_count)
280 iv_signal_unregister(&tinfo->sigchld_interest);
281 }
282
iv_wait_interest_register_spawn(struct iv_wait_interest * this,void (* fn)(void * cookie),void * cookie)283 int iv_wait_interest_register_spawn(struct iv_wait_interest *this,
284 void (*fn)(void *cookie), void *cookie)
285 {
286 struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
287 pid_t pid;
288
289 __iv_wait_interest_register(tinfo, this);
290
291 ___mutex_lock(&iv_wait_lock);
292
293 pid = fork();
294 if (pid < 0) {
295 ___mutex_unlock(&iv_wait_lock);
296 __iv_wait_interest_unregister(tinfo, this);
297 return pid;
298 }
299
300 if (pid == 0) {
301 iv_signal_child_reset_postfork();
302 fn(cookie);
303 exit(1);
304 } else {
305 this->pid = pid;
306 iv_avl_tree_insert(&iv_wait_interests, &this->avl_node);
307 }
308
309 ___mutex_unlock(&iv_wait_lock);
310
311 return 0;
312 }
313
iv_wait_interest_unregister(struct iv_wait_interest * this)314 void iv_wait_interest_unregister(struct iv_wait_interest *this)
315 {
316 struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
317
318 ___mutex_lock(&iv_wait_lock);
319 if (!(this->flags & IV_WAIT_STATUS_DEAD))
320 iv_avl_tree_delete(&iv_wait_interests, &this->avl_node);
321 ___mutex_unlock(&iv_wait_lock);
322
323 __iv_wait_interest_unregister(tinfo, this);
324 }
325
iv_wait_interest_kill(const struct iv_wait_interest * this,int sig)326 int iv_wait_interest_kill(const struct iv_wait_interest *this, int sig)
327 {
328 int ret;
329
330 ___mutex_lock(&iv_wait_lock);
331 if (!(this->flags & IV_WAIT_STATUS_DEAD))
332 ret = kill(this->pid, sig);
333 else
334 ret = -ESRCH;
335 ___mutex_unlock(&iv_wait_lock);
336
337 return ret;
338 }
339