-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Move most of Pthread.initRuntime
into native code. NFC
#14775
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! I added a small inline comment, which might also be resolved in a separate PR.
// XXX EMSCRIPTEN: self->tsd can be NULL in the case of the | ||
// main thread where pthread_key_create is not called. | ||
// See __pthread_key_create for where it get assigned. | ||
if (!self->tsd) return NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this check, perhaps we could ensure that self->tsd
is always initialized? This also matches musl's convention, which seems to always assign it to __pthread_tsd_main
once the first thread is created:
emscripten/system/lib/libc/musl/src/thread/pthread_create.c
Lines 162 to 163 in 4d864df
static void *dummy_tsd[1] = { 0 }; | |
weak_alias(dummy_tsd, __pthread_tsd_main); |
self->tsd = (void **)__pthread_tsd_main; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that we have at least one test that calls pthread_getspecific
before any threads are created and expects it to work: posixtest.test_pthread_key_create_2_1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or at least the test expects it to return NULL and not crash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, that test looks odd. When compiling with GCC it also complains that key is not initialized.
conformance/interfaces/pthread_key_create/2-1.c:33:14: error: ‘key’ is used uninitialized [-Werror=uninitialized]
33 | rc = pthread_getspecific(key);
| ^~~~~~~~~~~~~~~~~~~~~~~~
conformance/interfaces/pthread_key_create/2-1.c:29:23: note: ‘key’ declared here
29 | pthread_key_t key;
| ^~~
cc1: all warnings being treated as errors
But running it locally and on Alpine (which also uses musl) can be done without any problems.
$ cd ./tests/third_party/posixtestsuite
$ docker run -v $(pwd):/app -w /app -it alpine:latest \
apk add build-base && \
POSIX_TARGET=conformance/interfaces/pthread_key_create make LDFLAGS='-pthread -Wno-error=uninitialized'
...
conformance/interfaces/pthread_key_create/2-1: build: PASS
conformance/interfaces/pthread_key_create/2-1: link: PASS
...
conformance/interfaces/pthread_key_create/2-1: execution: PASS
Do you think it makes sense to initialize tsd
in __emscripten_pthread_data_constructor
? Similar to the logic in pthread_key_create
.
emscripten/system/lib/libc/musl/src/thread/pthread_key_create.c
Lines 22 to 24 in 8192916
/* This can only happen in the main thread before | |
* pthread_create has been called. */ | |
if (!self->tsd) self->tsd = __pthread_tsd_main; |
Though, the current approach would also be sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, my test command for Alpine was wrong (it ran the test on my own system). This also seems to segfault on Alpine.
$ docker run -v $(pwd):/app -w /app -it alpine:latest sh -c "\
apk add build-base && \
POSIX_TARGET=conformance/interfaces/pthread_key_create make LDFLAGS='-pthread -Wno-error=uninitialized'"
$ cat logfile
conformance/interfaces/pthread_key_create/1-1: build: PASS
conformance/interfaces/pthread_key_create/1-1: link: PASS
conformance/interfaces/pthread_key_create/1-2: build: PASS
conformance/interfaces/pthread_key_create/1-2: link: PASS
conformance/interfaces/pthread_key_create/2-1: build: PASS
conformance/interfaces/pthread_key_create/2-1: link: PASS
conformance/interfaces/pthread_key_create/3-1: build: PASS
conformance/interfaces/pthread_key_create/3-1: link: PASS
conformance/interfaces/pthread_key_create/speculative/5-1: build: PASS
conformance/interfaces/pthread_key_create/speculative/5-1: link: PASS
conformance/interfaces/pthread_key_create/1-1: execution: PASS
conformance/interfaces/pthread_key_create/1-2: execution: PASS
conformance/interfaces/pthread_key_create/2-1: execution: INTERRUPTED: Output:
Segmentation fault (core dumped)
conformance/interfaces/pthread_key_create/3-1: execution: PASS
conformance/interfaces/pthread_key_create/speculative/5-1: execution: FAILED: Output:
Test FAILED: EAGAIN was returned before the key limit was exceeded
Perhaps we should report this upstream.
This also avoids the use of malloc both for the main pthread struct and for its TLS vars. This in turn avoids the use of `withBuiltinMalloc` since we no longer use malloc here.
3637f1e
to
09c8871
Compare
// The pthread struct has a field that points to itself - this is used as | ||
// a magic ID to detect whether the pthread_t structure is 'alive'. | ||
__main_pthread.self = &__main_pthread; | ||
// pthread struct robust_list head should point to itself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation looks wrong here
// pthread struct robust_list head should point to itself. | |
// pthread struct robust_list head should point to itself. |
// a magic ID to detect whether the pthread_t structure is 'alive'. | ||
__main_pthread.self = &__main_pthread; | ||
// pthread struct robust_list head should point to itself. | ||
__main_pthread.robust_list.head = &__main_pthread.robust_list; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__main_pthread.robust_list.head = &__main_pthread.robust_list; | |
__main_pthread.robust_list.head = &__main_pthread.robust_list.head; |
This would avoid depending on head
being the initial field, or is that bad for some reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I was just trying to mimic exactly what the JS code was doing. I don't really understand the purpose of this assignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me either, but the actual address is identical in both cases - just that writing .head
would still work if we reorder the struct in the future?
var tlsMemory = _malloc({{{ cDefine('PTHREAD_KEYS_MAX') * 4 }}}); | ||
for (var i = 0; i < {{{ cDefine('PTHREAD_KEYS_MAX') }}}; ++i) HEAPU32[tlsMemory/4+i] = 0; | ||
Atomics.store(HEAPU32, (tb + {{{ C_STRUCTS.pthread.tsd }}} ) >> 2, tlsMemory); // Init thread-local-storage memory array. | ||
Atomics.store(HEAPU32, (tb + {{{ C_STRUCTS.pthread.tid }}} ) >> 2, tb); // Main thread ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand where the code above for tb.self etc. has moved to (C), but what now handles the TLS init?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed I probably should have explained that. musl uses statically allocated tsd
for the main thread.
It gets assigned on first use here:
if (!self->tsd) self->tsd = __pthread_tsd_main; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we can avoid malloc completely!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, thanks... and nice to avoid malloc!
So this is for pthread_key*
- what about C/C++ level TLS? Is that handled in another way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, C/C++ TLS variables are handled separately to the dynamically created TLS keys in pthread_key_create
Oops, once again I approved a PR without noticing it had automerge, so there are still some comments from me and @kleisauke . Sorry about that, I really have to be careful about looking for automerge all the time... |
Sorry @kripken I will avoid using auto-merge so much in future. And I will address all the comments here. |
No, I think it's fine to use auto-merge... I just need to get used to it. |
This also avoids the use of
malloc
both for the main pthread struct andfor its TLS vars. This in turn avoids the use of
withBuiltinMalloc
since we no longer use
malloc
here.