Implement thread synchronization and a bunch of stuff #8
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Finally I could take some time to make a proper PR...
Lets start by the bunch of related stuff:
About thread synchronization now.
Here is the few issues we want to address:
Point 1
The first thing to test when you'll see this PR is 1!
My first try for this was to register the calling address to
SDL_Init
, and use aptrdiff
with the entry points given to pthread_create on thread creation, assuming it would be the same regardless of the platform (as I expected both addresses to be in the same non-relocatable binary executable).It worked ok for SMB (which is one big executable), but games such as FEZ may not be monolithic.
The loading stuff for FEZ seems to be in a separate library, which means it's relocatable relative to the main binary, and that simply invalidate this first approach.
I managed to get better result by identifying each thread using the
ptrdiff
between the routine's address and the caller address.This approach will break if the routine passed to
pthread_create
is not part of the same object file as its caller. Lets just hope you don't want to TAS games like that.The value identifying each thread should therefore be only dependent on the game executable.
Two different entry points may have the same id only if their caller has the same relative position, which is very unlikely.
So when you checkout this, please activate the
LCF_THREAD
messages intaskflags.cpp
, and run the 64bit version of FEZ (we should have the same executable).Just open it, go to the main menu and close it, at the end of the execution you should have a summary of the run looking like this:
All these number can change but one: you should have "-557" or else we should find another way of identifying threads :s
Point 2
Tracking every thread creation make it easy to generate a summary as above: I register which entry points have been created under which
pthread_id
(which tells us the number of time a particular entry point has been called).Playing the game through a couple of level should give a good overview about which entry points are used, and tell us which one we should wait for.
For instance during my test with FEZ, entry point with id 2003 was only started once at the beginning, so it was probably related to automatic save, that doesn't lead to desync during the game (so we don't care).
However -557 was created at every load, which means we do care about it.
Ideally we would detect that automatically, but we could also simply create a simple database for known game executables.
Point 3
Here is the fun part.
As far as I could test, games create threads and detach them at the end of the routine execution, instead of joining them.
This is probably because the main thread doesn't want to sleep and want to continue to render frames during loading phases.
So the idea is the following:
Here 2 is not straightforward, as we want to pause the main thread from another thread: we cannot use pthread's structures like conditions to do that.
I did this through a custom sighandler for
SIGUSR1
.pthread_kill
enable us to send to a givenpthread_t
a specific signal, so we register the main thread'spthread_t
, and we sendSIGUSR1
to it when we want to suspend it.The custom sighandler is a simple spin on an
int
. Whenresume
is called, the spinning variable is reset, allowing the main thread to resume its execution.Here is the fez input file I used for this video: https://we.tl/zekoUXg748 (will disappear in 7 days).
So if the magic number matches, a good test would be to run
./run.sh -r fez.input pat/to/FEZ.bin.x86_64
and see if you can replay this :p (assuming the first save loads the same level obviously).Misc
I tried to hook the
pthread_cond
primitives but couldn't do it with the last master for some reasons that I didn't look into. I removed this in 7a08a60, it may give you ideas about what else I tried to track and understand the synchronizations in the game.I hope I described my approach clearly, please let me know if some points are still obscure.