在Windows上等待进程随机失败

Waiting on a process on Windows randomly fails

本文关键字:随机 失败 进程 等待 Windows      更新时间:2023-10-16

我有一个项目是编译器。我的编译器有一个集成测试套件,它将样本源编译成一个对象,调用ld链接它,然后调用可执行文件并检查结果。这三个步骤中的每一个都是独立于测试驱动程序的全新过程。

不幸的是,我看到了随机测试失败,因为出于某种原因,当我进入链接时,上一个测试还没有完成运行,尽管我明确地等待每个进程的终止,然后再开始下一步。因此ld失败,因为它无法写出新的可执行文件。

可以通过在新目录中运行每个测试或为临时文件提供唯一的名称来解决这个问题,但我不想这样做,因为这种方法应该有效,我只是在掩盖我不能等待进程正确终止的问题。

这是我启动和等待流程的代码:

#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()从未被调用,所以一切都很顺利。

为什么我不能等待流程?

根据评论,像Search这样的各种Windows组件可以锁定随机文件。这对我来说意味着,一般来说,我不能假设文件不会被锁定,因此我不应该假设我可以立即重用它。

因此,我决定不再重复使用中间文件。