From ed82eae13a8254d38b88e480cc14b92b26ea266a Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 22 Aug 2013 17:49:27 +0200 Subject: [PATCH] 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. --- src/win/process.c | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/win/process.c b/src/win/process.c index 3230bb2e..7f1c10dc 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -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); }