# HG changeset patch # User Christian Brabandt # Date 1455302705 -3600 # Node ID 75e0831549f1fbc19fb8afc3789fa8db8519ba2f # Parent 795f511dc6c317a4118d94c0ab66bb6fcfaec3e6 commit https://github.com/vim/vim/commit/76467dfcafcf295fd987f712730774c6f55317d3 Author: Bram Moolenaar Date: Fri Feb 12 19:30:26 2016 +0100 patch 7.4.1306 Problem: Job control doesn't work well on MS-Windows. Solution: Various fixes. (Ken Takata, Ozaki Kiichi , Yukihiro Nakadaira, Yasuhiro Matsumoto) diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -113,7 +113,7 @@ # Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is # i386) # -# Version Support: WINVER=[0x0400, 0x0500] (default is 0x0400) +# Version Support: WINVER=[0x0400, 0x0500] (default is 0x0500) # # Debug version: DEBUG=yes # Mapfile: MAP=[no, yes or lines] (default is yes) @@ -370,9 +370,8 @@ CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32 !endif ### Set the default $(WINVER) to make it work with VC++7.0 (VS.NET) -# When set to 0x0500 ":browse" stops working. !ifndef WINVER -WINVER = 0x0400 +WINVER = 0x0500 !endif # If you have a fixed directory for $VIM or $VIMRUNTIME, other than the normal diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -7720,8 +7720,7 @@ failret: static void job_free(job_T *job) { - /* TODO: free any handles */ - + mch_clear_job(job); vim_free(job); } @@ -14369,9 +14368,11 @@ f_job_start(typval_T *argvars UNUSED, ty s = vim_strsave_shellescape(s, FALSE, TRUE); if (s == NULL) goto theend; - } - ga_concat(&ga, s); - vim_free(s); + ga_concat(&ga, s); + vim_free(s); + } + else + ga_concat(&ga, s); if (li->li_next != NULL) ga_append(&ga, ' '); #endif @@ -21623,7 +21624,8 @@ get_tv_string_buf_chk(typval_T *varp, ch "process %ld %s", (long)job->jv_pid, status); # elif defined(WIN32) vim_snprintf((char *)buf, NUMBUFLEN, - "process %ld %s", (long)job->jv_pi.dwProcessId, + "process %ld %s", + (long)job->jv_proc_info.dwProcessId, status); # else /* fall-back */ diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5092,6 +5092,12 @@ mch_job_status(job_T *job) job->jv_status = JOB_ENDED; return "dead"; } + if (WIFSIGNALED(status)) + { + job->jv_exitval = -1; + job->jv_status = JOB_ENDED; + return "dead"; + } return "run"; } @@ -5099,6 +5105,7 @@ mch_job_status(job_T *job) mch_stop_job(job_T *job, char_u *how) { int sig = -1; + pid_t job_pid; if (STRCMP(how, "hup") == 0) sig = SIGHUP; @@ -5112,10 +5119,30 @@ mch_stop_job(job_T *job, char_u *how) sig = atoi((char *)how); else return FAIL; + /* TODO: have an option to only kill the process, not the group? */ - kill(-job->jv_pid, sig); + job_pid = job->jv_pid; + if (job_pid == getpgid(job_pid)) + job_pid = -job_pid; + + kill(job_pid, sig); + return OK; } + +/* + * Clear the data related to "job". + */ + void +mch_clear_job(job_T *job) +{ + /* call waitpid because child process may become zombie */ +# ifdef __NeXT__ + wait4(job->jv_pid, NULL, WNOHANG, (struct rusage *)0); +# else + waitpid(job->jv_pid, NULL, WNOHANG); +# endif +} #endif /* diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -5038,19 +5038,44 @@ mch_start_job(char *cmd, job_T *job) { STARTUPINFO si; PROCESS_INFORMATION pi; - + HANDLE jo; + + jo = CreateJobObject(NULL, NULL); + if (jo == NULL) + { + job->jv_status = JOB_FAILED; + return; + } + + ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; if (!vim_create_process(cmd, FALSE, + CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP | - CREATE_NO_WINDOW, + CREATE_NEW_CONSOLE, &si, &pi)) + { + CloseHandle(jo); job->jv_status = JOB_FAILED; + } else { - job->jv_pi = pi; + if (!AssignProcessToJobObject(jo, pi.hProcess)) + { + /* if failing, switch the way to terminate + * process with TerminateProcess. */ + CloseHandle(jo); + jo = NULL; + } + ResumeThread(pi.hThread); + CloseHandle(job->jv_proc_info.hThread); + job->jv_proc_info = pi; + job->jv_job_object = jo; job->jv_status = JOB_STARTED; } } @@ -5060,12 +5085,10 @@ mch_job_status(job_T *job) { DWORD dwExitCode = 0; - if (!GetExitCodeProcess(job->jv_pi.hProcess, &dwExitCode)) - return "dead"; - if (dwExitCode != STILL_ACTIVE) - { - CloseHandle(job->jv_pi.hProcess); - CloseHandle(job->jv_pi.hThread); + if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode) + || dwExitCode != STILL_ACTIVE) + { + job->jv_status = JOB_ENDED; return "dead"; } return "run"; @@ -5074,14 +5097,39 @@ mch_job_status(job_T *job) int mch_stop_job(job_T *job, char_u *how) { + int ret = 0; + int ctrl_c = STRCMP(how, "int") == 0; + if (STRCMP(how, "kill") == 0) - TerminateProcess(job->jv_pi.hProcess, 0); - else - return GenerateConsoleCtrlEvent( - STRCMP(how, "hup") == 0 ? - CTRL_BREAK_EVENT : CTRL_C_EVENT, - job->jv_pi.dwProcessId) ? OK : FAIL; - return OK; + { + if (job->jv_job_object != NULL) + return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL; + else + return TerminateProcess(job->jv_proc_info.hProcess, 0) ? OK : FAIL; + } + + if (!AttachConsole(job->jv_proc_info.dwProcessId)) + return FAIL; + ret = GenerateConsoleCtrlEvent( + ctrl_c ? CTRL_C_EVENT : CTRL_BREAK_EVENT, + job->jv_proc_info.dwProcessId) + ? OK : FAIL; + FreeConsole(); + return ret; +} + +/* + * Clear the data related to "job". + */ + void +mch_clear_job(job_T *job) +{ + if (job->jv_status != JOB_FAILED) + { + if (job->jv_job_object != NULL) + CloseHandle(job->jv_job_object); + CloseHandle(job->jv_proc_info.hProcess); + } } #endif diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -60,6 +60,7 @@ int mch_call_shell(char_u *cmd, int opti void mch_start_job(char **argv, job_T *job); char *mch_job_status(job_T *job); int mch_stop_job(job_T *job, char_u *how); +void mch_clear_job(job_T *job); void mch_breakcheck(void); int mch_expandpath(garray_T *gap, char_u *path, int flags); int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -43,6 +43,7 @@ int mch_call_shell(char_u *cmd, int opti void mch_start_job(char *cmd, job_T *job); char *mch_job_status(job_T *job); int mch_stop_job(job_T *job, char_u *how); +void mch_clear_job(job_T *job); void mch_set_normal_colors(void); void mch_write(char_u *s, int len); void mch_delay(long msec, int ignoreinput); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1249,7 +1249,8 @@ struct jobvar_S int jv_exitval; #endif #ifdef WIN32 - PROCESS_INFORMATION jv_pi; + PROCESS_INFORMATION jv_proc_info; + HANDLE jv_job_object; #endif jobstatus_T jv_status; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1306, +/**/ 1305, /**/ 1304,