|
| 1 | +/* A thread created from C++ is the thread that is joined. |
| 2 | + * |
| 3 | + * musl declares `pthread_t' twice: a pointer for C, and `unsigned long' for C++. |
| 4 | + * The two agree wherever a long holds a pointer, which is every architecture |
| 5 | + * musl was written for. Windows is LLP64 --- a long is thirty-two bits --- so a |
| 6 | + * C++ program kept the lower half of the thread's address, and pthread_join read |
| 7 | + * through the truncated value. libc++'s std::thread stores exactly this type, |
| 8 | + * so every std::thread on that system ended in an access violation when joined. |
| 9 | + * |
| 10 | + * ⭐ THE static_assert IS THE CRITERION, and it is a compile-time one: before the |
| 11 | + * change this file does not compile for x86_64-windows-gnu. The run afterwards |
| 12 | + * shows that the value survives the round trip through a started context. |
| 13 | + */ |
| 14 | +#include <pthread.h> |
| 15 | +#include <stdio.h> |
| 16 | + |
| 17 | +static_assert(sizeof(pthread_t) >= sizeof(void*), "pthread_t must hold the address musl stores in it"); |
| 18 | + |
| 19 | +static void* work(void* arg) |
| 20 | +{ |
| 21 | + *static_cast<int*>(arg) = 42; |
| 22 | + return arg; |
| 23 | +} |
| 24 | + |
| 25 | +int main() |
| 26 | +{ |
| 27 | + int failures = 0; |
| 28 | + int value = 0; |
| 29 | + pthread_t thread; |
| 30 | + if (pthread_create(&thread, nullptr, work, &value) != 0) { |
| 31 | + puts("pthread_create failed"); |
| 32 | + return 1; |
| 33 | + } |
| 34 | + void* result = nullptr; |
| 35 | + const int joined = pthread_join(thread, &result); |
| 36 | + printf("joined: %d, value %d, result %s\n", joined, value, result == &value ? "is the argument" : "is not the argument"); |
| 37 | + if (joined != 0) ++failures; |
| 38 | + if (value != 42) ++failures; |
| 39 | + if (result != &value) ++failures; |
| 40 | + printf("-- failures: %d --\n", failures); |
| 41 | + return failures == 0 ? 0 : 1; |
| 42 | +} |
0 commit comments