-
Notifications
You must be signed in to change notification settings - Fork 2.5k
global: synchronize initialization and shutdown with pthreads #3974
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
Conversation
I tried to come up with a test, but it wasn't easily possible as our testing frameworks requires global state as well. I used the following test program to verify the issue vanished: #include <pthread.h>
#include "common.h"
void *run(void *data)
{
int i;
for (i = 0; i < 20; i++)
{
git_libgit2_init();
git_libgit2_shutdown();
}
return data;
}
int main(int argc, char *argv[])
{
pthread_t threads[10];
unsigned i;
for (i = 0; i < sizeof(threads) / sizeof(*threads); i++)
{
pthread_create(&threads[i], NULL, run, NULL);
}
for (i = 0; i < sizeof(threads) / sizeof(*threads); i++)
{
pthread_join(threads[i], NULL);
}
return 0;
} |
So I may not be reasoning about this correctly, but I don't think this is quite sufficient. In your sample program, you're I suspect that - independent of threading - if you just Or am I not thinking about this correctly? |
No, we do actually re-initialize the pthread_once thingy. See |
Just to write down my thoughts and why it is correct to reset Usually I'd advice callers to not initialize/shutdown repeatedly, but I think we should nevertheless support this use case. |
🙇 Thank you, I didn't see that. 😪😫😪 So I think that there's still a situation here. Since we increment and decrement outside of the mutex, we could have two threads racing: Thread A enters |
Oh, yeah, you're right. The mutex also has to synchronize |
52e76ed
to
89be214
Compare
The issue should be fixed. I synchronize over |
The failing test is unrelated |
89be214
to
417c3f3
Compare
I accidentally included the example demonstrating the fix. Removed the file |
When trying to initialize and tear down global data structures from different threads at once with `git_libgit2_init` and `git_libgit2_shutdown`, we race around initializing data. While we use `pthread_once` to assert that we only initilize data a single time, we actually reset the `pthread_once_t` on the last call to `git_libgit2_shutdown`. As resetting this variable is not synchronized with other threads trying to access it, this is actually racy when one thread tries to do a complete shutdown of libgit2 while another thread tries to initialize it. Fix the issue by creating a mutex which synchronizes `init_once` and the library shutdown.
2732448
to
2100d8d
Compare
When threading is not enabled for libgit2, we keep global state in a simple static variable. When libgit2 is shut down, we clean up the global state by freeing the global state's dynamically allocated memory. When libgit2 is built with threading, we additionally free the thread-local storage and thus completely remove the global state. In a non-threaded build, though, we simply leave the global state as-is, which may result in an error upon reinitializing libgit2. Fix the issue by zeroing out the variable on a shutdown, thus returning it to its initial state.
Exercise the logic surrounding deinitialization of the libgit2 library as well as repeated concurrent de- and reinitialization. This tries to catch races and makes sure that it is possible to reinitialize libgit2 multiple times. After deinitializing libgit2, we have to make sure to setup options required for testing. Currently, this only includes setting up the configuration search path again. Before, this has been set up once in `tests/main.c`.
2100d8d
to
1c33ecc
Compare
Fixed another issue with re-initialization when not using threads which were caught by a set of new tests |
When trying to initialize and tear down global data structures
from different threads at once with
git_libgit2_init
andgit_libgit2_shutdown
, we race around initializing data. Whilewe use
pthread_once
to assert that we only initilize data asingle time, we actually reset the
pthread_once_t
on the lastcall to
git_libgit2_shutdown
. As resetting this variable is notsynchronized with other threads trying to access it, this is
actually racy when one thread tries to do a complete shutdown of
libgit2 while another thread tries to initialize it.
Fix the issue by creating a mutex which synchronizes
init_once
and the library shutdown.