Skip to content
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

Is wasi_thread_exit needed? #7

Open
abrown opened this issue Oct 18, 2022 · 21 comments
Open

Is wasi_thread_exit needed? #7

abrown opened this issue Oct 18, 2022 · 21 comments

Comments

@abrown
Copy link
Collaborator

abrown commented Oct 18, 2022

In a discussion yesterday with @sbc100 and @sunfishcode, we considered whether it might be necessary to add a second API call to the wasi-threads proposal to support early exit from threads. Since exception support is neither standardized nor widespread, it is impossible for a child thread to "early exit" and continue execution in the exported thread entry point, wasi_thread_start. This entry point can contain code responsible for important tasks, such as notifying joiners that the thread is complete (e.g., in wasi-libc), and possibly others such as cleaning up any TLS allocations.

One suggestion was to implement pthread_exit in wasi-libc using a new API call, wasi_thread_exit. But consider what happens when wasi_thread_exit is called: it could presumably dispose of the child thread from the host side, but it would not really have access to execute the cleanup code at the end of was_thread_start (e.g., the join notify instructions). To properly support pthread_exit it may be necessary to do more, e.g., exporting a second function from threaded modules, wasi_thread_stop.

Any additional thoughts on this?


One side issue we discussed is that traps and calls to exit in child threads take down the entire program. This eliminates a whole other set of problems and we should probably document this in the README.

@sbc100
Copy link
Member

sbc100 commented Oct 18, 2022

Interesting point regarding cleanup. Presumably pthread_exit could do any needed cleanup prior to calling wasi_thread_exit. Anyone who calls wasi_thread_exit is going underneath the pthread abstraction and therefore perhaps its reasonable that the pthread would not get cleaned up crorrect? Just like anyone calling wasi_thread_create (or whatever its called) directly is not going get a pthread-compliant thread.

@sbc100
Copy link
Member

sbc100 commented Oct 18, 2022

Note that we could consider not supporting pthread_exit at all. IIUC C++ thread API has not way to exit a thread early, unlike pthreads (pthread_exit) and C11 threads (https://en.cppreference.com/w/c/thread/thrd_exit).

I'm including the say its worth supported since thread is a lot of pthread code in the wild and pthread_exit is not a rare as things like thread cancellation (which I think we may be able to avoid supported).

@abrown
Copy link
Collaborator Author

abrown commented Oct 19, 2022

Interesting point regarding cleanup. Presumably pthread_exit could do any needed cleanup prior to calling wasi_thread_exit.

Oh, great point! They're both on the libc side so both places can have the cleanup code.

@yamt
Copy link
Contributor

yamt commented Dec 13, 2022

the current wasi-libc implementation seems to use wasi_proc_exit for both of thread exit and process exit.
i guess it's simpler to distinguish them.

yamt added a commit to yamt/wasi-libc that referenced this issue Dec 13, 2022
Unlike many of platforms with threads/lwps, wasi doesn't
have separate api for thread exit and process exit as of writing
this. It only has a single `wasi_proc_exit`, which is currently
used for both of process exit and thread exit by wasi-libc.
Becasue of that, on wasi, exit(3) only terminates the calling thread.

On the other hand, the stdio exit logic uses FFINALLOCK macro,
which leaves the file locked. While it's fine on platforms where
exit somehow forcibly terminates other threads soon, it can make
other threads block on these locks forever on wasi.

Until the situation is cleaned up, this commit makes stdio exit use
normal file locking instead of the "final" variant. This change
allows other threads to continue running. Hopefully those threads
will exit by themselves soon.

cf. WebAssembly/wasi-threads#7
@sbc100
Copy link
Member

sbc100 commented Dec 13, 2022

I don't think wasi_proc_exit should be used to implement pthread_exit.

@loganek
Copy link
Collaborator

loganek commented Dec 13, 2022

Having another syscall for exiting the thread sounds reasonable. Also, I agree with @sbc100 about keeping the API simple and move as much of the implementation as possible to WASM code - that's what we do in thread_spawn() (pthread_create() does a lot of stuff on top of that), so I think following the same pattern for wasi_thread_exit/pthread_exit doesn't seem to be wrong.

@sbc100
Copy link
Member

sbc100 commented Dec 13, 2022

@loganek do you think being able to exit a thread without unwinding the stack is critical? What do you think about simply not implementing pthread_exit at all?

@yamt
Copy link
Contributor

yamt commented Dec 14, 2022

i guess choices are:

a-1. use wasi_proc_exit for process exit. introduce wasi_thread_exit for thread exit.

a-2. use wasi_proc_exit for process exit. introduce no extra api for thread exit. just make wasi_thread_start return. do not provide pthread_exit to user apps.

b-1. use wasi_proc_exit for thread exit. introduce wasi_proc_exit_group for process exit. (linux-like solution)

b-2. use wasi_proc_exit for thread exit. introduce no extra wasi api for process exit. to implement terminate-other-threads behavior in libc, i guess we need to introduce some other wasi api like "kill-thread".

i feel a little anxios about a-1 and a-2 because they can affect non wasi-threads threading. (web worker and other embedder-managed threading)

@sbc100
Copy link
Member

sbc100 commented Dec 14, 2022

Can you explain a little more why you have concerns about a-1? It seems like the most obvious solution to me. I'm not sure what you mean by "non wasi-threads threading". If I implement wasi threads on top of web workers that is still "wasi-threads threading" isn't it?

@yamt
Copy link
Contributor

yamt commented Dec 14, 2022

Can you explain a little more why you have concerns about a-1? It seems like the most obvious solution to me. I'm not sure what you mean by "non wasi-threads threading". If I implement wasi threads on top of web workers that is still "wasi-threads threading" isn't it?

it's just a vague concern about adding extra responsibility to existing api.

there seems to be wasi users using threads without wasi-threads.
eg. see "Build with libc-WASI" section in https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/pthread_library.md

@loganek
Copy link
Collaborator

loganek commented Dec 14, 2022

@loganek do you think being able to exit a thread without unwinding the stack is critical? What do you think about simply not implementing pthread_exit at all?

@sbc100 I'm mainly worried about porting existing code. Whereas this won't be a problem for a new code that's written with WASM in mind and without that function, porting some of the projects might require major refactoring (which maintainers might not be happy about, so we'll end up with lots of <projectname>-wasm forks).

Also, you mentioned c++'s threads - the common pattern for exiting the thread is to put everything in a try...catch block and throw an exception of a dedicated type to exit the thread (stack unwinding wasn't the main reason for that in many cases, it was just to exit the thread quickly).

Having said that, not having wasi_thread_exit() feels like two-way-door decision to me (or at least more two-way-door than having it in the first place, and then try to remove it from the spec). I think we speculate a bit here although I didn't see (and don't have) any concrete data showing that the function must exist along with wasi_thread_spawn() from day 1. If there are concerns around having the function, I'd rather not have it and wait for feedback from community, see their usecases, and make the decision then.

Just a personal note - I think both pthread_exit and exit are misused way too often. I've seen them being used very incorrectly and when I saw the code I wished those functions didn't exist at all.

@yamt
Copy link
Contributor

yamt commented Dec 15, 2022

@loganek
Copy link
Collaborator

loganek commented Dec 15, 2022

Thanks @yamt , I was more interested in real usecases than just a number of occurrences of a specific function on github - i.e. do we know for sure that not having a pthread_exit will be problematic so we have to include it in the first release of the WASI threads?

@yamt
Copy link
Contributor

yamt commented Dec 15, 2022

Thanks @yamt , I was more interested in real usecases than just a number of occurrences of a specific function on github - i.e. do we know for sure that not having a pthread_exit will be problematic so we have to include it in the first release of the WASI threads?

it isn't just a number.
you can follow the link to see how/where it's used.
other tabs (issues/commits/discussions) might be interesting too.

@sbc100
Copy link
Member

sbc100 commented Dec 15, 2022

Based on how many programs seems to depend on pthread_exit I'm leaning towards adding wasi_thread_exit. Is there anyone who feels strongly that we should not add it?

@abrown
Copy link
Collaborator Author

abrown commented Dec 16, 2022

I am leaning in favor of wasi_thread_exit at this point. I share @loganek's concerns about adding to the API unnecessarily and too early but I think it is rather fundamental to making a lot of C code compile (i.e., code you have pointed out that uses pthread_exit). It also frees up wasi_proc_exit to be used correctly.

I propose we test the viability of implementing this in WAMR and Wasmtime in the first week or two of the new year and then merge it in here (and the other related PRs) once we are sure everything works. Thoughts on this?

@loganek
Copy link
Collaborator

loganek commented Dec 16, 2022

I'm leaning towards not implementing this function for now, and only add it when somebody comes with a usecase where the existence of it either is absolutely necessary or greatly simplifies implementations at scale. I was at the beginning in favour of adding the function because of number of projects that use this function. After giving more thoughts on that, I wasn't actually sure what's the actual impact of not adding the function right now. As I said earlier, it's so much easier to add a new function when we know it's really needed than removing/maintaining something we're not sure about.

Having said that, I won't block adding this function now, if people here believe it's absolutely necessary to have it in the first version of wasi threads, then let's add it. I just think there's a bit of simplifications we've taken to keep the API simple for now (egnot having priorities) that we probably could postpone the other features (like thread exit) until we really need it.

@abrown
Copy link
Collaborator Author

abrown commented Dec 16, 2022

Ok, I'm fine with that as well; we can leave this issue open and when someone really needs it then we can just add it.

@yamt
Copy link
Contributor

yamt commented Dec 17, 2022

IMO, it's simpler to just implement wasi_thread_exit than making possibly temporary wasi-libc changes to avoid it.

I propose we test the viability of implementing this in WAMR and Wasmtime in the first week or two of the new year and then merge it in here (and the other related PRs) once we are sure everything works. Thoughts on this?

the implementation of wasi_thread_exit should be trivial because what it does is exactly a subset of wasi_proc_exit, which needs to be implemented anyway.
i'm sure it's the case for WAMR at least.

yamt added a commit to yamt/wasi-libc that referenced this issue Dec 19, 2022
The current wasi-threads has no thread-exit functionality.
Thus it isn't straightforward to implement pthread_exit
without leaking thread context. This commit simply disables
pthread_exit for now.

Also, instead of abusing `wasi_proc_exit` for thread exit,
make let `wasi_thread_start` return.

Note: `wasi_proc_exit` is supposed to terminate all threads
in the "process", not only the calling thread.

Note: Depending on the conclusion of the discussion about
`wasi_thread_exit`, we might revisit this change later.

References:
WebAssembly/wasi-threads#7
WebAssembly/wasi-threads#17
yamt added a commit to yamt/wasi-libc that referenced this issue Dec 19, 2022
The current wasi-threads has no thread-exit functionality.
Thus it isn't straightforward to implement pthread_exit
without leaking thread context. This commit simply disables
pthread_exit for now.

Also, instead of abusing `wasi_proc_exit` for thread exit,
make `wasi_thread_start` return.

Note: `wasi_proc_exit` is supposed to terminate all threads
in the "process", not only the calling thread.

Note: Depending on the conclusion of the discussion about
`wasi_thread_exit`, we might revisit this change later.

References:
WebAssembly/wasi-threads#7
WebAssembly/wasi-threads#17
abrown pushed a commit to WebAssembly/wasi-libc that referenced this issue Dec 21, 2022
The current wasi-threads has no thread-exit functionality.
Thus it isn't straightforward to implement pthread_exit
without leaking thread context. This commit simply disables
pthread_exit for now.

Also, instead of abusing `wasi_proc_exit` for thread exit,
make `wasi_thread_start` return.

Note: `wasi_proc_exit` is supposed to terminate all threads
in the "process", not only the calling thread.

Note: Depending on the conclusion of the discussion about
`wasi_thread_exit`, we might revisit this change later.

References:
WebAssembly/wasi-threads#7
WebAssembly/wasi-threads#17
@yamt
Copy link
Contributor

yamt commented Feb 3, 2023

a datapoint:

cpython has PyThread_exit_thread, which needs pthread_exit to implement properly.
cf. python/cpython#101537
PyThread_exit_thread seems like a part of its stable abi.

@whitequark
Copy link

@abrown @loganek I have a use case that I would like to be covered by wasi-threads that is impossible to do without wasi_thread_exit.

I would like to implement an adapter, purely using the Component Model, which allows one WASI CLI command to invoke others and interact with them via the filesystem and standard I/O streams.

This is extremely useful for porting toolchains to run in the WASI world. I am working with FPGA toolchains, which are obscure, but a more familiar example--which is also relevant here!--is gcc: the gcc compiler driver spawns a cc1 process and then an as process to compile a file. It would be very useful to be able to port the GCC toolchain to pure WASI, too.

For the most part, this use case can be handled by careful component composition. The environment and arguments can be copied around easily; the clocks, filesystem, socket, and random implementations can be provided by the host and/or virtualized using WASI-Virt; and the terminal or standard streams can be virtualized by an adapter. Concurrency can be provided by making the adapter spawn a thread per launched sub-command. However, there is one API that throws a spanner in the works: proc_exit.

At the moment, without assuming that exception handling is available (it isn't on Wasmtime and probably won't be for quite a while still), there is no way to provide proc_exit to a sub-command without letting that sub-command terminate the entire process. This is a problem because proc_exit is often used for early exit in case of an error, and if I am to port practical C code, I would not be able to avoid providing it to sub-commands.

If I were able to map proc_exit to wasi_thread_exit, I would be able to ship not just individual tools as I currently do in the YoWASP project, but complete FPGA toolchains, with expansive worlds that include e.g. a Python-based frontend for parsing and semantic analysis, Yosys for synthesis, nextpnr for placement and route, SMTBMC for formal verification, and Boolector or Yices as the engine for SMTBMC. This isn't a theoretical use case: I've been shipping most of it already, and there is also user demand to expand the support for this toolchain, since many of these tools can be difficult to install, especially for newcomers.

Without wasi_thread_exit, I could only implement this by using host functions in each of the language embeddings (Python and JavaScript) to spawn new interpreters (where proc_exit can be safely executed), but there are two problems with this:

  1. Instead of shipping each of the built commands as a single WASI component file, I now have to ship a WASI component file, a JS embedding, and a Python embedding.
  2. I am no longer able to combine individual tools into one big composed WASI toolchain component file, which would be a nice delivery optimization.
  3. Because of how much additional effort goes into implementing the host functions and per-language embeddings, I currently do not plan to ship this as the support burden will be too high.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants