-
Notifications
You must be signed in to change notification settings - Fork 203
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
Implement a stub pthreads library for THREAD_MODEL=single
#518
Conversation
does this break pthread detection mechanisms like cmake find_package(Threads)? |
I don't know. I don't use cmake beyond building other software that uses it. Several of the comments in the linked issue are implying that, even if it might, not having these stubs is causing even more difficulty with porting software. |
3d5abda
to
a6adc10
Compare
having a stub for mutex etc is probably fine. otoh, i feel it isn't a great idea to provide pthread_create. |
This is one part of breaking up #518 into smaller PRs. This should be (IMO) the least-controversial batch of commits
This is the next part of breaking up #518 into smaller PRs. This is the rest of the commits which change the existing `THREAD_MODEL=posix` functionality. It: * _removes_ some functions which are optional and which were already nonfunctional. * (Continues to) establish a precedent of trying to remain as compatible with Open Group specifications as possible, even when there are major differences in WASI capabilities (i.e. thread cancellation) Compared to the RFC PR, the `pthread_atfork` stub has been dropped as it is now officially obsolete as of the very recent Issue 8 of the specifications.
THREAD_MODEL=single
THREAD_MODEL=single
f47ff55
to
80e0c80
Compare
Although there are also thoughts of potentially changing it, libcxx currently does require/expect pthread_create to exist. Incidentally, it performs detection whether or not pthreads is supported by looking for |
I've rebased this PR and fixed the issues that CI uncovered. I do wish to merge this, but a decision needs to be made about how to handle the "pthreads detection in existing code" issue. |
@@ -0,0 +1,21 @@ | |||
|
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.
Should we call this directory pthread-stubs
?
And maybe it should live at the top level alongside dlmalloc
and emmalloc
(which override/replace libc functions too). @sunfishcode any preference?
ping? |
anyway, libcxx is not a representative application, i suspect. can you explain why having a stub pthread_create is important for you? |
The immediate reason is: I want to be able to build clang+LLVM for WASI (using @whitequark's patchset), which requires threading functions inside libcxx. The usage of types such as As far as I can tell, the only possible choices are:
The tl;dr is "I need libcxx. I am trying to avoid having to put in all of the effort needed to enable a new 'libcxx has synchronization primitives but no thread creation' build mode for libcxx. I also generally don't think omitting In general, the code I want to get ported to WASI would look something like
With full pthreads stubs in wasi-libc, this code can either be ported by doing nothing ("just" don't pass the Can you perhaps elaborate further as to why you oppose having |
(Thanks for doing this work, @ArcaneNibble) |
how about having libwasi-emulated-pthread.a to follow the precedents?
i don't want to break pthread-detection mechanisms around there, including cmake FindThreads. |
Oh! I quite like this idea! I would still want libcxx to be built with threading support enabled for both
The deliberate intention was to cause detection mechanisms to now detect "this platform has pthreads." This is why I implemented all of the stub functions s.t. they comply with the POSIX specifications (rather than just returning 0 like some other examples that have been posted in #501 ). Can we get community consensus on the approach of "put single-threaded pthreads stubs into libwasi-emulated-pthread.a but always build libcxx with threading enabled (thus causing a program to have undefined symbols if it uses libcxx threading but doesn't link the .a file)" before I go ahead and implement it? |
do you mean to make wasi-sdk ship libcxx built with LIBCXX_ENABLE_THREADS=ON? why? |
Yes, because I specifically want to easily port software which uses C++11 threading. i.e. the exact same reason why I am trying to enable pthreads stubs |
@yamt I feel like we are repeatedly talking past each other. Is there any way we can jump into a real-time discussion (e.g. IRC)? |
ping, is there some way through which we can arrive at community consensus about how this pthreads stub functionality should be named and exposed? |
{PTHREAD_THREADS_MAX} would be exceeded." | ||
*/ | ||
return EAGAIN; | ||
} |
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 don't have write permission for this repo)
It feels to me like pthread_create, pthread_detach and pthread_join should be kept in a separate archive just like mmap (libwasi-emulated-mman.a) and the signal handler stubs (libwasi-emulated-signal.a) pthread_once and pthread_exit are fine to keep in the main libc.a given that (unlike pthread_create and co) they are actual implementations behaving as they should according to the POSIX specification, albeit only working correctly on single threaded targets, but they are only used on single threaded targets anyway.
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.
80e0c80
to
87d8ee2
Compare
As there have been multiple people in support of something like this, I have:
|
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.
This approach seems reasonable to me.
bf6015b
to
260e8ce
Compare
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 like this PR and took a look today. I think my main hang-up is the question about when these stubs should (a) fail with a trap, (b) silently do nothing, and (c) work as intended. I thought we'd have a lot of (b) seeing as they're stubs but understand why we occasionally need (a) I guess. I suppose you chose (c) in some cases, like simple constructors, because it was easy enough to add? Or were there some real needs for this in your Clang use case?
@@ -1,8 +1,7 @@ | |||
#include "pthread_impl.h" | |||
#include <threads.h> | |||
|
|||
#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__) && \ | |||
defined(_REENTRANT) | |||
#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__) |
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.
If I'm reading things right, you're keeping the convention of conditionally compiling the threads stuff (stub or otherwise) behind _REENTRANT
. Why remove it here? Or, conversely, why add it in the previous file (pthread_impl.h
)? One of these seems inconsistent unless I'm missing something.
{ | ||
if (!b->_b_limit) return PTHREAD_BARRIER_SERIAL_THREAD; | ||
__builtin_trap(); | ||
} |
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'm confused here:
pthread_barrier_init
is a copy of the original function, i.e., it workspthread_barrier_destroy
does nothing, but silentlypthread_barrier_wait
traps
Shouldn't we more coherent and, e.g., "just trap" for all three or "just work"?
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.
Let's look at it again:
pthread_barrier_init
creates a barrier in a way appropriate for a single-threaded environment.pthread_barrier_destroy
destroys a barrier in a way appropriate for a single-threaded environment, which in this case happens to do nothing.
-pthread_barrier_wait
waits on a barrier in a way appropriate for a single-threaded environment, i.e. traps since this would result in a deadlock, except for the 1-thread case where it returnsPTHREAD_BARRIER_SERIAL_THREAD
.
If I didn't miss anything, this is a conformant single-threaded implementation, so there is nothing that "doesn't work" there.
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 see what you mean; let me look at this again.
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.
Ok, I was misreading when this stub code was being used: I see now that it is only in single-threaded "stub" scenarios, i.e., #ifndef _REENTRANT
.
Clang does actually need the initialization and destruction of primitives to succeed, this is not just an arbitrary choice. Overall, I like @ArcaneNibble's chosen strategy, which is to produce a conformant single-threaded implementation that traps on deadlock instead of halting. |
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 get it, looks good to me. It will be interesting to see how this holds up when we add proper pthread tests. From the poking I did, the implementations included here do make sense under the assumption that this is only run in single-threaded WebAssembly.
{ | ||
if (!b->_b_limit) return PTHREAD_BARRIER_SERIAL_THREAD; | ||
__builtin_trap(); | ||
} |
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.
Ok, I was misreading when this stub code was being used: I see now that it is only in single-threaded "stub" scenarios, i.e., #ifndef _REENTRANT
.
@@ -0,0 +1,43 @@ | |||
// This file is linked into libc |
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.
One thing that led to my earlier confusion is the naming of these files, coupled with the lack of documentation. I think the organization of all of this boils down to:
- for non
*-threads
targets, we compile in the single-threaded stub implementations plus this file,stub-pthreads-good.c
- for the
libwasi-emulated-pthread.{so|a}
libraries, we compile thestub-pthreads-emulated.c
file; we expect users to link that library solely with a non*-threads
libc - for
*-threads
targets, none of this code is compiled
I can see now what the intent is here, though. I might be tempted to refactor this a bit in the future but, if this is immediately useful to some you, let's go with it as-is for now. Some of the confusion is also caused by how the Makefile is structured, etc.
Oh, one last comment: @ArcaneNibble, as I reviewed this, I noticed that the recent #522 modifies the expected files so we can't merge this as-is. Can you rebase or merge that in? |
This is needed in order to *correctly* implement things such as cancellation handlers being invoked on pthread_exit
This fixes compatibility with old versions of clang
This is "good" as it actually does perform all the cancellation actions and then calls exit()
These functions always fail
260e8ce
to
9b80a8e
Compare
Rebased |
#define pthread_create(...) ({ _Static_assert(0, "This mode of WASI does not have threads enabled; \ | ||
to enable stub functions which always fail, \ | ||
compile with -D_WASI_EMULATED_PTHREAD and link with -lwasi-emulated-pthread"); 0;}) |
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.
Apologies if this is already being discussed elsewhere (wasn't able to find it), but:
We're hitting this assertion in Homebrew even when targeting wasm32-wasip1-threads
.12 This is just from building a simple hello world
program in C++.
Is that intentional? If yes, then perhaps the message should omit the part about -lwasi-emulated-pthread
, since that should not be necessary when targeting wasm32-wasip1-threads
?
Footnotes
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.
Filed #558 for this.
This patch series first starts with a number of commits stubbing out functions in the existingEDIT: These have been split off into separate PRs and merged.THREAD_model=posix
code. According to "The Open Group Base Specifications Issue 7, 2018 edition", there are a number of mandatory functions which have not been provided. There are also some optional functions that have been partially provided in a not-useful way (e.g. get but no set function). For these, I have chosen to clean them up and remove the get functions for consistency.The remainder of the patches then build up a stub implementation of pthreads for
THREAD_MODEL=single
. I have done my best to try to make sure that all functions are as conforming as possible (under the assumption that another thread cannot ever be launched). This means that objects such as mutexes and rwlocks actually do update their state and will correctly fail when locks cannot be acquired.When an inevitable deadlock occurs, I have chosen to return EDEADLK when it has been explicitly listed as a permissible return value, and to invoke
__builtin_trap
otherwise.I have tested this by rebuilding libc++ with threads enabled and then smoke-testing Clang/LLVM-on-WASI to make sure that it can compile a simple program. I have not run any more-extensive conformance testing.
Fixes #501