Skip to content

[Web] Use actual PThread pool size for get_default_thread_pool_size() #104458

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

marcosc90
Copy link
Contributor

@marcosc90 marcosc90 commented Mar 21, 2025

fixes #104428

This resolves a freeze in WebAssembly builds when physics/3d/run_on_separate_thread=true and threading is enabled.

Previously, get_default_thread_pool_size() in OS_Web was hardcoded to return 1, which caused physics step group tasks to stall. The only available worker was occupied by this long-lived task:

void PhysicsServer3DWrapMT::_thread_loop() {
while (!exit) {
WorkerThreadPool::get_singleton()->yield();
command_queue.flush_all();
}
}

As a result, the following task

WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep3D::_setup_constraint, nullptr, total_constraint_count, -1, true, SNAME("Physics3DConstraintSetup"));

would never execute, leading to a deadlock. This patch replaces the hardcoded value with a dynamic call to a JS hook that reflects the available pthread pool size at runtime.

bugsquad edit: fixes #104004

@marcosc90 marcosc90 requested a review from a team as a code owner March 21, 2025 19:30
@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from bfe4570 to 7a83399 Compare March 21, 2025 19:32
@adamscott
Copy link
Member

adamscott commented Apr 14, 2025

For reference, here's the PR #54499 that introduced the static 1 value.

Copy link
Member

@adamscott adamscott left a 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 an answer directly for https://github.com/godotengine/godot/pull/104458/files#r2008222268 though.

@adamscott adamscott changed the title Web: Use actual pthread pool size for get_default_thread_pool_size() [Web] Use actual PThread pool size for get_default_thread_pool_size() Apr 14, 2025
@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from 486fe27 to 7ead9c3 Compare April 14, 2025 20:41
@marcosc90
Copy link
Contributor Author

Pushed again to fix lint issues from suggested changes in the review.

@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from 7ead9c3 to 91db595 Compare April 29, 2025 08:32
@marcosc90 marcosc90 requested a review from a team as a code owner April 29, 2025 08:32
@Faless
Copy link
Collaborator

Faless commented May 2, 2025

Hi @marcosc90 , thank you for looking into this issue.

I will start with some context, but first, I didn't realize you could pass an expression as PTHREAD_POOL_SIZE, that is indeed much better than an hardcoded value. This also means we could expose it to the JS support code and make it configurable on export which is great 🎆 .

Now for some context...


Most browsers have a hard cap on threads, historically, that cap has been quite low.
When hardware concurrency is capped the value of navigator.hardwareConcurrency may be the cap (may, i.e. hardwareConcurrency may in fact be more than the cap).
This means we can't trust too much the value of navigator.hardwareConcurrency.

On the web, threads are spawned asynchronously, so code like:

thread.start(something)
// STUFF appends without returning the main loop
thread.join()

Will produce a deadlock.

This is why emscripten introduced the PTHREAD_POOL_SIZE option.

But this means that any thread spawned in addition to the PTHREAD_POOL_SIZE, will still cause deadlocks when occurring in the above scenario.

The issue we historically had with the godot thread pool was it filling the emscripten thread pool size, causing deadlocks in other parts of the engine (where the aformentioned start/join was used, sometimes by third party libraries).
This was made worse by the fact that we were using multiple WorkerThreadPools in the past (I don't know if that's the case anymore).

So, in general, we need to find a good trade-off, and that's not easy to find.


With this in mind, I suggest we may just want to make both values customizable via JS via platform/web/js/engine/config.js.

We can set those value to a reasonable defeault (8 for the emscripten pool, 4 for the godot pool).

We can make the value customizable when exporting via the editor by adding the values to the config in EditorExportPlatformWeb

We can also optionally add more logic (in a follow up PR) to EditorExportPlatformWeb to throw an error/warning if values are set too small for the current project settings.

We could also explore building with PTHREAD_POOL_SIZE_STRICT but that probably deserves its own discussion.

Let me know what you think.

@marcosc90
Copy link
Contributor Author

Hi @Faless, that sounds good to me since currently on my custom build I'm using some custom values, so making the customizable would be great.

I'm not yet familiar with how all the settings work in godot, to confirm, do you mean adding those configs to the export template settings?

image

Just below Thread Support, adding something like:

  • Emscripten Pool Size: 8
  • Godot Pool Size: 4

@Faless
Copy link
Collaborator

Faless commented May 4, 2025

I'm not yet familiar with how all the settings work in godot, to confirm, do you mean adding those configs to the export template settings?

Just below Thread Support, adding something like:

* Emscripten Pool Size: 8

* Godot Pool Size: 4

Yes, that's indeed what I've been thinking

@marcosc90
Copy link
Contributor Author

I've been quite busy, I'll try to get this done during the weekend.

@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from 91db595 to b182993 Compare May 13, 2025 21:44
@marcosc90 marcosc90 requested a review from a team as a code owner May 13, 2025 21:44
@marcosc90
Copy link
Contributor Author

marcosc90 commented May 13, 2025

Just pushed some changes. I'm not really sure if this is exactly how configs should be passed, it is working correctly though.

image

I'm not good with public docs, so please feel free to add/suggest the description for both settings, I enabled "Allow edits"

@Faless
Copy link
Collaborator

Faless commented May 14, 2025

Please apply the following patch so we don't pollute window.

patch.diff.txt

@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from b182993 to 5d950cb Compare May 14, 2025 10:59
@marcosc90
Copy link
Contributor Author

Much better, thanks @Faless . I was looking for a better way, but was still figuring it out how all the code glues together.

Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the docs, you will need to run Godot from the engine root with the --doctool argument. It will automatically generate the correct docs bindings in the correct place so you don't trigger the CI error. See: https://docs.godotengine.org/en/latest/contributing/documentation/updating_the_class_reference.html#updating-class-reference-when-working-on-the-engine

I have made some rough suggestions, but this is outside my area of expertise, so the content may not be exactly accurate

@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from 5d950cb to dec04c6 Compare May 15, 2025 16:51
@marcosc90
Copy link
Contributor Author

Thanks @clayjohn

image

image

@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from dec04c6 to 1692e6d Compare May 15, 2025 17:13
@marcosc90 marcosc90 force-pushed the fix-thread-collision-shape-3d branch from 1692e6d to ae0faab Compare May 15, 2025 17:14
Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me now. CC @Faless For final confirmation

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