0

I am trying to make a judge system on Windows using C++ (please don't tell me anything about Linux). Thus, I have to run the precompiled code and measure its CPU time and memory consumption. At first I used the CreateProcess to run the precompiled code, WaitForSingleObject to wait the end of the process and measured the time using getMillisecondsNow() and the memory using std::async(). It didn't work, the application terminated almost immediately before it get exit code from precompiled code. I read on forums that this is due to the fact that the tested code creates a process, but there were just for loop with random integers output. After that I read The Old New Thing and started use GetQueuedCompletionStatus to wait the end of the process. It is now waiting for the program to terminate. Always. I cannot make time limits.

First attempt

void myJudger(std::wstring aName, std::wstring aInputFilePath, std::wstring aOutputFilePath)
{
    long long timeUsage = 1000;
    long long reservedTime = 200;
    long long memoryLimit = 200;

    PROCESS_INFORMATION processInfo;
    ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));

    STARTUPINFOW startupInfo = { 0 };
    IORedirection(startupInfo, aInputFilePath, aOutputFilePath);

    wchar_t* cmd = const_cast<wchar_t*>(aName.c_str());
    auto feature = std::async(std::launch::async, getMaxMemoryUsage, std::ref(processInfo), memoryLimit);
    if (CreateProcess(NULL, cmd,
        NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo) == FALSE)
    {
        std::cout << "ERROR #2: can't start process" << std::endl;
    }
    long long startTime = getMillisecondsNow();
    ResumeThread(processInfo.hThread);
    //DWORD waitResult = 
    DWORD waitResult;
    while ((waitResult = WaitForSingleObject(processInfo.hProcess, INFINITE)) == STILL_ACTIVE)
    {
        std::cout << "working...\n";
    }
    if (waitResult == WAIT_FAILED)
    {
        std::cout << "ERROR #3: WAIT_FAILED" << std::endl;
    }
    if (waitResult == WAIT_OBJECT_0)
    {
        std::cout << "Code complite\n" << std::endl;
    }
    if (waitResult == WAIT_TIMEOUT)
    {
        std::cout << "Time out\n" << std::endl;
    }
    if (getExitCode(processInfo.hProcess) == STILL_ACTIVE) {
        std::cout << "Alive" << std::endl;
        killProcess(processInfo);
    }
    long long endTime = getMillisecondsNow();
    long long memoryUsage = feature.get();

    std::cout << "time usage: " << endTime - startTime << std::endl;
    std::cout << "memory usage: " << memoryUsage << std::endl;
}

Second attempt

void myJudger3(std::wstring aName, std::wstring aInputFilePath, std::wstring aOutputFilePath)
{
    CHandle Job(CreateJobObject(nullptr, nullptr));
    if (!Job) ERRORCOUT(1, "create job object");

    CHandle IOPort(CreateIoCompletionPort(INVALID_HANDLE_VALUE,
        nullptr, 0, 1));
    if (!IOPort) ERRORCOUT(2,"create IO completion port");

    JOBOBJECT_ASSOCIATE_COMPLETION_PORT Port;
    Port.CompletionKey = Job;
    Port.CompletionPort = IOPort;
    if (!SetInformationJobObject(Job,
        JobObjectAssociateCompletionPortInformation,
        &Port, sizeof(Port))) ERRORCOUT(3, "set information");

    PROCESS_INFORMATION ProcessInformation;
    STARTUPINFO StartupInfo = { sizeof(StartupInfo) };
    IORedirection(StartupInfo, aInputFilePath, aOutputFilePath);
    wchar_t* cmd = const_cast<wchar_t*>(aName.c_str());

    long long memoryLimit = 2e6;
    auto feature = std::async(std::launch::async, getMaxMemoryUsage, std::ref(ProcessInformation), memoryLimit);

    if (!CreateProcess(nullptr, cmd, nullptr, nullptr,
        TRUE, CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED | CREATE_NO_WINDOW, nullptr, nullptr,
        &StartupInfo, &ProcessInformation))
        ERRORCOUT(4, "create process");

    if (!AssignProcessToJobObject(Job,
        ProcessInformation.hProcess)) ERRORCOUT(4, "assign process");

    long long startTime = getMillisecondsNow();
    ResumeThread(ProcessInformation.hThread);


    uint_64 memoryUsage = feature.get();

    CloseHandle(ProcessInformation.hThread);
    CloseHandle(ProcessInformation.hProcess);

    DWORD CompletionCode;
    ULONG_PTR CompletionKey;
    LPOVERLAPPED Overlapped;

    GetQueuedCompletionStatus(IOPort, &CompletionCode,
        &CompletionKey, &Overlapped, 1000);
    std::cout  << getMillisecondsNow() - startTime << std::endl;
    long long endTime = getMillisecondsNow();

    std::cout << "\n\ntime usage: " << endTime - startTime << std::endl;
    std::cout << "\n\nmemory usage: " << memoryUsage << std::endl;
}

Code to test

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        cout << rand() << "\n";
    }
}
r_comrad
  • 9
  • 1
  • 1
    I think for a judge system it is important to restrict resources rather than measure them. – user7860670 May 30 '21 at 13:03
  • 4
    *"PLEASE HELP, I CAN'T DO IT ANYMORE, IT DOESN'T WORK, I DON'T UNDERSTAND WHY, WHAT SHOLD I DO"* is not a question. Please take the [tour] and read [ask]. – IInspectable May 30 '21 at 13:14
  • I can't restrict resources: the program either always terminates immediately or always waits for the execution to finish. – r_comrad May 30 '21 at 13:27
  • 2
    You created a [job object](https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects). It's trivial to [enforce time or memory limits](https://learn.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-setinformationjobobject) for this job. – IInspectable May 30 '21 at 13:48
  • 1
    According to the documentation on the function [WaitForSingleObject](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject), that function will never return `STILL_ACTIVE` (`0x103`). Therefore, why are you checking for that return value? Did you confuse that function with `GetExitCodeProcess`, which can return that value? – Andreas Wenzel May 30 '21 at 13:49
  • What do the functions `getExitCode` and `killProcess` do? Are those custom functions that call `GetExitCodeProcess` and `TerminateProcess`? – Andreas Wenzel May 30 '21 at 14:01
  • getExitCode is just GetExitCodeProcess in my small function, sorry for that. killProcess function for terminating process. – r_comrad May 30 '21 at 14:11
  • In my first attempt I used this https://github.com/hzxie/voj/blob/master/judger/src/main/cpp/windows/Judger.Core.Runner.cpp – r_comrad May 30 '21 at 14:13
  • About WaitForSingleObject, I assume that I saw somewhere this check and therefore use it myself. – r_comrad May 30 '21 at 14:16
  • Is there a sample of using SetInformationJobObject for C++, I only find it fore C# https://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net – r_comrad May 30 '21 at 14:28
  • Instantiate the appropriate information struct, initialize it with the values you want and pass a pointer and the struct's size. There is no magic here. It's just your average overloaded C API design. – IInspectable May 30 '21 at 14:35
  • I'll try but it's definitely dark magic. – r_comrad May 30 '21 at 15:18
  • 1
    @r_comrad this is what happens when you just copy/paste other people's code without taking the time to study it, read it's documentation, understand what it is actually doing. – Remy Lebeau May 30 '21 at 16:42
  • It is recommended to use [Event Tracing](https://learn.microsoft.com/en-us/windows/win32/etw/event-tracing-portal). There are some discussions in this [Q&A](https://learn.microsoft.com/en-us/answers/questions/355597/looking-for-the-right-api-to-get-seprate-disk-and.html). – YangXiaoPo-MSFT May 31 '21 at 06:58

0 Answers0