Skip to content

Commit

Permalink
Merge pull request #4114 from mrkline/improved-linux-spawn-process
Browse files Browse the repository at this point in the history
Avoid brute force fd close() on Linux
  • Loading branch information
schveiguy committed Apr 4, 2016
2 parents df99fc8 + bed2df7 commit 5bdc3b3
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 7 deletions.
11 changes: 11 additions & 0 deletions changelog.dd
Expand Up @@ -23,6 +23,8 @@ $(BUGSTITLE Library Changes,
$(XREF algorithm_searching, maxPos) were added.))
$(LI Range support for the convenience wrapper $(XREF container_rbtree, redBlackTree)
was added.)
$(LI $(RELATIVE_LINK2 process, Process creation in
$(STDMODREF process, std.process) was sped up on Posix.))
)

$(BUGSTITLE Library Changes,
Expand Down Expand Up @@ -65,6 +67,15 @@ $(P Previous to this addition, in order to get the number of the greatest
This change adds convenience functions to fix this issue.)
)

$(LI $(RELATIVE_LINK2 process, Process creation in
$(STDMODREF process, std.process) was sped up on Posix.)
Previously, Posix systems would attempt to close every file descriptor from 3
to the maximum file descriptor number if `inheritFDs` was not specified
with `spawnProcess`, `pipeProcess`, etc.
$(STDMODREF process, std.process) now uses `poll()` to determine which
descriptors need closing.
)

)

Macros:
Expand Down
59 changes: 52 additions & 7 deletions std/process.d
Expand Up @@ -450,17 +450,62 @@ private Pid spawnProcessImpl(in char[][] args,
setCLOEXEC(STDERR_FILENO, false);
if (!(config & Config.inheritFDs))
{
import core.sys.posix.poll;
import core.sys.posix.sys.resource;

// Get the maximum number of file descriptors that could be open.
rlimit r;
getrlimit(RLIMIT_NOFILE, &r);
foreach (i; 3 .. cast(int) r.rlim_cur) close(i);
errnoEnforce(getrlimit(RLIMIT_NOFILE, &r) == 0);
immutable maxDescriptors = cast(int)r.rlim_cur;

// The above, less stdin, stdout, and stderr
immutable maxToClose = maxDescriptors - 3;

// Call poll() to see which ones are actually open:
// Done as an internal function because MacOS won't allow
// alloca and exceptions to mix.
@nogc nothrow
static bool pollClose(int maxToClose)
{
import core.stdc.stdlib : alloca;

pollfd* pfds = cast(pollfd*)alloca(pollfd.sizeof * maxToClose);
foreach (i; 0 .. maxToClose)
{
pfds[i].fd = i + 3;
pfds[i].events = 0;
pfds[i].revents = 0;
}
if (poll(pfds, maxToClose, 0) >= 0)
{
foreach (i; 0 .. maxToClose)
{
// POLLNVAL will be set if the file descriptor is invalid.
if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd);
}
return true;
}
else
{
return false;
}
}

if (!pollClose(maxToClose))
{
// Fall back to closing everything.
foreach (i; 3 .. maxDescriptors) close(i);
}
}
else
{ // This is already done if we don't inherit descriptors.

// Close the old file descriptors, unless they are
// either of the standard streams.
if (stdinFD > STDERR_FILENO) close(stdinFD);
if (stdoutFD > STDERR_FILENO) close(stdoutFD);
if (stderrFD > STDERR_FILENO) close(stderrFD);
// Close the old file descriptors, unless they are
// either of the standard streams.
if (stdinFD > STDERR_FILENO) close(stdinFD);
if (stdoutFD > STDERR_FILENO) close(stdoutFD);
if (stderrFD > STDERR_FILENO) close(stderrFD);
}

// Execute program.
core.sys.posix.unistd.execve(argz[0], argz.ptr, envz);
Expand Down

0 comments on commit 5bdc3b3

Please sign in to comment.