Skip to content

Commit

Permalink
[Truffle] Process.waitall
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed Apr 14, 2015
1 parent 1c5072b commit 6fcb945
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 3 deletions.
Expand Up @@ -6,6 +6,34 @@
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*
* Some of the code in this class is transliterated from C++ code in Rubinius.
*
* Copyright (c) 2007-2014, Evan Phoenix and contributors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Rubinius nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jruby.truffle.nodes.rubinius;

Expand All @@ -27,6 +55,7 @@
import org.jruby.truffle.runtime.signal.ProcSignalHandler;
import org.jruby.truffle.runtime.signal.SignalOperations;

import org.jruby.truffle.runtime.subsystems.ThreadManager;
import sun.misc.Signal;

import com.oracle.truffle.api.CompilerDirectives;
Expand All @@ -39,6 +68,9 @@
import java.util.ArrayList;
import java.util.List;

import static jnr.constants.platform.Errno.*;
import static jnr.constants.platform.WaitFlags.*;

/**
* Rubinius primitives associated with the VM.
*/
Expand Down Expand Up @@ -529,4 +561,78 @@ public RubyArray getSection(RubyString section) {

}

@RubiniusPrimitive(name = "vm_wait_pid", needsSelf = false)
public abstract static class VMWaitPidPrimitiveNode extends RubiniusPrimitiveNode {

public VMWaitPidPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public VMWaitPidPrimitiveNode(VMWaitPidPrimitiveNode prev) {
super(prev);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object waitPID(final int input_pid, boolean no_hang) {
// Transliterated from Rubinius C++ - not tidied up significantly to make merging changes easier

int options = 0;
final int[] status = new int[]{0};
int pid;

if(no_hang) {
options |= WNOHANG.intValue();
}

final int finalOptions = options;

retry:

pid = getContext().getThreadManager().runOnce(new ThreadManager.BlockingActionWithoutGlobalLock<Integer>() {

@Override
public Integer block() throws InterruptedException {
return getContext().getRuntime().getPosix().waitpid(input_pid, status, finalOptions);
}

});

final int errno = getContext().getRuntime().getPosix().errno();

if(pid == -1) {
if(errno == ECHILD.intValue()) return false;
if(errno == EINTR.intValue()) {
throw new UnsupportedOperationException();
//if(!state->check_async(calling_environment)) return NULL;
//goto retry;
}

// TODO handle other errnos?
return false;
}

if(no_hang && pid == 0) {
return nil();
}

Object output = nil();
Object termsig = nil();
Object stopsig = nil();

/* TODO CS 14-April-15 figure out how to do this using JNR
if(status == WIFEXITED.i) {
output = WEXITSTATUS(status);
} else if(WIFSIGNALED(status)) {
termsig = WTERMSIG(status);
} else if(WIFSTOPPED(status)){
stopsig = WSTOPSIG(status);
}
*/

return RubyArray.fromObjects(getContext().getCoreLibrary().getArrayClass(), output, termsig, stopsig, pid);
}

}

}
Expand Up @@ -233,6 +233,7 @@ public CoreLibrary(RubyContext context) {
defineClass(errnoModule, systemCallErrorClass, "ENXIO");
defineClass(errnoModule, systemCallErrorClass, "EPERM");
defineClass(errnoModule, systemCallErrorClass, "EXDEV");
defineClass(errnoModule, systemCallErrorClass, "ECHILD");

// ScriptError
RubyClass scriptErrorClass = defineClass(exceptionClass, "ScriptError");
Expand Down
2 changes: 1 addition & 1 deletion truffle/src/main/ruby/core.rb
Expand Up @@ -152,7 +152,7 @@
require_relative 'core/rubinius/common/range'
require_relative 'core/rubinius/common/struct'
require_relative 'core/rubinius/common/process'
#require_relative 'core/rubinius/common/process_mirror'
require_relative 'core/rubinius/common/process_mirror'
require_relative 'core/rubinius/common/random'
require_relative 'core/rubinius/common/regexp'
require_relative 'core/rubinius/common/signal'
Expand Down
7 changes: 5 additions & 2 deletions truffle/src/main/ruby/core/rubinius/bootstrap/process.rb
Expand Up @@ -24,9 +24,12 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Only part of Rubinius' process.rb

module Process
def self.wait_pid_prim(pid, no_hang)
Rubinius.primitive :vm_wait_pid
raise PrimitiveFailure, "Process.wait_pid primitive failed"
end

def self.time
Rubinius.primitive :vm_time
raise PrimitiveFailure, "Process.time primitive failed"
Expand Down
150 changes: 150 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/process.rb
Expand Up @@ -27,6 +27,10 @@
# Only part of Rubinius' process.rb

module Process
module Constants
WNOHANG = 1
end
include Constants

FFI = Rubinius::FFI

Expand Down Expand Up @@ -76,4 +80,150 @@ def self.groups
g
end

#
# Wait for all child processes.
#
# Blocks until all child processes have exited, and returns
# an Array of [pid, Process::Status] results, one for each
# child.
#
# Be mindful of the effects of creating new processes while
# .waitall has been called (usually in a different thread.)
# The .waitall call does not in any way check that it is only
# waiting for children that existed at the time it was called.
#
def self.waitall
statuses = []

begin
while true
statuses << Process.wait2
end
rescue Errno::ECHILD
end

statuses
end

#
# Wait for the given process to exit.
#
# The pid may be the specific pid of some previously forked
# process, or -1 to indicate to watch for *any* child process
# exiting. Other options, such as process groups, may be available
# depending on the system.
#
# With no arguments the default is to block waiting for any
# child processes (pid -1.)
#
# The flag may be Process::WNOHANG, which indicates that
# the child should only be quickly checked. If it has not
# exited yet, nil is returned immediately instead.
#
# The return value is the exited pid or nil if Process::WNOHANG
# was used and the child had not yet exited.
#
# If the pid has exited, the global $? is set to a Process::Status
# object representing the exit status (and possibly other info) of
# the child.
#
# If there exists no such pid (e.g. never forked or already
# waited for), or no children at all, Errno::ECHILD is raised.
#
# TODO: Support other options such as WUNTRACED? --rue
#
def self.wait2(input_pid=-1, flags=nil)
input_pid = Rubinius::Type.coerce_to input_pid, Integer, :to_int

if flags and (flags & WNOHANG) == WNOHANG
value = wait_pid_prim input_pid, true
return if value.nil?
else
value = wait_pid_prim input_pid, false
end

if value == false
raise Errno::ECHILD, "No child process: #{input_pid}"
end

# wait_pid_prim returns a tuple when wait needs to communicate
# the pid that was actually detected as stopped (since wait
# can wait for all child pids, groups, etc)
status, termsig, stopsig, pid = value

status = Process::Status.new(pid, status, termsig, stopsig)
Rubinius::Mirror::Process.set_status_global status

[pid, status]
end

#--
# TODO: Most of the fields aren't implemented yet.
# TODO: Also, these objects should only need to be constructed by
# Process.wait and family.
#++

class Status

attr_reader :exitstatus
attr_reader :termsig
attr_reader :stopsig

def initialize(pid, exitstatus, termsig=nil, stopsig=nil)
@pid = pid
@exitstatus = exitstatus
@termsig = termsig
@stopsig = stopsig
end

def to_i
@exitstatus
end

def to_s
@exitstatus.to_s
end

def &(num)
@exitstatus & num
end

def ==(other)
other = other.to_i if other.kind_of? Process::Status
@exitstatus == other
end

def >>(num)
@exitstatus >> num
end

def coredump?
false
end

def exited?
@exitstatus != nil
end

def pid
@pid
end

def signaled?
@termsig != nil
end

def stopped?
@stopsig != nil
end

def success?
if exited?
@exitstatus == 0
else
nil
end
end
end

end
37 changes: 37 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/process_mirror.rb
@@ -0,0 +1,37 @@
# Copyright (c) 2007-2014, Evan Phoenix and contributors
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Rubinius nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Only part of Rubinius' process_mirror.rb

module Rubinius
class Mirror
module Process
def self.set_status_global(status)
::Thread.current[:$?] = status
end
end
end
end

0 comments on commit 6fcb945

Please sign in to comment.