Skip to content

Trim STAThreadAttribute in single-threaded (browser/wasi) Mono apps#130603

Open
pavelsavara with Copilot wants to merge 6 commits into
mainfrom
copilot/remove-stathreadattribute-blazor-wasm
Open

Trim STAThreadAttribute in single-threaded (browser/wasi) Mono apps#130603
pavelsavara with Copilot wants to merge 6 commits into
mainfrom
copilot/remove-stathreadattribute-blazor-wasm

Conversation

Copilot AI commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

main PR

Description

System.STAThreadAttribute was unnecessarily retained in trimmed single-threaded Mono apps (browser/WASI).

This PR now addresses both sides of the behavior:

  • src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml keeps System.STAThreadAttribute rooted only when System.Runtime.CompilerServices.RuntimeFeature.IsMultithreadingSupported is true.
  • src/mono/mono/metadata/object.c was updated to tolerate the attribute type being absent by using a try-load path when checking for [STAThread] on entrypoint methods.

This preserves existing behavior on multithreaded Mono platforms while allowing single-threaded browser/WASI apps to trim the attribute safely.

Customer Impact

Without this fix, trimmed single-threaded Mono apps can retain unnecessary metadata, and WASI startup could fail when the attribute is trimmed.

Regression

No known product regression is introduced. The native startup path now handles missing System.STAThreadAttribute gracefully instead of assuming it is always present.

Testing

  • Baseline build: ./build.sh mono+libs
  • Targeted runtime build: ./build.sh mono.runtime -c Release
  • Targeted WASI/Mono build: ./build.sh -os wasi -subset mono+libs -c Debug
  • Parallel validation (code review + security scan) completed.

Risk

Low to medium. The change is narrowly scoped to STA attribute lookup in Mono startup and linker rooting configuration. Multithreaded platforms continue to root the type; single-threaded platforms can trim it, and runtime lookup now safely handles absence.

Package authoring no longer needed in .NET 9

IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.

Copilot AI self-assigned this Jul 13, 2026
Copilot AI review requested due to automatic review settings July 13, 2026 08:10
Copilot AI removed the request for review from Copilot July 13, 2026 08:10
Co-authored-by: pavelsavara <271576+pavelsavara@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 13, 2026 08:14
@dotnet-policy-service dotnet-policy-service Bot added the linkable-framework Issues associated with delivering a linker friendly framework label Jul 13, 2026
Copilot AI changed the title [WIP] Remove STAThreadAttribute from Blazor WASM app Trim STAThread/MTAThread attributes when multithreading is unsupported Jul 13, 2026
Copilot AI requested a review from pavelsavara July 13, 2026 08:16
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture size-reduction Issues impacting final app size primary for size sensitive workloads labels Jul 13, 2026
@pavelsavara pavelsavara temporarily deployed to copilot-pat-pool July 13, 2026 08:41 — with GitHub Actions Inactive
@pavelsavara pavelsavara temporarily deployed to copilot-pat-pool July 13, 2026 08:42 — with GitHub Actions Inactive
@MichalStrehovsky

Copy link
Copy Markdown
Member

How are we ending up needing these attribute in the first place? When I make a new console app (on Windows even!), and PublishTrimmed the app, this attribute trims just fine. Does Blazor place the attribute on Main for some reason? Or roots some other code that needs it? I think this is the core part that should be addressed.

@pavelsavara

Copy link
Copy Markdown
Member

How are we ending up needing these attribute in the first place?

Great question, thanks

<type fullname="System.STAThreadAttribute" /> in

https://github.com/dotnet/runtime/blame/0ded4d28b4990cc4bdb6f1e44bafca3f22aa6eda/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml#L338

@copilot removing root cause is better alternative. Investigate if this type rooting is still necessary on other Mono/mobile platforms. If not, remove it from ILLink.Descriptors.xml

If it's still need, make it only explicitly used for those (MT) Mono platforms.

…tributes change

Co-authored-by: pavelsavara <271576+pavelsavara@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 13, 2026 09:34

Copilot AI commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

How are we ending up needing these attribute in the first place? When I make a new console app (on Windows even!), and PublishTrimmed the app, this attribute trims just fine. Does Blazor place the att...

Good catch — the attribute trims fine on CoreCLR because nothing roots it. On Mono it's the descriptor that keeps it alive: src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml unconditionally roots System.STAThreadAttribute because the native runtime (object.c: prepare_thread_to_exec_main) looks it up via mono_class_load_from_name (which aborts if the type is missing) to set apartment state when launching an entry point through mono_runtime_run_main. Blazor WASM doesn't reach that path (the entry point is invoked directly), so the root was pure dead weight there. I've switched to fixing the root cause — see the updated commit.

Note

This comment was generated by GitHub Copilot.

How are we ending up needing these attribute in the first place?

Great question, thanks
...

Done in the latest commit. I confirmed the root is still needed on multithreaded Mono platforms (desktop/iOS/Android launch the entry point via mono_runtime_run_mainprepare_thread_to_exec_main, which calls mono_class_load_from_name for System.STAThreadAttribute and aborts if it's missing). Browser/wasi invoke the entry point directly and never hit that path, and apartment state is meaningless there.

So rather than removing the root outright, I gated it in ILLink.Descriptors.xml on System.Runtime.CompilerServices.RuntimeFeature.IsMultithreadingSupported (featurevalue="true" featuredefault="true") — kept for MT platforms, trimmed on single-threaded browser/wasi where that switch is false. I also reverted the earlier ILLink.LinkAttributes.Shared.xml change in favor of this root-cause fix.

Note

This comment was generated by GitHub Copilot.

Copilot AI changed the title Trim STAThread/MTAThread attributes when multithreading is unsupported Trim STAThreadAttribute in single-threaded (browser/wasi) Mono apps Jul 13, 2026
@pavelsavara pavelsavara marked this pull request as ready for review July 13, 2026 13:54
@pavelsavara pavelsavara requested a review from steveisok as a code owner July 13, 2026 13:54
Copilot AI review requested due to automatic review settings July 13, 2026 13:54
@pavelsavara pavelsavara requested a review from vitek-karas as a code owner July 13, 2026 13:54
@pavelsavara

Copy link
Copy Markdown
Member

/ba-g CI failure is #130618

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR attempts to reduce trimming roots for Mono single-threaded WebAssembly targets by making System.STAThreadAttribute no longer unconditionally rooted in Mono’s ILLink descriptors, instead gating its retention on RuntimeFeature.IsMultithreadingSupported.

Changes:

  • Removes the unconditional System.STAThreadAttribute root from the main System.Private.CoreLib descriptor list.
  • Re-adds System.STAThreadAttribute as a feature-gated root conditioned on System.Runtime.CompilerServices.RuntimeFeature.IsMultithreadingSupported == true.

Comment thread src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml Outdated
Co-authored-by: pavelsavara <271576+pavelsavara@users.noreply.github.com>
Comment on lines +646 to +652
<!-- STAThreadAttribute is read by the native runtime (object.c: prepare_thread_to_exec_main)
when launching an entry point via mono_runtime_run_main to set the apartment state.
On single-threaded runtimes this attribute can be trimmed: apartment state is meaningless
there and the native lookup tolerates the type being absent. -->
<assembly fullname="System.Private.CoreLib" feature="System.Runtime.CompilerServices.RuntimeFeature.IsMultithreadingSupported" featurevalue="true" featuredefault="true">
<type fullname="System.STAThreadAttribute" />
</assembly>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If the runtime tolerates it now, why keep it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For the windows which are not single-threaded. To have the apartment state set correctly.

-> CoInitializeEx()

mono_thread_init_apartment_state (void)
{
#ifdef HOST_WIN32
MonoInternalThread* thread = mono_thread_internal_current ();
/* Positive return value indicates success, either
* S_OK if this is first CoInitialize call, or
* S_FALSE if CoInitialize already called, but with same
* threading model. A negative value indicates failure,
* probably due to trying to change the threading model.
*/
if (CoInitializeEx(NULL, (thread->apartment_state == ThreadApartmentState_STA)
? COINIT_APARTMENTTHREADED
: COINIT_MULTITHREADED) < 0) {
thread->apartment_state = ThreadApartmentState_Unknown;
}
#endif
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Or you are saying that the C# code would keep the attribute if it was needed ? And that we don't need to root is just for the VM sake ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The only test I can find is src\mono\mono\tests\bug-79215.2.il which is never executed, it's a dead weight.
Since we are not running any workload on Windows/Mono I can take a risk. I'm not willing to add tests for this.

@steveisok @lewing thoughts ?

Copilot AI review requested due to automatic review settings July 14, 2026 09:06
@pavelsavara pavelsavara temporarily deployed to copilot-pat-pool July 14, 2026 09:06 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment on lines 334 to 338
<!-- exception.c: mono_get_exception_stack_overflow / mono_exception_from_name -->
<method signature="System.Void .ctor()" />
</type>

<!-- object.c: mono_runtime_exec_main -->
<type fullname="System.STAThreadAttribute" />

<!-- domain.c: mono_defaults.string_class -->
Comment on lines 4665 to 4668
MonoInternalThread* thread = mono_thread_internal_current ();
MonoCustomAttrInfo* cinfo;
MonoClass *sta_thread_attribute_class = mono_class_try_get_sta_thread_attribute_class ();
gboolean has_stathread_attribute;
Copilot AI requested review from Copilot and removed request for Copilot July 14, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-System.Threading linkable-framework Issues associated with delivering a linker friendly framework size-reduction Issues impacting final app size primary for size sensitive workloads

Projects

None yet

Development

Successfully merging this pull request may close these issues.

STAThreadAttribute is unnecessarily being left in a Blazor WASM default app

4 participants