简体   繁体   中英

Waiting on a process on Windows randomly fails

I have a project which is a compiler. I have an integration test suite for my compiler that compiles the sample source into an object, invokes ld to link it, then invokes the executable and checks the result. Each of these three steps is a fresh process separate from the test driver.

Unfortunately I am seeing random test failures because for some reason, when I come to link, the previous test has not finished running, even though I explicitly wait on termination of each process before beginning the next step. Thus ld fails as it cannot write out the new executable.

I could fix this problem by running each test in a new directory or giving the temp files unique names, but I don't want to do that since this approach should work and I would just be papering over the problem that I can't wait on a process to terminate properly.

Here's my code for starting and waiting for the process:

#include <Windows.h>
#include <iostream>
#include <thread>

class Pipe {
    HANDLE ReadHandle;
    HANDLE writehandle;
public:
    Pipe() {
        SECURITY_ATTRIBUTES saAttr;
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        saAttr.bInheritHandle = TRUE;
        saAttr.lpSecurityDescriptor = NULL;
        CreatePipe(&ReadHandle, &writehandle, &saAttr, 0);
    }
    HANDLE WriteHandle() {
        return writehandle;
    }
    std::string Contents() {
        CloseHandle(writehandle);
        DWORD dwRead;
        CHAR chBuf[1024];
        BOOL bSuccess = FALSE;

        std::string result;
        for (;;)
        {
            bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
            if (!bSuccess || dwRead == 0) break;
            result += std::string(chBuf, chBuf + dwRead);
        }
        return result;
    }
    ~Pipe() {
        CloseHandle(ReadHandle);
    }
};
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout)
{
    auto throw_last_err = [] {
        DWORD dw = GetLastError();
        const char* message;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, nullptr);
        std::string err = message;
        LocalFree((void*)message);
        throw std::runtime_error(err);
    };
    ProcessResult result;
    Pipe stdoutpipe;
    Pipe stderrpipe;
    PROCESS_INFORMATION info = { 0 };
    STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
    std::string final_args = name;
    for (auto arg : args)
         final_args += " " + arg;
    startinfo.hStdOutput = stdoutpipe.WriteHandle();
    startinfo.hStdError = stderrpipe.WriteHandle();
    startinfo.hStdInput = INVALID_HANDLE_VALUE;
    startinfo.dwFlags |= STARTF_USESTDHANDLES;
    auto proc = CreateProcess(
        name.c_str(),
        &final_args[0],
        nullptr,
        nullptr,
        TRUE,
        NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
        nullptr,
        nullptr,
        &startinfo,
        &info
         );
    if (!proc) {
        throw_last_err();        
    }
    if (timeout == 0)
        timeout = INFINITE;

    std::thread writethread([&] {
        result.std_out = stdoutpipe.Contents();
    });
    std::thread errthread([&] {
        result.std_err = stderrpipe.Contents();
    });

    auto waiterr = WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE);
    if (waiterr == WAIT_TIMEOUT) {
        TerminateProcess(info.hProcess, 1);
        waiterr = WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE);
        if (waiterr != WAIT_OBJECT_0) {
            throw_last_err();
        }
    } else if (waiterr != WAIT_OBJECT_0) {
        throw_last_err();
    }

    writethread.join();
    errthread.join();
    DWORD exit_code;
    GetExitCodeProcess(info.hProcess, &exit_code);
    CloseHandle(info.hProcess);
    CloseHandle(info.hThread);
    result.exitcode = exit_code;
    if (exit_code != 0)
        return result;
    return result;
}

throw_last_err() is never called, so everything went fine, supposedly.

Why can I not wait on the process?

According to the comments, various Windows components like Search can hold locks on random files. This implies to me that in general, I can't assume that the file won't be locked and that therefore I should not assume that I can re-use it immediately.

Therefore I've decided that I will simply not re-use the intermediate files.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM