-
-
Notifications
You must be signed in to change notification settings - Fork 32k
gh-130115: fix thread identifiers for 32-bit musl #130391
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
Changes from 8 commits
51d6579
a9ca268
f6ad25d
b93868e
7a4de5f
af1233b
7bdfd34
dc7c497
140a4d8
63812b5
4ae6d7e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Fix an issue with thread identifiers being sign-extended on some platforms. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -306,16 +306,34 @@ do_start_joinable_thread(void (*func)(void *), void *arg, pthread_t* out_id) | |
return 0; | ||
} | ||
|
||
/* Helper to convert pthread_t to PyThread_ident_t. POSIX allows pthread_t to be | ||
non-arithmetic, e.g., musl typedefs it as a pointer */ | ||
static PyThread_ident_t | ||
_pthread_t_to_ident(pthread_t value) { | ||
#if SIZEOF_PTHREAD_T > SIZEOF_LONG | ||
return (PyThread_ident_t) *(unsigned long *) &value; | ||
#else | ||
PyThread_ident_t ident; | ||
#if defined(__linux__) && !defined(__GLIBC__) | ||
ident = (PyThread_ident_t) (uintptr_t) value; | ||
assert(pthread_equal(value, (pthread_t) (uintptr_t) ident)); | ||
#else | ||
ident = (PyThread_ident_t) value; | ||
assert(pthread_equal(value, (pthread_t) ident)); | ||
#endif | ||
return ident; | ||
#endif // SIZEOF_PTHREAD_T > SIZEOF_LONG | ||
} | ||
|
||
int | ||
PyThread_start_joinable_thread(void (*func)(void *), void *arg, | ||
PyThread_ident_t* ident, PyThread_handle_t* handle) { | ||
pthread_t th = (pthread_t) 0; | ||
if (do_start_joinable_thread(func, arg, &th)) { | ||
return -1; | ||
} | ||
*ident = (PyThread_ident_t) th; | ||
*ident = _pthread_t_to_ident(th); | ||
*handle = (PyThread_handle_t) th; | ||
assert(th == (pthread_t) *ident); | ||
assert(th == (pthread_t) *handle); | ||
return 0; | ||
} | ||
|
@@ -328,11 +346,7 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) | |
return PYTHREAD_INVALID_THREAD_ID; | ||
} | ||
pthread_detach(th); | ||
#if SIZEOF_PTHREAD_T <= SIZEOF_LONG | ||
return (unsigned long) th; | ||
#else | ||
return (unsigned long) *(unsigned long *) &th; | ||
#endif | ||
return (unsigned long) _pthread_t_to_ident(th);; | ||
} | ||
|
||
int | ||
|
@@ -357,8 +371,7 @@ PyThread_get_thread_ident_ex(void) { | |
if (!initialized) | ||
PyThread_init_thread(); | ||
threadid = pthread_self(); | ||
assert(threadid == (pthread_t) (PyThread_ident_t) threadid); | ||
return (PyThread_ident_t) threadid; | ||
return _pthread_t_to_ident(threadid); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is potentially lossy compared to the previous version if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I understand your concern. This was an edge case in #if SIZEOF_PTHREAD_T <= SIZEOF_LONG
return (unsigned long) th;
#else
return (unsigned long) *(unsigned long *) &th; It's probably fine to drop the condition in Looking at the history: The cast through ulong* was for Alpha OSF which was dropped from PEP 11 a few years ago (CPython 3.3) https://bugs.python.org/issue8606. My guess is it was also defined as a pointer or struct type and this was a way to work around it by returning at least I think if we find other platforms where we need to support this workaround, they can be chained to the MUSL If others agree, I can drop the condition so the function looks like so: static PyThread_ident_t
_pthread_t_to_ident(pthread_t value) {
PyThread_ident_t ident;
#if defined(__linux__) && !defined(__GLIBC__)
ident = (PyThread_ident_t) (uintptr_t) value;
assert(pthread_equal(value, (pthread_t) (uintptr_t) ident));
#else
ident = (PyThread_ident_t) value;
assert(pthread_equal(value, (pthread_t) ident));
#endif
return ident;
} I do not suggest this as a long term solution; I do think we need to work towards making this opaque. I'm just trying to find something that is a stop-gap that can be ported back with relative ease that doesn't cause a regression. |
||
} | ||
|
||
unsigned long | ||
|
Uh oh!
There was an error while loading. Please reload this page.
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.
Why go through a pointer? This is cryptic and also uncomfortably fragile (what about big-endian platforms?).
I would suggest perhaps something like:
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.
That was legacy code that I can certainly drop (as mentioned above) . This is definitely simpler, I'll give it a spin and push it if tests pass.
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've updated the branch to this implementation. The unit tests continue to pass AFAICT.