Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only call GLUT functions from the GLUT thread. #270

Merged
merged 7 commits into from
Dec 3, 2022

Conversation

mkilgore
Copy link
Contributor

The main goal of this PR is fixing #66, which was necessary to fix to get my GLUT test suite working consistently (as without it being fixed, programs simply explode too often and fail the tests). This required the addition of some new code, which I placed into the libqb/src. This gave a good opportunity for moving all the GLUT initialization (which is fairly self-contained) into libqb/src as well, which what a lot of the changes here are about. The commits contain fairly detailed explanations of the changes so I would refer to those, but here's an overview:

To fix #66 we needed a way of telling the GLUT thread to perform GLUT actions when the program wants to do them, which I implemented in glut-message.h, glut-message.cpp, and glut-msg-queue.cpp. It's fairly simple, we have a std::queue protected with a mutex, and the GLUT idle or timer function (depending on platform) calls a function to process the queue. Additionally there are libqb_glut_* functions which can be called in the same way as the original glut functions and simply add commands to the queue. For most commands we don't wait for them to be completed, but for glutGet() we use a completion to wait for the command to be executed and then get the result.

The rest of the changes are refactoring the GLUT stuff into libqb/src. For the most part it's just a copy-paste into new functions and then wiring those back up to libqb.cpp. $Console:Only vs. GLUT have different cpp files for linking (replacing the #ifdef logic in libqb.cpp).

Additionally I was very happy that this refactor removed the little bit of Objective-C++ that was living in libqb.cpp, so I put that in a .mm file in libqb/src/ and was able to remove the libqb.mm file.

Fixes: #66

@mkilgore mkilgore added the bug Something isn't working label Nov 28, 2022
@SteveMcNeill
Copy link
Member

Will this also allow us some better control over the DPI Awareness settings? People keep asking about them, and if we can enable/disable them. The last time I was playing around with them, they seemed to be interwoven with the same glut processes that you're moving around and tweaking here for us.

@mkilgore
Copy link
Contributor Author

@SteveMcNeill I haven't looked into it that closely but I wouldn't expect these changes to make it any easier. Really I didn't change a whole lot here, we call things slightly differently to avoid the threading issues from #66 but all the GLUT logic is the same.

These tests cover all the commands that generally interact with GLUT.
The ensure that these functions can be used at the very beginning of a
program with no issues. Additionally they verify the behavior of these
functions in the presence of `$SCREENHIDE`, and also `_ScreenHide`.
Xvfb is being used to give us an X server implementation on the Linux
build agents. A running X server is necessary for graphics to function
(which we have so far avoided testing).
This fixes all the code so that all the calls to glut functions
happen on the same thread that is running GLUT.

We achieve this by creating a queue of GLUT commands to execute.
Commands can be added to the queue anywhere in the code, and then the
queue is processed on the GLUT thread via it's idle func or timer func.
The command is run and if necessary the result is provided in the
message queue object. Each object contains a completion which can be
waited on to block until the GLUT thread has processed the command.

Fixes: QB64-Phoenix-Edition#66
With the recent changes to libqb.cpp to pull out some of the GLUT logic,
the only actual Objective-C in libqb.cpp was pulled out. That being the
case, it's no longer necessary to have libqb.mm for compiling libqb.cpp,
so we're removing it to simplify the compliation logic a bit.
Fairly straightfowrad, programs were randomly seg faulting on exit. This
was happening due to GLUT registering a cleanup function via atexit(),
which then gets called when exit() is called.

The issue happens when exit() is called on a thread other than the GLUT
thread, which leads to the exit() call then attempting to cleanup GLUT
while the other thread is still using it, which randomly leads to seg
faults.

Fixing this is slightly annoying. We cannot stop the GLUT thread, as
the basic GLUT API (which is used on Mac OS) simply does not offer a way
to exit the glutMainLoop() call. Thus the simplest solution is to simply
make sure we call exit() on the GLUT thread, which we can fairly easily
due via the message queue.

That being the case, a new libqb_exit() API was added, which simply
regsiters the GLUT exit message and then waits for the program to end.
The atexit() handler then runs on the GLUT thread and everything works
out fine.

In the future we probably should redo the exit logic a bit so that all
the threads are actually stopped/joined to ensure the exit process is
consistent, however this is good enough for now. Also, there's plenty of
error states which call exit() which I did not address.
The commands _ScreenX and _ScreenY got significantly slower due to the
need to wait for the GLUT thread to wake up and execute the glutGet()
command for them. We've already seen a few programs (including the IDE)
where this behavior completely grinds the program to a halt, so we
definitely can't keep it.

The simple solution here is to not call glutGet() on every _ScreenX/Y
command. Instead every time the idle/timer function runs we get the
current values for the relevant glutGet() variables and store them.
libqb_glut_get() then checks if the value being read is one of the ones
we read in the idle/timer functionand if so just returns the last read
value. By doing it this way the commands no longer has to wait on the
GLUT thread for the result.
@mkilgore mkilgore merged commit acf918b into QB64-Phoenix-Edition:main Dec 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

glutIconifyWindow(), glutHideWindow(), glutShowWindow() should not be called outside of the GLUT thread
4 participants