Skip to content

Delay to close application after returning false from OnUserUpdate on Windows #312

Open

Description

@fusion32

Hello, I was trying out the PGE in preparation for the game jam, and noticed that (at least on Windows) returning false from OnUserUpdate() didn't close the window until after some input/window event. After some digging, it seems that Host_Windows_WinAPI::systemActive is being updated to false, but because GetMessage() is blocking the event thread, the update is not seen until an event is received. I did a small modification to Host_Windows_WinAPI::StartSystem() that seems to address it. I'm not too sure about other hosts, but the one for X11 seems to do a hot polling loop which should avoid this issue, besides consuming a lot more CPU.

bool Host_Windows_WinAPI::StartSystem()
{
    // Pre-context start hook
    pPrimaryPGE->OnPreContextStart();

    // Mark system as active
    systemActive = true;

    //##########################################################################
    // This is the modified bit.

    // Create system thread - handles gpu context
    DWORD windowThreadId = GetCurrentThreadId();
    std::thread threadSystem([this, windowThreadId]()
        {
            if(OnSystemThreadStart())
            {
                while(systemActive && OnSystemTick())
                {
                    continue;
                }

                OnSystemThreadEnd();
            }

            // Update `systemActive` to false and post a WM_NULL message to make
            // sure the blocking event loop below doesn't hang until an actual
            // event arrives.
            systemActive = false;
            PostThreadMessage(windowThreadId, WM_NULL, 0, 0);
        });

    // Blocking event loop on this thread - handles windows. Note that we check
    // whether the system is still active before calling GetMessage to make sure
    // we don't miss updates to systemActive after DispatchMessage returns.
    MSG msg;
    while(systemActive && GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    //##########################################################################

    systemActive = false;
    if(threadSystem.joinable())
        threadSystem.join();

    // Post-context end hook
    return pPrimaryPGE->OnPostContextEnd();
}

PS: On a slightly different subject, I was trying to figure out how input events are captured and sent to the system thread, and I could have missed something but it may be missing synchronization (at least on Windows, again)? Going back to to Host_Windows_WinAPI::StartSystem(), the call stack will look roughly like DispatchMessage() > WINAPI_EventHandler() > host->OnWindowEvent() > window->olc_OnXXX, and window state (mouse, keyboard, size, etc...) which is also accessed from the system thread is not being synchronized.

EDIT: So my modification did fix the issue but only partially. After some more testing, the window/event thread would hang indefinitely if the WM_QUIT message was posted while the window was being moved or resized, because it would be discarded by some modal loop. I did some more digging and it seems that posting a WM_QUIT is not quite the same as PostQuitMessage(), as the later would also set some flag to the thread's queue that would cause it to generate WM_QUIT events whenever it was empty, which should propagate all the way up. It isn't possible to call PostQuitMessage() from another thread tho, so I modified Host_Windows_WinAPI::StartSystem() again (above), but this time PostThreadMessage() is only used to wake up the event loop.

Activity

  1. self-assigned this
    on Oct 23, 2026
  2. Johnnyg63 commented on Oct 23, 2026

    @Johnnyg63
    Collaborator

    It was the onUserCreate() method that was having issues when False was returned, corrected in PR #342

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions