1
0
forked from aniani/vim

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)
This commit is contained in:
Bram Moolenaar
2016-02-12 19:30:26 +01:00
parent 7c29f38781
commit 76467dfcaf
8 changed files with 105 additions and 24 deletions

View File

@@ -113,7 +113,7 @@
# Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is # Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is
# i386) # i386)
# #
# Version Support: WINVER=[0x0400, 0x0500] (default is 0x0400) # Version Support: WINVER=[0x0400, 0x0500] (default is 0x0500)
# #
# Debug version: DEBUG=yes # Debug version: DEBUG=yes
# Mapfile: MAP=[no, yes or lines] (default is yes) # Mapfile: MAP=[no, yes or lines] (default is yes)
@@ -370,9 +370,8 @@ CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib
!endif !endif
### Set the default $(WINVER) to make it work with VC++7.0 (VS.NET) ### Set the default $(WINVER) to make it work with VC++7.0 (VS.NET)
# When set to 0x0500 ":browse" stops working.
!ifndef WINVER !ifndef WINVER
WINVER = 0x0400 WINVER = 0x0500
!endif !endif
# If you have a fixed directory for $VIM or $VIMRUNTIME, other than the normal # If you have a fixed directory for $VIM or $VIMRUNTIME, other than the normal

View File

@@ -7720,8 +7720,7 @@ failret:
static void static void
job_free(job_T *job) job_free(job_T *job)
{ {
/* TODO: free any handles */ mch_clear_job(job);
vim_free(job); vim_free(job);
} }
@@ -14369,9 +14368,11 @@ f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
s = vim_strsave_shellescape(s, FALSE, TRUE); s = vim_strsave_shellescape(s, FALSE, TRUE);
if (s == NULL) if (s == NULL)
goto theend; goto theend;
}
ga_concat(&ga, s); ga_concat(&ga, s);
vim_free(s); vim_free(s);
}
else
ga_concat(&ga, s);
if (li->li_next != NULL) if (li->li_next != NULL)
ga_append(&ga, ' '); ga_append(&ga, ' ');
#endif #endif
@@ -21623,7 +21624,8 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
"process %ld %s", (long)job->jv_pid, status); "process %ld %s", (long)job->jv_pid, status);
# elif defined(WIN32) # elif defined(WIN32)
vim_snprintf((char *)buf, NUMBUFLEN, vim_snprintf((char *)buf, NUMBUFLEN,
"process %ld %s", (long)job->jv_pi.dwProcessId, "process %ld %s",
(long)job->jv_proc_info.dwProcessId,
status); status);
# else # else
/* fall-back */ /* fall-back */

View File

@@ -5092,6 +5092,12 @@ mch_job_status(job_T *job)
job->jv_status = JOB_ENDED; job->jv_status = JOB_ENDED;
return "dead"; return "dead";
} }
if (WIFSIGNALED(status))
{
job->jv_exitval = -1;
job->jv_status = JOB_ENDED;
return "dead";
}
return "run"; return "run";
} }
@@ -5099,6 +5105,7 @@ mch_job_status(job_T *job)
mch_stop_job(job_T *job, char_u *how) mch_stop_job(job_T *job, char_u *how)
{ {
int sig = -1; int sig = -1;
pid_t job_pid;
if (STRCMP(how, "hup") == 0) if (STRCMP(how, "hup") == 0)
sig = SIGHUP; sig = SIGHUP;
@@ -5112,10 +5119,30 @@ mch_stop_job(job_T *job, char_u *how)
sig = atoi((char *)how); sig = atoi((char *)how);
else else
return FAIL; return FAIL;
/* TODO: have an option to only kill the process, not the group? */ /* 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; 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 #endif
/* /*

View File

@@ -5038,19 +5038,44 @@ mch_start_job(char *cmd, job_T *job)
{ {
STARTUPINFO si; STARTUPINFO si;
PROCESS_INFORMATION pi; 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)); ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (!vim_create_process(cmd, FALSE, if (!vim_create_process(cmd, FALSE,
CREATE_SUSPENDED |
CREATE_DEFAULT_ERROR_MODE | CREATE_DEFAULT_ERROR_MODE |
CREATE_NEW_PROCESS_GROUP | CREATE_NEW_PROCESS_GROUP |
CREATE_NO_WINDOW, CREATE_NEW_CONSOLE,
&si, &pi)) &si, &pi))
{
CloseHandle(jo);
job->jv_status = JOB_FAILED; job->jv_status = JOB_FAILED;
}
else 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; job->jv_status = JOB_STARTED;
} }
} }
@@ -5060,12 +5085,10 @@ mch_job_status(job_T *job)
{ {
DWORD dwExitCode = 0; DWORD dwExitCode = 0;
if (!GetExitCodeProcess(job->jv_pi.hProcess, &dwExitCode)) if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
return "dead"; || dwExitCode != STILL_ACTIVE)
if (dwExitCode != STILL_ACTIVE)
{ {
CloseHandle(job->jv_pi.hProcess); job->jv_status = JOB_ENDED;
CloseHandle(job->jv_pi.hThread);
return "dead"; return "dead";
} }
return "run"; return "run";
@@ -5074,14 +5097,39 @@ mch_job_status(job_T *job)
int int
mch_stop_job(job_T *job, char_u *how) mch_stop_job(job_T *job, char_u *how)
{ {
int ret = 0;
int ctrl_c = STRCMP(how, "int") == 0;
if (STRCMP(how, "kill") == 0) if (STRCMP(how, "kill") == 0)
TerminateProcess(job->jv_pi.hProcess, 0); {
if (job->jv_job_object != NULL)
return TerminateJobObject(job->jv_job_object, 0) ? OK : FAIL;
else else
return GenerateConsoleCtrlEvent( return TerminateProcess(job->jv_proc_info.hProcess, 0) ? OK : FAIL;
STRCMP(how, "hup") == 0 ? }
CTRL_BREAK_EVENT : CTRL_C_EVENT,
job->jv_pi.dwProcessId) ? OK : FAIL; if (!AttachConsole(job->jv_proc_info.dwProcessId))
return OK; 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 #endif

View File

@@ -60,6 +60,7 @@ int mch_call_shell(char_u *cmd, int options);
void mch_start_job(char **argv, job_T *job); void mch_start_job(char **argv, job_T *job);
char *mch_job_status(job_T *job); char *mch_job_status(job_T *job);
int mch_stop_job(job_T *job, char_u *how); int mch_stop_job(job_T *job, char_u *how);
void mch_clear_job(job_T *job);
void mch_breakcheck(void); void mch_breakcheck(void);
int mch_expandpath(garray_T *gap, char_u *path, int flags); 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); int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags);

View File

@@ -43,6 +43,7 @@ int mch_call_shell(char_u *cmd, int options);
void mch_start_job(char *cmd, job_T *job); void mch_start_job(char *cmd, job_T *job);
char *mch_job_status(job_T *job); char *mch_job_status(job_T *job);
int mch_stop_job(job_T *job, char_u *how); 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_set_normal_colors(void);
void mch_write(char_u *s, int len); void mch_write(char_u *s, int len);
void mch_delay(long msec, int ignoreinput); void mch_delay(long msec, int ignoreinput);

View File

@@ -1249,7 +1249,8 @@ struct jobvar_S
int jv_exitval; int jv_exitval;
#endif #endif
#ifdef WIN32 #ifdef WIN32
PROCESS_INFORMATION jv_pi; PROCESS_INFORMATION jv_proc_info;
HANDLE jv_job_object;
#endif #endif
jobstatus_T jv_status; jobstatus_T jv_status;

View File

@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1306,
/**/ /**/
1305, 1305,
/**/ /**/