VirtualBox

source: kBuild/trunk/src/kmk/posixos.c@ 3387

Last change on this file since 3387 was 3140, checked in by bird, 6 years ago

kmk: Merged in changes from GNU make 4.2.1 (2e55f5e4abdc0e38c1d64be703b446695e70b3b6 / https://git.savannah.gnu.org/git/make.git).

  • Property svn:eol-style set to native
File size: 10.5 KB
Line 
1/* POSIX-based operating system interface for GNU Make.
2Copyright (C) 2016 Free Software Foundation, Inc.
3This file is part of GNU Make.
4
5GNU Make is free software; you can redistribute it and/or modify it under the
6terms of the GNU General Public License as published by the Free Software
7Foundation; either version 3 of the License, or (at your option) any later
8version.
9
10GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#include "makeint.h"
18
19#include <stdio.h>
20
21#ifdef HAVE_FCNTL_H
22# include <fcntl.h>
23#endif
24#if defined(HAVE_PSELECT) && defined(HAVE_SYS_SELECT_H)
25# include <sys/select.h>
26#endif
27
28#include "debug.h"
29#include "job.h"
30#include "os.h"
31
32#ifdef MAKE_JOBSERVER
33
34/* This section provides OS-specific functions to support the jobserver. */
35
36/* These track the state of the jobserver pipe. Passed to child instances. */
37static int job_fds[2] = { -1, -1 };
38
39/* Used to signal read() that a SIGCHLD happened. Always CLOEXEC.
40 If we use pselect() this will never be created and always -1.
41 */
42static int job_rfd = -1;
43
44/* Token written to the pipe (could be any character...) */
45static char token = '+';
46
47static int
48make_job_rfd (void)
49{
50#ifdef HAVE_PSELECT
51 /* Pretend we succeeded. */
52 return 0;
53#else
54 EINTRLOOP (job_rfd, dup (job_fds[0]));
55 if (job_rfd >= 0)
56 CLOSE_ON_EXEC (job_rfd);
57
58 return job_rfd;
59#endif
60}
61
62unsigned int
63jobserver_setup (int slots)
64{
65 int r;
66
67 EINTRLOOP (r, pipe (job_fds));
68 if (r < 0)
69 pfatal_with_name (_("creating jobs pipe"));
70
71 if (make_job_rfd () < 0)
72 pfatal_with_name (_("duping jobs pipe"));
73
74 while (slots--)
75 {
76 EINTRLOOP (r, write (job_fds[1], &token, 1));
77 if (r != 1)
78 pfatal_with_name (_("init jobserver pipe"));
79 }
80
81 return 1;
82}
83
84unsigned int
85jobserver_parse_auth (const char *auth)
86{
87 /* Given the command-line parameter, parse it. */
88 if (sscanf (auth, "%d,%d", &job_fds[0], &job_fds[1]) != 2)
89 OS (fatal, NILF,
90 _("internal error: invalid --jobserver-auth string '%s'"), auth);
91
92 DB (DB_JOBS,
93 (_("Jobserver client (fds %d,%d)\n"), job_fds[0], job_fds[1]));
94
95#ifdef HAVE_FCNTL_H
96# define FD_OK(_f) (fcntl ((_f), F_GETFD) != -1)
97#else
98# define FD_OK(_f) 1
99#endif
100
101 /* Make sure our pipeline is valid, and (possibly) create a duplicate pipe,
102 that will be closed in the SIGCHLD handler. If this fails with EBADF,
103 the parent has closed the pipe on us because it didn't think we were a
104 submake. If so, warn and default to -j1. */
105
106 if (!FD_OK (job_fds[0]) || !FD_OK (job_fds[1]) || make_job_rfd () < 0)
107 {
108 if (errno != EBADF)
109 pfatal_with_name (_("jobserver pipeline"));
110
111 job_fds[0] = job_fds[1] = -1;
112
113 return 0;
114 }
115
116 return 1;
117}
118
119char *
120jobserver_get_auth (void)
121{
122 char *auth = xmalloc ((INTSTR_LENGTH * 2) + 2);
123 sprintf (auth, "%d,%d", job_fds[0], job_fds[1]);
124 return auth;
125}
126
127unsigned int
128jobserver_enabled (void)
129{
130 return job_fds[0] >= 0;
131}
132
133void
134jobserver_clear (void)
135{
136 if (job_fds[0] >= 0)
137 close (job_fds[0]);
138 if (job_fds[1] >= 0)
139 close (job_fds[1]);
140 if (job_rfd >= 0)
141 close (job_rfd);
142
143 job_fds[0] = job_fds[1] = job_rfd = -1;
144}
145
146void
147jobserver_release (int is_fatal)
148{
149 int r;
150 EINTRLOOP (r, write (job_fds[1], &token, 1));
151 if (r != 1)
152 {
153 if (is_fatal)
154 pfatal_with_name (_("write jobserver"));
155 perror_with_name ("write", "");
156 }
157}
158
159unsigned int
160jobserver_acquire_all (void)
161{
162 unsigned int tokens = 0;
163
164 /* Close the write side, so the read() won't hang. */
165 close (job_fds[1]);
166 job_fds[1] = -1;
167
168 while (1)
169 {
170 char intake;
171 int r;
172 EINTRLOOP (r, read (job_fds[0], &intake, 1));
173 if (r != 1)
174 return tokens;
175 ++tokens;
176 }
177}
178
179/* Prepare the jobserver to start a child process. */
180void
181jobserver_pre_child (int recursive)
182{
183 /* If it's not a recursive make, avoid polutting the jobserver pipes. */
184 if (!recursive && job_fds[0] >= 0)
185 {
186 CLOSE_ON_EXEC (job_fds[0]);
187 CLOSE_ON_EXEC (job_fds[1]);
188 }
189}
190
191void
192jobserver_post_child (int recursive)
193{
194#if defined(F_GETFD) && defined(F_SETFD)
195 if (!recursive && job_fds[0] >= 0)
196 {
197 unsigned int i;
198 for (i = 0; i < 2; ++i)
199 {
200 int flags;
201 EINTRLOOP (flags, fcntl (job_fds[i], F_GETFD));
202 if (flags >= 0)
203 {
204 int r;
205 EINTRLOOP (r, fcntl (job_fds[i], F_SETFD, flags & ~FD_CLOEXEC));
206 }
207 }
208 }
209#endif
210}
211
212void
213jobserver_signal (void)
214{
215 if (job_rfd >= 0)
216 {
217 close (job_rfd);
218 job_rfd = -1;
219 }
220}
221
222void
223jobserver_pre_acquire (void)
224{
225 /* Make sure we have a dup'd FD. */
226 if (job_rfd < 0 && job_fds[0] >= 0 && make_job_rfd () < 0)
227 pfatal_with_name (_("duping jobs pipe"));
228}
229
230#ifdef HAVE_PSELECT
231
232/* Use pselect() to atomically wait for both a signal and a file descriptor.
233 It also provides a timeout facility so we don't need to use SIGALRM.
234
235 This method relies on the fact that SIGCHLD will be blocked everywhere,
236 and only unblocked (atomically) within the pselect() call, so we can
237 never miss a SIGCHLD.
238 */
239unsigned int
240jobserver_acquire (int timeout)
241{
242 sigset_t empty;
243 fd_set readfds;
244 struct timespec spec;
245 struct timespec *specp = NULL;
246 int r;
247 char intake;
248
249 sigemptyset (&empty);
250
251 FD_ZERO (&readfds);
252 FD_SET (job_fds[0], &readfds);
253
254 if (timeout)
255 {
256 /* Alarm after one second (is this too granular?) */
257 spec.tv_sec = 1;
258 spec.tv_nsec = 0;
259 specp = &spec;
260 }
261
262 r = pselect (job_fds[0]+1, &readfds, NULL, NULL, specp, &empty);
263
264 if (r == -1)
265 {
266 /* Better be SIGCHLD. */
267 if (errno != EINTR)
268 pfatal_with_name (_("pselect jobs pipe"));
269 return 0;
270 }
271
272 if (r == 0)
273 /* Timeout. */
274 return 0;
275
276 /* The read FD is ready: read it! */
277 EINTRLOOP (r, read (job_fds[0], &intake, 1));
278 if (r < 0)
279 pfatal_with_name (_("read jobs pipe"));
280
281 /* What does it mean if read() returns 0? It shouldn't happen because only
282 the master make can reap all the tokens and close the write side...?? */
283 return r > 0;
284}
285
286#else
287
288/* This method uses a "traditional" UNIX model for waiting on both a signal
289 and a file descriptor. However, it's complex and since we have a SIGCHLD
290 handler installed we need to check ALL system calls for EINTR: painful!
291
292 Read a token. As long as there's no token available we'll block. We
293 enable interruptible system calls before the read(2) so that if we get a
294 SIGCHLD while we're waiting, we'll return with EINTR and we can process the
295 death(s) and return tokens to the free pool.
296
297 Once we return from the read, we immediately reinstate restartable system
298 calls. This allows us to not worry about checking for EINTR on all the
299 other system calls in the program.
300
301 There is one other twist: there is a span between the time reap_children()
302 does its last check for dead children and the time the read(2) call is
303 entered, below, where if a child dies we won't notice. This is extremely
304 serious as it could cause us to deadlock, given the right set of events.
305
306 To avoid this, we do the following: before we reap_children(), we dup(2)
307 the read FD on the jobserver pipe. The read(2) call below uses that new
308 FD. In the signal handler, we close that FD. That way, if a child dies
309 during the section mentioned above, the read(2) will be invoked with an
310 invalid FD and will return immediately with EBADF. */
311
312static RETSIGTYPE
313job_noop (int sig UNUSED)
314{
315}
316
317/* Set the child handler action flags to FLAGS. */
318static void
319set_child_handler_action_flags (int set_handler, int set_alarm)
320{
321 struct sigaction sa;
322
323#ifdef __EMX__
324 /* The child handler must be turned off here. */
325 signal (SIGCHLD, SIG_DFL);
326#endif
327
328 memset (&sa, '\0', sizeof sa);
329 sa.sa_handler = child_handler;
330 sa.sa_flags = set_handler ? 0 : SA_RESTART;
331
332#if defined SIGCHLD
333 if (sigaction (SIGCHLD, &sa, NULL) < 0)
334 pfatal_with_name ("sigaction: SIGCHLD");
335#endif
336
337#if defined SIGCLD && SIGCLD != SIGCHLD
338 if (sigaction (SIGCLD, &sa, NULL) < 0)
339 pfatal_with_name ("sigaction: SIGCLD");
340#endif
341
342#if defined SIGALRM
343 if (set_alarm)
344 {
345 /* If we're about to enter the read(), set an alarm to wake up in a
346 second so we can check if the load has dropped and we can start more
347 work. On the way out, turn off the alarm and set SIG_DFL. */
348 if (set_handler)
349 {
350 sa.sa_handler = job_noop;
351 sa.sa_flags = 0;
352 if (sigaction (SIGALRM, &sa, NULL) < 0)
353 pfatal_with_name ("sigaction: SIGALRM");
354 alarm (1);
355 }
356 else
357 {
358 alarm (0);
359 sa.sa_handler = SIG_DFL;
360 sa.sa_flags = 0;
361 if (sigaction (SIGALRM, &sa, NULL) < 0)
362 pfatal_with_name ("sigaction: SIGALRM");
363 }
364 }
365#endif
366}
367
368unsigned int
369jobserver_acquire (int timeout)
370{
371 char intake;
372 int got_token;
373 int saved_errno;
374
375 /* Set interruptible system calls, and read() for a job token. */
376 set_child_handler_action_flags (1, timeout);
377
378 EINTRLOOP (got_token, read (job_rfd, &intake, 1));
379 saved_errno = errno;
380
381 set_child_handler_action_flags (0, timeout);
382
383 if (got_token == 1)
384 return 1;
385
386 /* If the error _wasn't_ expected (EINTR or EBADF), fatal. Otherwise,
387 go back and reap_children(), and try again. */
388 errno = saved_errno;
389
390 if (errno != EINTR && errno != EBADF)
391 pfatal_with_name (_("read jobs pipe"));
392
393 if (errno == EBADF)
394 DB (DB_JOBS, ("Read returned EBADF.\n"));
395
396 return 0;
397}
398
399#endif
400
401#endif /* MAKE_JOBSERVER */
402
403/* Create a "bad" file descriptor for stdin when parallel jobs are run. */
404int
405get_bad_stdin (void)
406{
407 static int bad_stdin = -1;
408
409 /* Set up a bad standard input that reads from a broken pipe. */
410
411 if (bad_stdin == -1)
412 {
413 /* Make a file descriptor that is the read end of a broken pipe.
414 This will be used for some children's standard inputs. */
415 int pd[2];
416 if (pipe (pd) == 0)
417 {
418 /* Close the write side. */
419 (void) close (pd[1]);
420 /* Save the read side. */
421 bad_stdin = pd[0];
422
423 /* Set the descriptor to close on exec, so it does not litter any
424 child's descriptor table. When it is dup2'd onto descriptor 0,
425 that descriptor will not close on exec. */
426 CLOSE_ON_EXEC (bad_stdin);
427 }
428 }
429
430 return bad_stdin;
431}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use