windows: make uv_spawn() failure more predictable

* It will now report some types of errors synchronously, to bring it on
  par with uv-unix. Fixes #865.

* Failure to find the target executable will now set up stdio pipes
  correctly, so the user can assume that when uv_spawn() returns 0 these
  pipes will never trigger asserts in libuv's guts.
This commit is contained in:
Bert Belder 2013-08-22 17:49:27 +02:00
parent cd2794c01f
commit ed82eae13a

View File

@ -836,25 +836,25 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
err = uv_utf8_to_utf16_alloc(options.file, &application);
if (err)
goto done;
goto immediate_failure;
err = make_program_args(options.args,
options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
&arguments);
if (err)
goto done;
goto immediate_failure;
if (options.env) {
err = make_program_env(options.env, &env);
if (err)
goto done;
if (err)
goto immediate_failure;
}
if (options.cwd) {
/* Explicit cwd */
err = uv_utf8_to_utf16_alloc(options.cwd, &cwd);
if (err)
goto done;
goto immediate_failure;
} else {
/* Inherit cwd */
@ -863,59 +863,60 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
cwd_len = GetCurrentDirectoryW(0, NULL);
if (!cwd_len) {
err = GetLastError();
goto done;
goto immediate_failure;
}
cwd = (WCHAR*) malloc(cwd_len * sizeof(WCHAR));
if (cwd == NULL) {
err = ERROR_OUTOFMEMORY;
goto done;
goto immediate_failure;
}
r = GetCurrentDirectoryW(cwd_len, cwd);
if (r == 0 || r >= cwd_len) {
err = GetLastError();
goto done;
goto immediate_failure;
}
}
/* Get PATH environment variable. */
/* Get PATH environment variable. */
{
DWORD path_len, r;
path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
if (path_len == 0) {
err = GetLastError();
goto done;
goto immediate_failure;
}
path = (WCHAR*) malloc(path_len * sizeof(WCHAR));
if (path == NULL) {
err = ERROR_OUTOFMEMORY;
goto done;
goto immediate_failure;
}
r = GetEnvironmentVariableW(L"PATH", path, path_len);
if (r == 0 || r >= path_len) {
err = GetLastError();
goto done;
goto immediate_failure;
}
}
err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
if (err)
goto immediate_failure;
/* Beyond this point, failure is reported asynchronously. */
application_path = search_path(application,
cwd,
path);
if (application_path == NULL) {
/* Not found. */
err = ERROR_FILE_NOT_FOUND;
goto done;
goto success_or_async_failure;
}
err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
if (err)
goto done;
startup.cb = sizeof(startup);
startup.lpReserved = NULL;
startup.lpDesktop = NULL;
@ -1013,7 +1014,9 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
err = GetLastError();
}
done:
/* We get here if we successfully created a process, or when we */
/* encountered failure that we want to report asynchronously. */
success_or_async_failure:
free(application);
free(application_path);
free(arguments);
@ -1040,6 +1043,20 @@ done:
}
return 0;
/* This code path is taken when we run into an error that we want to */
/* report immediately. */
immediate_failure:
free(application);
free(application_path);
free(arguments);
free(cwd);
free(env);
free(path);
assert(process->child_stdio_buffer == NULL);
return uv_translate_sys_error(err);
}