merge revision(s) 35249,35250: [Backport #6296]

* include/ruby/win32.h (rb_w32_aspawn_flags): add the declaration of
	  new function.

	* process.c (enum): add EXEC_OPTION_PGROUP and move the position
	  above for the usage in proc_spawn_n().

	* process.c (proc_spawn_n): add an argument to pass new option
	  `new_pgroup`. The option specifies CREATE_NEW_PROCESS_GROUP flag to
	  CreateProcessW(). This flag is necessary for the usage of
	  Process.kill on the subprocess on Windows.

	* process.c (rb_exec_arg_addopt): ditto.

	* process.c (rb_spawn_process): ditto.

	* process.c (documentation for rb_f_spawn): add documentation for new
	  option `new_pgroup` of spawn.

	* test/ruby/test_process.rb (TestProcess#test_execopts_new_pgroup):
	  add tests for option `new_pgroup`.

	* test/ruby/test_thread.rb
	  (TestThreadGroup#test_thread_timer_and_interrupt):
	  add option `new_pgroup: true` to spawn on Windows. It's needed for
	  Process.kill on a subprocess.

	* win32/win32.c (CreateChild): add an argument to pass
	  dwCreationFlags of CreateProcessW().

	* win32/win32.c (rb_w32_spawn): ditto.

	* win32/win32.c (rb_w32_aspawn_flags): add new function to pass
	  dwCreationFlags.

	* win32/win32.c (rb_w32_aspawn): refactor to move the content to
	  rb_w32_aspawn_flags().
	  [ruby-core:43245][Bug #6131]

	* test/ruby/test_thread.rb
	  (TestThreadGroup#test_thread_timer_and_interrupt): skip on Windows.
	  Process.kill cannot kill a subprocess if CREATE_NEW_PROCESS_GROUP
	  flag is not specified in a call to CreateProcessW().

	* win32/win32.c (CreateChild): revert the usage of
	  CREATE_NEW_PROCESS_GROUP flag for compatibility.
	  [ruby-core:43245][Bug #6131]


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_9_3@35332 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2012-04-14 21:47:27 +00:00
parent 7201291187
commit 97d00bcdee
7 changed files with 130 additions and 26 deletions

View file

@ -1,3 +1,54 @@
Sun Apr 15 06:40:28 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com>
* include/ruby/win32.h (rb_w32_aspawn_flags): add the declaration of
new function.
* process.c (enum): add EXEC_OPTION_PGROUP and move the position
above for the usage in proc_spawn_n().
* process.c (proc_spawn_n): add an argument to pass new option
`new_pgroup`. The option specifies CREATE_NEW_PROCESS_GROUP flag to
CreateProcessW(). This flag is necessary for the usage of
Process.kill on the subprocess on Windows.
* process.c (rb_exec_arg_addopt): ditto.
* process.c (rb_spawn_process): ditto.
* process.c (documentation for rb_f_spawn): add documentation for new
option `new_pgroup` of spawn.
* test/ruby/test_process.rb (TestProcess#test_execopts_new_pgroup):
add tests for option `new_pgroup`.
* test/ruby/test_thread.rb
(TestThreadGroup#test_thread_timer_and_interrupt):
add option `new_pgroup: true` to spawn on Windows. It's needed for
Process.kill on a subprocess.
* win32/win32.c (CreateChild): add an argument to pass
dwCreationFlags of CreateProcessW().
* win32/win32.c (rb_w32_spawn): ditto.
* win32/win32.c (rb_w32_aspawn_flags): add new function to pass
dwCreationFlags.
* win32/win32.c (rb_w32_aspawn): refactor to move the content to
rb_w32_aspawn_flags().
[ruby-core:43245][Bug #6131]
Sun Apr 15 06:40:28 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com>
* test/ruby/test_thread.rb
(TestThreadGroup#test_thread_timer_and_interrupt): skip on Windows.
Process.kill cannot kill a subprocess if CREATE_NEW_PROCESS_GROUP
flag is not specified in a call to CreateProcessW().
* win32/win32.c (CreateChild): revert the usage of
CREATE_NEW_PROCESS_GROUP flag for compatibility.
[ruby-core:43245][Bug #6131]
Sun Apr 15 04:35:48 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com> Sun Apr 15 04:35:48 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com>
* io.c (rb_io_eof): use eof() instead of io_fillbuf(). It's because * io.c (rb_io_eof): use eof() instead of io_fillbuf(). It's because

View file

@ -284,6 +284,7 @@ extern int gettimeofday(struct timeval *, struct timezone *);
extern rb_pid_t waitpid (rb_pid_t, int *, int); extern rb_pid_t waitpid (rb_pid_t, int *, int);
extern rb_pid_t rb_w32_spawn(int, const char *, const char*); extern rb_pid_t rb_w32_spawn(int, const char *, const char*);
extern rb_pid_t rb_w32_aspawn(int, const char *, char *const *); extern rb_pid_t rb_w32_aspawn(int, const char *, char *const *);
extern rb_pid_t rb_w32_aspawn_flags(int, const char *, char *const *, DWORD);
extern int kill(int, int); extern int kill(int, int);
extern int fcntl(int, int, ...); extern int fcntl(int, int, ...);
extern rb_pid_t rb_w32_getpid(void); extern rb_pid_t rb_w32_getpid(void);

View file

@ -1216,6 +1216,21 @@ rb_proc_exec(const char *str)
#endif /* _WIN32 */ #endif /* _WIN32 */
} }
enum {
EXEC_OPTION_PGROUP,
EXEC_OPTION_RLIMIT,
EXEC_OPTION_UNSETENV_OTHERS,
EXEC_OPTION_ENV,
EXEC_OPTION_CHDIR,
EXEC_OPTION_UMASK,
EXEC_OPTION_DUP2,
EXEC_OPTION_CLOSE,
EXEC_OPTION_OPEN,
EXEC_OPTION_DUP2_CHILD,
EXEC_OPTION_CLOSE_OTHERS,
EXEC_OPTION_NEW_PGROUP
};
#if defined(_WIN32) #if defined(_WIN32)
#define HAVE_SPAWNV 1 #define HAVE_SPAWNV 1
#endif #endif
@ -1261,7 +1276,7 @@ proc_spawn_v(char **argv, char *prog)
#endif #endif
static rb_pid_t static rb_pid_t
proc_spawn_n(int argc, VALUE *argv, VALUE prog) proc_spawn_n(int argc, VALUE *argv, VALUE prog, VALUE options)
{ {
char **args; char **args;
int i; int i;
@ -1273,8 +1288,17 @@ proc_spawn_n(int argc, VALUE *argv, VALUE prog)
args[i] = RSTRING_PTR(argv[i]); args[i] = RSTRING_PTR(argv[i]);
} }
args[i] = (char*) 0; args[i] = (char*) 0;
if (args[0]) if (args[0]) {
#if defined(_WIN32)
DWORD flags = 0;
if (RTEST(rb_ary_entry(options, EXEC_OPTION_NEW_PGROUP))) {
flags = CREATE_NEW_PROCESS_GROUP;
}
pid = rb_w32_aspawn_flags(P_NOWAIT, prog ? RSTRING_PTR(prog) : 0, args, flags);
#else
pid = proc_spawn_v(args, prog ? RSTRING_PTR(prog) : 0); pid = proc_spawn_v(args, prog ? RSTRING_PTR(prog) : 0);
#endif
}
ALLOCV_END(v); ALLOCV_END(v);
return pid; return pid;
} }
@ -1322,20 +1346,6 @@ hide_obj(VALUE obj)
return obj; return obj;
} }
enum {
EXEC_OPTION_PGROUP,
EXEC_OPTION_RLIMIT,
EXEC_OPTION_UNSETENV_OTHERS,
EXEC_OPTION_ENV,
EXEC_OPTION_CHDIR,
EXEC_OPTION_UMASK,
EXEC_OPTION_DUP2,
EXEC_OPTION_CLOSE,
EXEC_OPTION_OPEN,
EXEC_OPTION_DUP2_CHILD,
EXEC_OPTION_CLOSE_OTHERS
};
static VALUE static VALUE
check_exec_redirect_fd(VALUE v, int iskey) check_exec_redirect_fd(VALUE v, int iskey)
{ {
@ -1519,6 +1529,16 @@ rb_exec_arg_addopt(struct rb_exec_arg *e, VALUE key, VALUE val)
} }
else else
#endif #endif
#ifdef _WIN32
if (id == rb_intern("new_pgroup")) {
if (!NIL_P(rb_ary_entry(options, EXEC_OPTION_NEW_PGROUP))) {
rb_raise(rb_eArgError, "new_pgroup option specified twice");
}
val = RTEST(val) ? Qtrue : Qfalse;
rb_ary_store(options, EXEC_OPTION_NEW_PGROUP, val);
}
else
#endif
#if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM) #if defined(HAVE_SETRLIMIT) && defined(NUM2RLIM)
if (strncmp("rlimit_", rb_id2name(id), 7) == 0 && if (strncmp("rlimit_", rb_id2name(id), 7) == 0 &&
(rtype = rlimit_type_by_lname(rb_id2name(id)+7)) != -1) { (rtype = rlimit_type_by_lname(rb_id2name(id)+7)) != -1) {
@ -3050,7 +3070,7 @@ rb_spawn_process(struct rb_exec_arg *earg, VALUE prog, char *errmsg, size_t errm
pid = proc_spawn(RSTRING_PTR(prog)); pid = proc_spawn(RSTRING_PTR(prog));
} }
else { else {
pid = proc_spawn_n(argc, argv, prog); pid = proc_spawn_n(argc, argv, prog, earg->options);
} }
# if defined(_WIN32) # if defined(_WIN32)
if (pid == -1) if (pid == -1)
@ -3185,6 +3205,9 @@ rb_f_system(int argc, VALUE *argv)
* :pgroup => true or 0 : make a new process group * :pgroup => true or 0 : make a new process group
* :pgroup => pgid : join to specified process group * :pgroup => pgid : join to specified process group
* :pgroup => nil : don't change the process group (default) * :pgroup => nil : don't change the process group (default)
* create new process group: Windows only
* :new_pgroup => true : the new process is the root process of a new process group
* :new_pgroup => false : don't create a new process group (default)
* resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit. * resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
* :rlimit_resourcename => limit * :rlimit_resourcename => limit
* :rlimit_resourcename => [cur_limit, max_limit] * :rlimit_resourcename => [cur_limit, max_limit]
@ -3224,6 +3247,7 @@ rb_f_system(int argc, VALUE *argv)
* If a hash is given as +options+, * If a hash is given as +options+,
* it specifies * it specifies
* process group, * process group,
* create new process group,
* resource limit, * resource limit,
* current directory, * current directory,
* umask and * umask and
@ -3245,6 +3269,17 @@ rb_f_system(int argc, VALUE *argv)
* pid = spawn(command, :pgroup=>true) # process leader * pid = spawn(command, :pgroup=>true) # process leader
* pid = spawn(command, :pgroup=>10) # belongs to the process group 10 * pid = spawn(command, :pgroup=>10) # belongs to the process group 10
* *
* The <code>:new_pgroup</code> key in +options+ specifies to pass
* +CREATE_NEW_PROCESS_GROUP+ flag to <code>CreateProcessW()</code> that is
* Windows API. This option is only for Windows.
* true means the new process is the root process of the new process group.
* The new process has CTRL+C disabled. This flag is necessary for
* <code>Process.kill(:SIGINT, pid)</code> on the subprocess.
* :new_pgroup is false by default.
*
* pid = spawn(command, :new_pgroup=>true) # new process group
* pid = spawn(command, :new_pgroup=>false) # same process group
*
* The <code>:rlimit_</code><em>foo</em> key specifies a resource limit. * The <code>:rlimit_</code><em>foo</em> key specifies a resource limit.
* <em>foo</em> should be one of resource types such as <code>core</code>. * <em>foo</em> should be one of resource types such as <code>core</code>.
* The corresponding value should be an integer or an array which have one or * The corresponding value should be an integer or an array which have one or

View file

@ -1327,4 +1327,13 @@ class TestProcess < Test::Unit::TestCase
end end
end end
end end
def test_execopts_new_pgroup
return unless windows?
assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>true) }
assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>false) }
assert_nothing_raised { spawn(*TRUECOMMAND, :new_pgroup=>true) }
assert_nothing_raised { IO.popen([*TRUECOMMAND, :new_pgroup=>true]) {} }
end
end end

View file

@ -691,7 +691,9 @@ class TestThreadGroup < Test::Unit::TestCase
t0 = Time.now.to_f t0 = Time.now.to_f
pid = nil pid = nil
cmd = 'r,=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; r.read' cmd = 'r,=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; r.read'
s, err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true) do |in_p, out_p, err_p, cpid| opt = {}
opt[:new_pgroup] = true if /mswin|mingw/ =~ RUBY_PLATFORM
s, err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
out_p.gets out_p.gets
pid = cpid pid = cpid
Process.kill(:SIGINT, pid) Process.kill(:SIGINT, pid)

View file

@ -1,5 +1,5 @@
#define RUBY_VERSION "1.9.3" #define RUBY_VERSION "1.9.3"
#define RUBY_PATCHLEVEL 181 #define RUBY_PATCHLEVEL 182
#define RUBY_RELEASE_DATE "2012-04-15" #define RUBY_RELEASE_DATE "2012-04-15"
#define RUBY_RELEASE_YEAR 2012 #define RUBY_RELEASE_YEAR 2012

View file

@ -63,7 +63,7 @@
#define TO_SOCKET(x) _get_osfhandle(x) #define TO_SOCKET(x) _get_osfhandle(x)
static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE); static struct ChildRecord *CreateChild(const WCHAR *, const WCHAR *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE, DWORD);
static int has_redirection(const char *); static int has_redirection(const char *);
int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout); int rb_w32_wait_events(HANDLE *events, int num, DWORD timeout);
static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags); static int rb_w32_open_osfhandle(intptr_t osfhandle, int flags);
@ -1008,10 +1008,9 @@ child_result(struct ChildRecord *child, int mode)
static struct ChildRecord * static struct ChildRecord *
CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa, CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
HANDLE hInput, HANDLE hOutput, HANDLE hError) HANDLE hInput, HANDLE hOutput, HANDLE hError, DWORD dwCreationFlags)
{ {
BOOL fRet; BOOL fRet;
DWORD dwCreationFlags;
STARTUPINFOW aStartupInfo; STARTUPINFOW aStartupInfo;
PROCESS_INFORMATION aProcessInformation; PROCESS_INFORMATION aProcessInformation;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
@ -1058,7 +1057,7 @@ CreateChild(const WCHAR *cmd, const WCHAR *prog, SECURITY_ATTRIBUTES *psa,
aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
} }
dwCreationFlags = (CREATE_NEW_PROCESS_GROUP | NORMAL_PRIORITY_CLASS); dwCreationFlags |= NORMAL_PRIORITY_CLASS;
if (lstrlenW(cmd) > 32767) { if (lstrlenW(cmd) > 32767) {
child->pid = 0; /* release the slot */ child->pid = 0; /* release the slot */
@ -1209,14 +1208,14 @@ rb_w32_spawn(int mode, const char *cmd, const char *prog)
wshell = shell ? acp_to_wstr(shell, NULL) : NULL; wshell = shell ? acp_to_wstr(shell, NULL) : NULL;
if (v2) ALLOCV_END(v2); if (v2) ALLOCV_END(v2);
ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL), mode); ret = child_result(CreateChild(wcmd, wshell, NULL, NULL, NULL, NULL, 0), mode);
free(wshell); free(wshell);
free(wcmd); free(wcmd);
return ret; return ret;
} }
rb_pid_t rb_pid_t
rb_w32_aspawn(int mode, const char *prog, char *const *argv) rb_w32_aspawn_flags(int mode, const char *prog, char *const *argv, DWORD flags)
{ {
int c_switch = 0; int c_switch = 0;
size_t len; size_t len;
@ -1275,12 +1274,19 @@ rb_w32_aspawn(int mode, const char *prog, char *const *argv)
if (v) ALLOCV_END(v); if (v) ALLOCV_END(v);
wprog = prog ? acp_to_wstr(prog, NULL) : NULL; wprog = prog ? acp_to_wstr(prog, NULL) : NULL;
ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL), mode); ret = child_result(CreateChild(wcmd, wprog, NULL, NULL, NULL, NULL, flags), mode);
free(wprog); free(wprog);
free(wcmd); free(wcmd);
return ret; return ret;
} }
rb_pid_t
rb_w32_aspawn(int mode, const char *prog, char *const *argv)
{
return rb_w32_aspawn_flags(mode, prog, argv, 0);
}
/* License: Artistic or GPL */
typedef struct _NtCmdLineElement { typedef struct _NtCmdLineElement {
struct _NtCmdLineElement *next; struct _NtCmdLineElement *next;
char *str; char *str;