From b44c00892a9871a9f124c9173102a9a177a728d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Tue, 26 Apr 2022 08:44:27 -0400 Subject: [PATCH] [mono] merge wasm-threading-eventpipe into main (#68232) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge initial work on multi-threaded WebAssembly. The normal wasm build is single-threaded. There should be no functional changes to its behavior. To enable a multi-threaded build pass `/p:WasmEnableThreads=true`. See `src/mono/wasm/threads.md` for details. The big changes are: 1. The normal ref assemblies related to threading retain `[UnsupportedOSPlatform("browser")]` attributes for various threading-related functions 2. In System.Private.CoreLib, the `[UnsupportedOSPlatform]` attributes are removed, and functions that used to always throw PNSE nwo do a runtime check using `System.Threading.Thread.IsThreadStartSupported` to check if threading is enabled in the current build. 3. A new nuget `Microsoft.NET.WebAssembly.Threading` is created. It contains experimental ref assemblies without the `[UnsupportedOSPlatform]` attributes. The intention is that code opting into experimenting with multithreading will include this nuget by setting some property that will be used by the wasm MSBuild SDK to pick a multi-threaded runtime build and configure things appropriately. (The SDK updates don't exist yet). 4. In the multi-threaded runtime we don't use Emscripten's "main thread" option (ie: the browser thread is the main one); we also continue to run certain runtime-internal jobs (finalizers, GC pump) on the main thread even in the multi-threaded runtime. Remaining work is tracked in the related issue https://github.com/dotnet/runtime/issues/68162 --- * Initial changes for emscripten 2.0.34 * Use emcc-link.rsp in build targets * Use updated docker images * Fix compiler warnings * Put `--profiling-funcs` to `_EmccLinkFlags` * Fix build src/mono/mono/mini/mini-runtime.c:3407:25: error: ‘invoke’ undeclared (first use in this function); did you mean ‘revoke’? 3407 | invoke = mono_marshal_get_runtime_invoke_dynamic (); * Add shell to the environment Environment setting https://github.com/emscripten-core/emscripten/blob/2.0.34/src/settings.js#L616-L641 From emscripten 2.0.25 release notes - Support for the 'shell' environment is now disabled by default. Running under `d8`, `js`, or `jsc` is not something that most emscripten users ever want to do, so including the support code is, more often than not, unnecessary. Users who want shell support can enable it by including 'shell' in `-s ENVIRONMENT` (#14535). Example of the the size increase for bench sample: -a--- 12/10/2021 3:35 PM 382113 dotnet.js -a--- 12/13/2021 10:37 AM 383589 dotnet.js * Add emcc-link.rsp to PlatformManifestFileEntry * Feedback https://github.com/emscripten-core/emscripten/blob/2fda25eea756c78c8cb024aa5b6c2b188bf7990f/src/settings.js#L1173-L1176 -s EXPORT_ES6 is link option * Bump emscripten version * Bump llvm package version and use its libclang * Use newer docker images with emscripten 3.1.1 * Remove unused variable * Add runtime support for threads in the wasm build To enable, pass `/p:WasmEnableThreads` when building the runtime ./build.sh -Subset mono+libs -os Browser -arch wasm /p:WasmEnableThreads=true * Prevent runtime from starting twice when loaded in a web worker * Automatically populate the emscripten mainScriptUrlOrBlob property so that worker initialization can find dotnet.js * Add compatibility shim so that emscripten's generated worker.js can properly get a Module instance, since we broke the API * Checkpoint * Bring back threadpool and add some tracing in diagnostics * Add comments and fix a typo * Introduce 'MonoObjectRef' ts type. Migrate mono_string_intern to not have a retval * Checkpoint (strings are broken for some reason) * Fix string interning * Migrate ObjectToString and GetDateValue * Checkpoint gc safe/unsafe region work * More ref conversion * Checkpoint (broken?) * Fix missing method * Fix incorrect signatures * Fix lint * Add new 'R' signature char for 'ref object' * Remove AddEventListener and RemoveEventListener Checkpoint * eslint fixes * Update call_method signature to avoid passing raw object pointers * Ref-ify one websocket API and fix types and incorrect rooting of two others * Deprecation metadata * More ref * Remove outdated comments * Convert typed_array_new to ref * Add volatile modifiers, satisfy eslint * Update src/mono/wasm/runtime/corebindings.c * Missing conflict * Fix build, set coop gc, and always copy dotnet.worker.js when it's around for apps * Disable sample profiler, add some functions that were missing from katelyn's PR. * Add safepoint around ep_rt_wait_event_set. Tweak sample to explicitly exit wasm in order to properly flush event to the nettrace file (w/ rundown events). * [gc] Start the GC Finalizer thread on threaded WASM * [mono] add GC Unsafe in mono_assembly_load; remove in EventPipe Remove GC Unsafe hack in ep_rt_wait_event_set * post-merge cleanup: delete duplicated definitions * [sample] Env vars should be stringy * updated dotnet.d.ts * Add mono_threads_wasm_async_run_in_main_thread; do background work on main Don't start a finalizer thread Queue all background work to run on the main thread * [mono] Fix non-threaded wasm build * Add a System.Threading.Thread.WebAssembly.Threading ref assembly * Update the browser sample to use System.Threading.Thread.WebAssembly.Threading * Rationalize System.Threading.Thread build In CoreLib, never add the [UnsupportedOSPlatform("browser")] attribute. Rely on runtime checks (`ThrowIfNoThreadStart()`). In System.Threading.Thread ref assembly, always add the unsupported platform attribute. Add mismatches to ApiCompat baseline. In System.Threading.Thread.WebAssembly.Threading don't add the attributes, and also set DisablePackageBaselineValidation to prevent Restore from looking for a System.Threading.Thread.WebAssembly.Threading nuget (fixes in-tree ProjectReferences for testing) * only turn on analyzers for the browser sample not all wasm samples * Make a single Microsoft.NET.WebAssembly.Threading nupkg that holds all the special ref assemblies * works: sample has ProjectReference to the Microsoft.NET.WebAssembly.Threading.proj * Add System.Threading.ThreadPool.WebAssembly.Threading ref assembly * ThreadPool: throw PNSE if no thread start * Update wasm threads.md * apicompat: correct warnings for System.Threading.ThreadPool * Add dotnet.worker.js to the runtime pack; update PlatformManifestFileEntry * [wasm] startup: detect Blazor dotnet.[version].[hash].js location Blazor injects a `` element into the header when it boots; detect it and extract the URL of the script. This is needed by Emscripten's dotnet.worker.js to run WorkerGlobalScope.importScripts * one more fix to Microsoft.NET.WebAssembly.Threading Seems to be necessary in order for the resulting .nupkg not to reference non-existent nugets for the ProjectReferences * rename sample to browser-mt-eventpipe * bring back unmodified browser sample The multithreading sample is browser-mt-eventpipe * update browser-mt-eventpipe sample to use ref assembly refs Referencing the rollup Microsoft.NET.WebAssembly.Threading.proj doesn't work (it ends up bundling the ref assemblies into the publish dir and breaking the app) * Use correct ifdef in AppContext.AnyOS.cs * [build] support WasmEnableThreads and WasmEnablePerfTracing These toplevel options either turn on multi-threading in general, or turn on multithreading only for eventpipe internals. For libraries these define `FeatureWasmThreads` and `FeatureWasmPerfTracing` properties and the `FEATURE_WASM_THREADS` and `FEATURE_WASM_PERFTRACING` compiler constants. For the native code, they control `DISABLE_THREADS` and `DISABLE_WASM_USER_THREADS` cmake and preprocessor settings. * Only add the portable threadpool on wasm if threading is enabled * rename browser-mt-eventpipe csproj and main assembly Give it a unique name distinct from Wasm.Browser.CJS.Sample.csproj * fix /p:WasmEnableThreads=false build after merge * now fix the WasmEnableThreads=true build * we need two ThreadPoolBoundHandle implementation stubs one for PortableThreadPool when threads are enabled, one for single-threaded wasm * Add a System.Diagnostics.Tracing ref assembly gated by FeatureWasmPerfTracing * [eventpipe] use the correct cmake option name see src/mono/mono.proj * revert debug printf and commented out code * turn off additional logging * hack: set FeatureWasmPerfTracing in the browser-mt-eventpipe sample It would be better to drive this (the inclusion of the tracing runtime component) from a user-visible flag. not the FeatureWasmPerfTracing implementation detail * remove unused variable, remove unneeded configure checks, revert line damage; add better comment in export.ts * Exclude Microsoft.NET.WebAssembly.Threading from testPackages.proj * review feedback, Apply suggestions from code review * Use a testPackages settings file to skip package runtime item verification * remove unneeded Directory.Build.props for ref package since ti doesn't compile its own assembly, none of these properties are needed * use one ProjectReference item to share metadata for the ref assemblies * remove ProjectReference comment and NoTargetsDoNotReferenceOutputAssemblies prop * Remove unneeded target * packaging simplification - move `_ExperimentalUpdateFileVersion` target to packaging.targets, conditioned on a new `IsExperimentalRefAssembly` attribute. (The target increments the file version of the ref assembly to make it easier to distinguish from the real non-experimental ref assembly) - Remove unneeded src subdirectories in ref assembly libraries - Move properties that are only used in the ref assembly projects directory into the projects and delete Directory.Build.props in the experimental ref assembly subdirectories. * move and rename UpdateExperimentalRefAssemblyFileVersion target packages.targets is only included for IsPackable=true projects, and these ref assemblies are not packable. * Assorted code review nits * Don't build/pack the multi-threaded sample on single-threaded runtime * remove gratuitous debug printfs * Apply suggestions from code review * merge followup: nullable is enabled by default now * make eslint happy * include wasm-config.h in wasm runtime host * include wasm-config.h into the runtime pack fixes aot compilation * Add wasm-config.h to manifest * put wasm-config.h into include/wasm from the outset * put back noExitRuntime replacement for CJS Co-authored-by: Radek Doulik Co-authored-by: Radek Doulik Co-authored-by: Zoltan Varga Co-authored-by: Steve Pfister Co-authored-by: Katelyn Gadd Co-authored-by: Viktor Hofer --- eng/references.targets | 16 ++++ .../System/Threading/ThreadPool.Windows.cs | 1 - .../Directory.Build.props | 2 + .../Microsoft.NET.WebAssembly.Threading.proj | 25 ++++++ ...ics.Tracing.WebAssembly.PerfTracing.csproj | 22 +++++ .../System.Diagnostics.Tracing.Counters.cs | 10 +++ .../ref/System.Diagnostics.Tracing.csproj | 2 + .../src/ApiCompatBaseline.txt | 7 ++ .../src/MatchingRefApiCompatBaseline.txt | 7 +- .../System.Private.CoreLib.Shared.projitems | 6 +- .../src/System/AppContext.AnyOS.cs | 2 + .../Diagnostics/Tracing/CounterGroup.cs | 3 - .../Diagnostics/Tracing/DiagnosticCounter.cs | 3 - .../Diagnostics/Tracing/EventCounter.cs | 3 - .../Tracing/IncrementingEventCounter.cs | 3 - .../Tracing/IncrementingPollingCounter.cs | 3 - .../Diagnostics/Tracing/PollingCounter.cs | 3 - .../RegisteredWaitHandle.Portable.cs | 2 +- .../src/System/Threading/Thread.cs | 21 +++-- .../System/Threading/ThreadPool.Portable.cs | 1 + .../System/Threading/ThreadPoolWorkQueue.cs | 8 -- ...eading.Thread.WebAssembly.Threading.csproj | 24 ++++++ .../ref/System.Threading.Thread.cs | 8 ++ .../ref/System.Threading.Thread.csproj | 4 +- .../src/ApiCompatBaseline.txt | 6 ++ .../src/MatchingRefApiCompatBaseline.txt | 6 ++ .../src/System.Threading.Thread.csproj | 3 +- ...ng.ThreadPool.WebAssembly.Threading.csproj | 22 +++++ .../ref/System.Threading.ThreadPool.cs | 18 +++++ .../ref/System.Threading.ThreadPool.csproj | 2 + .../src/ApiCompatBaseline.txt | 11 +++ .../src/MatchingRefApiCompatBaseline.txt | 11 +++ .../settings.targets | 5 ++ src/libraries/tests.proj | 5 ++ .../System.Private.CoreLib.csproj | 18 +++-- .../System/Threading/Thread.Browser.Mono.cs | 25 ------ ...eadPoolBoundHandle.Browser.Threads.Mono.cs | 15 ++++ src/mono/mono.proj | 21 ++++- src/mono/mono/eventpipe/CMakeLists.txt | 4 +- src/mono/mono/eventpipe/ep-rt-mono.h | 1 + src/mono/mono/metadata/assembly.c | 8 +- src/mono/mono/metadata/gc.c | 18 ++++- src/mono/mono/mini/mini-exceptions.c | 2 + src/mono/mono/utils/CMakeLists.txt | 1 + src/mono/mono/utils/mono-threads-wasm.c | 55 ++++++++++++- src/mono/mono/utils/mono-threads-wasm.h | 42 ++++++++++ .../sample/wasm/browser-mt-eventpipe/Makefile | 11 +++ .../wasm/browser-mt-eventpipe/Program.cs | 51 ++++++++++++ .../Wasm.Browser.ThreadsEP.Sample.csproj | 43 ++++++++++ .../wasm/browser-mt-eventpipe/index.html | 20 +++++ .../sample/wasm/browser-mt-eventpipe/main.js | 66 +++++++++++++++ src/mono/sample/wasm/wasm.mk | 9 ++- src/mono/wasm/build/WasmApp.InTree.props | 2 +- src/mono/wasm/build/WasmApp.Native.targets | 2 + src/mono/wasm/build/WasmApp.targets | 2 + src/mono/wasm/runtime/CMakeLists.txt | 7 +- .../wasm/runtime/cjs/dotnet.cjs.extpost.js | 3 +- src/mono/wasm/runtime/corebindings.c | 1 + src/mono/wasm/runtime/dotnet.d.ts | 2 + src/mono/wasm/runtime/driver.c | 24 ++++++ src/mono/wasm/runtime/exports.ts | 9 +++ src/mono/wasm/runtime/pinvoke.c | 1 + src/mono/wasm/runtime/startup.ts | 25 +++++- src/mono/wasm/runtime/wasm-config.h.in | 13 +++ src/mono/wasm/threads.md | 80 +++++++++++++++++++ src/mono/wasm/wasm.proj | 22 ++++- 66 files changed, 793 insertions(+), 85 deletions(-) create mode 100644 src/libraries/Microsoft.NET.WebAssembly.Threading/src/Microsoft.NET.WebAssembly.Threading.proj create mode 100644 src/libraries/System.Diagnostics.Tracing.WebAssembly.PerfTracing/ref/System.Diagnostics.Tracing.WebAssembly.PerfTracing.csproj create mode 100644 src/libraries/System.Diagnostics.Tracing/src/ApiCompatBaseline.txt create mode 100644 src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj create mode 100644 src/libraries/System.Threading.Thread/src/ApiCompatBaseline.txt create mode 100644 src/libraries/System.Threading.Thread/src/MatchingRefApiCompatBaseline.txt create mode 100644 src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj create mode 100644 src/libraries/System.Threading.ThreadPool/src/ApiCompatBaseline.txt create mode 100644 src/libraries/System.Threading.ThreadPool/src/MatchingRefApiCompatBaseline.txt create mode 100644 src/libraries/testPackages/packageSettings/Microsoft.NET.WebAssembly.Threading/settings.targets delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs create mode 100644 src/mono/mono/utils/mono-threads-wasm.h create mode 100644 src/mono/sample/wasm/browser-mt-eventpipe/Makefile create mode 100644 src/mono/sample/wasm/browser-mt-eventpipe/Program.cs create mode 100644 src/mono/sample/wasm/browser-mt-eventpipe/Wasm.Browser.ThreadsEP.Sample.csproj create mode 100644 src/mono/sample/wasm/browser-mt-eventpipe/index.html create mode 100644 src/mono/sample/wasm/browser-mt-eventpipe/main.js create mode 100644 src/mono/wasm/runtime/wasm-config.h.in create mode 100644 src/mono/wasm/threads.md diff --git a/eng/references.targets b/eng/references.targets index bdb7faeed36b2..5b7418b38e61f 100644 --- a/eng/references.targets +++ b/eng/references.targets @@ -91,4 +91,20 @@ Condition="$(NetCoreAppLibraryNoReference.Contains('%(Filename);'))" /> + + + + + <_FileVersionMaj>$(FileVersion.Split('.')[0]) + <_FileVersionMin>$(FileVersion.Split('.')[1]) + <_FileVersionBld>$(FileVersion.Split('.')[2]) + <_FileVersionRev>$(FileVersion.Split('.')[3]) + $(_FileVersionMaj).$([MSBuild]::Add($(_FileVersionMin), 100)).$(_FileVersionBld).$(_FileVersionRev) + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index d478eb62e6ed9..391148cf1cd07 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -13,7 +13,6 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // - [UnsupportedOSPlatform("browser")] public sealed class RegisteredWaitHandle : MarshalByRefObject { private readonly Lock _lock; diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 18b7a886f2aeb..e98978abda49f 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -211,6 +211,7 @@ + @@ -237,6 +238,7 @@ + diff --git a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/Microsoft.NET.WebAssembly.Threading.proj b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/Microsoft.NET.WebAssembly.Threading.proj new file mode 100644 index 0000000000000..c9b7e5207f0f4 --- /dev/null +++ b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/Microsoft.NET.WebAssembly.Threading.proj @@ -0,0 +1,25 @@ + + + $(NetCoreAppCurrent) + true + false + none + false + true + true + Exposes Threading APIs for WebAssembly projects + + $(NoWarn);NU5128;NU5131 + + ref + + + + + + + diff --git a/src/libraries/System.Diagnostics.Tracing.WebAssembly.PerfTracing/ref/System.Diagnostics.Tracing.WebAssembly.PerfTracing.csproj b/src/libraries/System.Diagnostics.Tracing.WebAssembly.PerfTracing/ref/System.Diagnostics.Tracing.WebAssembly.PerfTracing.csproj new file mode 100644 index 0000000000000..f639277acdb37 --- /dev/null +++ b/src/libraries/System.Diagnostics.Tracing.WebAssembly.PerfTracing/ref/System.Diagnostics.Tracing.WebAssembly.PerfTracing.csproj @@ -0,0 +1,22 @@ + + + true + $(NetCoreAppCurrent) + true + System.Diagnostics.Tracing + + true + + false + Microsoft + true + $(DefineConstants);FEATURE_WASM_PERFTRACING + + + + + + + + + diff --git a/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.Counters.cs b/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.Counters.cs index 56c748f026e3e..79a8e9ddd909c 100644 --- a/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.Counters.cs +++ b/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.Counters.cs @@ -3,7 +3,9 @@ namespace System.Diagnostics.Tracing { +#if !FEATURE_WASM_PERFTRACING [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] +#endif public abstract partial class DiagnosticCounter : System.IDisposable { internal DiagnosticCounter() { } @@ -14,13 +16,17 @@ internal DiagnosticCounter() { } public void AddMetadata(string key, string? value) { } public void Dispose() { } } +#if !FEATURE_WASM_PERFTRACING [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] +#endif public partial class PollingCounter : System.Diagnostics.Tracing.DiagnosticCounter { public PollingCounter(string name, System.Diagnostics.Tracing.EventSource eventSource, System.Func metricProvider) { } public override string ToString() { throw null; } } +#if !FEATURE_WASM_PERFTRACING [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] +#endif public partial class IncrementingEventCounter : System.Diagnostics.Tracing.DiagnosticCounter { public IncrementingEventCounter(string name, System.Diagnostics.Tracing.EventSource eventSource) { } @@ -28,14 +34,18 @@ public IncrementingEventCounter(string name, System.Diagnostics.Tracing.EventSou public void Increment(double increment = 1) { } public override string ToString() { throw null; } } +#if !FEATURE_WASM_PERFTRACING [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] +#endif public partial class IncrementingPollingCounter : System.Diagnostics.Tracing.DiagnosticCounter { public IncrementingPollingCounter(string name, System.Diagnostics.Tracing.EventSource eventSource, System.Func totalValueProvider) { } public System.TimeSpan DisplayRateTimeScale { get { throw null; } set { } } public override string ToString() { throw null; } } +#if !FEATURE_WASM_PERFTRACING [System.Runtime.Versioning.UnsupportedOSPlatform("browser")] +#endif public partial class EventCounter : System.Diagnostics.Tracing.DiagnosticCounter { public EventCounter(string name, System.Diagnostics.Tracing.EventSource eventSource) { } diff --git a/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.csproj b/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.csproj index cfc2c469399c3..ca04d307f917f 100644 --- a/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.csproj +++ b/src/libraries/System.Diagnostics.Tracing/ref/System.Diagnostics.Tracing.csproj @@ -2,6 +2,8 @@ true $(NetCoreAppCurrent) + false + $(DefineConstants);FEATURE_WASM_PERFTRACING diff --git a/src/libraries/System.Diagnostics.Tracing/src/ApiCompatBaseline.txt b/src/libraries/System.Diagnostics.Tracing/src/ApiCompatBaseline.txt new file mode 100644 index 0000000000000..6c54217770126 --- /dev/null +++ b/src/libraries/System.Diagnostics.Tracing/src/ApiCompatBaseline.txt @@ -0,0 +1,7 @@ +Compat issues with assembly System.Diagnostics.Tracing: +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.DiagnosticCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.EventCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.IncrementingEventCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.IncrementingPollingCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.PollingCounter' in the contract but not the implementation. +Total Issues: 4 diff --git a/src/libraries/System.Diagnostics.Tracing/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.Diagnostics.Tracing/src/MatchingRefApiCompatBaseline.txt index 754bf028a45ad..d5f2617641ea3 100644 --- a/src/libraries/System.Diagnostics.Tracing/src/MatchingRefApiCompatBaseline.txt +++ b/src/libraries/System.Diagnostics.Tracing/src/MatchingRefApiCompatBaseline.txt @@ -1,6 +1,11 @@ Compat issues with assembly System.Diagnostics.Tracing: +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.DiagnosticCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.EventCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.IncrementingEventCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.IncrementingPollingCounter' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Diagnostics.Tracing.PollingCounter' in the contract but not the implementation. MembersMustExist : Member 'protected void System.Diagnostics.Tracing.EventCounter.Flush()' does not exist in the reference but it does exist in the implementation. CannotMakeTypeAbstract : Type 'System.Diagnostics.Tracing.EventListener' is abstract in the reference but is not abstract in the implementation. CannotMakeMoreVisible : Visibility of member 'System.Diagnostics.Tracing.EventListener..ctor()' is 'protected' in the reference but 'public' in the implementation. CannotMakeMoreVisible : Visibility of member 'System.Diagnostics.Tracing.EventListener.EventSourceIndex(System.Diagnostics.Tracing.EventSource)' is 'protected' in the reference but 'public' in the implementation. -Total Issues: 4 +Total Issues: 8 diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index b2a4460e5909e..b6c8e95fc10d2 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -74,7 +74,7 @@ - + @@ -2329,7 +2329,7 @@ - + @@ -2346,7 +2346,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs index 25bda986d7408..9fdaf0e918112 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs @@ -12,6 +12,7 @@ namespace System { public static partial class AppContext { +#if !TARGET_BROWSER [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", Justification = "Single File apps should always set APP_CONTEXT_BASE_DIRECTORY therefore code handles Assembly.Location equals null")] private static string GetBaseDirectoryCore() @@ -33,6 +34,7 @@ private static string GetBaseDirectoryCore() return directory; } +#endif #if FEATURE_PERFTRACING internal static void LogSwitchValues(RuntimeEventSource ev) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs index aec84922437a3..cf7b4471ff7e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs @@ -15,9 +15,6 @@ namespace Microsoft.Diagnostics.Tracing namespace System.Diagnostics.Tracing #endif { -#if NETCOREAPP - [UnsupportedOSPlatform("browser")] -#endif internal sealed class CounterGroup { private readonly EventSource _eventSource; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs index c03c9c5cdd94b..aae0333497c0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs @@ -20,9 +20,6 @@ namespace System.Diagnostics.Tracing /// DiagnosticCounter is an abstract class that serves as the parent class for various Counter* classes, /// namely EventCounter, PollingCounter, IncrementingEventCounter, and IncrementingPollingCounter. /// -#if NETCOREAPP - [UnsupportedOSPlatform("browser")] -#endif public abstract class DiagnosticCounter : IDisposable { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs index 0c4460facc30f..6fea134739756 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs @@ -24,9 +24,6 @@ namespace System.Diagnostics.Tracing /// See https://github.com/dotnet/runtime/blob/main/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs /// which shows tests, which are also useful in seeing actual use. /// -#if NETCOREAPP - [UnsupportedOSPlatform("browser")] -#endif public partial class EventCounter : DiagnosticCounter { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs index 13e5f1906ec32..3d20263b80f4d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs @@ -21,9 +21,6 @@ namespace System.Diagnostics.Tracing /// It does not calculate statistics like mean, standard deviation, etc. because it only accumulates /// the counter value. /// -#if NETCOREAPP - [UnsupportedOSPlatform("browser")] -#endif public partial class IncrementingEventCounter : DiagnosticCounter { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs index 21d14e6b0b044..4b209901093e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs @@ -22,9 +22,6 @@ namespace System.Diagnostics.Tracing /// Unlike IncrementingEventCounter, this takes in a polling callback that it can call to update /// its own metric periodically. /// -#if NETCOREAPP - [UnsupportedOSPlatform("browser")] -#endif public partial class IncrementingPollingCounter : DiagnosticCounter { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs index a2a4244fd13ad..e19f1f37fdb64 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs @@ -20,9 +20,6 @@ namespace System.Diagnostics.Tracing /// function to collect metrics on its own rather than the user having to call WriteMetric() /// every time. /// -#if NETCOREAPP - [UnsupportedOSPlatform("browser")] -#endif public partial class PollingCounter : DiagnosticCounter { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index 5fea9dda9246e..de31539b74b0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -10,12 +10,12 @@ namespace System.Threading /// /// An object representing the registration of a via . /// - [UnsupportedOSPlatform("browser")] public sealed partial class RegisteredWaitHandle : MarshalByRefObject { internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, int millisecondsTimeout, bool repeating) { + Thread.ThrowIfNoThreadStart(); Handle = waitHandle.SafeWaitHandle; Callback = callbackHelper; TimeoutDurationMs = millisecondsTimeout; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index 3d4b2d480e6a2..7efa9b6a2a012 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -154,15 +154,25 @@ public Thread(ParameterizedThreadStart start, int maxStackSize) } #if !TARGET_BROWSER - [UnsupportedOSPlatformGuard("browser")] internal static bool IsThreadStartSupported => true; +#else +#if FEATURE_WASM_THREADS + internal static bool IsThreadStartSupported => true; +#else + internal static bool IsThreadStartSupported => false; +#endif +#endif + + internal static void ThrowIfNoThreadStart() { + if (!IsThreadStartSupported) + throw new PlatformNotSupportedException(); + } /// Causes the operating system to change the state of the current instance to , and optionally supplies an object containing data to be used by the method the thread executes. /// An object that contains data to be used by the method the thread executes. /// The thread has already been started. /// There is not enough memory available to start this thread. /// This thread was created using a delegate instead of a delegate. - [UnsupportedOSPlatform("browser")] public void Start(object? parameter) => Start(parameter, captureContext: true); /// Causes the operating system to change the state of the current instance to , and optionally supplies an object containing data to be used by the method the thread executes. @@ -174,11 +184,12 @@ public Thread(ParameterizedThreadStart start, int maxStackSize) /// Unlike , which captures the current and uses that context to invoke the thread's delegate, /// explicitly avoids capturing the current context and flowing it to the invocation. /// - [UnsupportedOSPlatform("browser")] public void UnsafeStart(object? parameter) => Start(parameter, captureContext: false); private void Start(object? parameter, bool captureContext) { + ThrowIfNoThreadStart(); + StartHelper? startHelper = _startHelper; // In the case of a null startHelper (second call to start on same thread) @@ -201,7 +212,6 @@ private void Start(object? parameter, bool captureContext) /// Causes the operating system to change the state of the current instance to . /// The thread has already been started. /// There is not enough memory available to start this thread. - [UnsupportedOSPlatform("browser")] public void Start() => Start(captureContext: true); /// Causes the operating system to change the state of the current instance to . @@ -211,11 +221,11 @@ private void Start(object? parameter, bool captureContext) /// Unlike , which captures the current and uses that context to invoke the thread's delegate, /// explicitly avoids capturing the current context and flowing it to the invocation. /// - [UnsupportedOSPlatform("browser")] public void UnsafeStart() => Start(captureContext: false); private void Start(bool captureContext) { + ThrowIfNoThreadStart(); StartHelper? startHelper = _startHelper; // In the case of a null startHelper (second call to start on same thread) @@ -228,7 +238,6 @@ private void Start(bool captureContext) StartCore(); } -#endif private void RequireCurrentThread() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index 2b62413a04827..25ab1e6fcb72d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -107,6 +107,7 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( ArgumentNullException.ThrowIfNull(waitObject); ArgumentNullException.ThrowIfNull(callBack); + Thread.ThrowIfNoThreadStart(); RegisteredWaitHandle registeredHandle = new RegisteredWaitHandle( waitObject, new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index bed31df066c1a..840e0b68e7bf5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1169,7 +1169,6 @@ public static partial class ThreadPool internal static bool EnableWorkerTracking => IsWorkerTrackingEnabledInConfig && EventSource.IsSupported; [CLSCompliant(false)] - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1184,7 +1183,6 @@ public static RegisteredWaitHandle RegisterWaitForSingleObject( } [CLSCompliant(false)] - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1198,7 +1196,6 @@ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, false); } - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1212,7 +1209,6 @@ public static RegisteredWaitHandle RegisterWaitForSingleObject( return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)millisecondsTimeOutInterval, executeOnlyOnce, true); } - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1226,7 +1222,6 @@ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)millisecondsTimeOutInterval, executeOnlyOnce, false); } - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1242,7 +1237,6 @@ public static RegisteredWaitHandle RegisterWaitForSingleObject( return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)millisecondsTimeOutInterval, executeOnlyOnce, true); } - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1258,7 +1252,6 @@ public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)millisecondsTimeOutInterval, executeOnlyOnce, false); } - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -1275,7 +1268,6 @@ bool executeOnlyOnce return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)tm, executeOnlyOnce, true); } - [UnsupportedOSPlatform("browser")] public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, diff --git a/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj b/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj new file mode 100644 index 0000000000000..341b1140e3778 --- /dev/null +++ b/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj @@ -0,0 +1,24 @@ + + + $(NetCoreAppCurrent) + true + System.Threading.Thread + true + + true + + false + Microsoft + true + $(DefineConstants);FEATURE_WASM_THREADS + + + + + + + + + + + diff --git a/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.cs b/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.cs index c34b32cc8aff7..d764627ce6365 100644 --- a/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.cs +++ b/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.cs @@ -89,16 +89,24 @@ public static void SetData(System.LocalDataStoreSlot slot, object? data) { } public static void Sleep(int millisecondsTimeout) { } public static void Sleep(System.TimeSpan timeout) { } public static void SpinWait(int iterations) { } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public void Start() { } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public void Start(object? parameter) { } [System.ObsoleteAttribute("Thread.Suspend has been deprecated. Use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources.")] public void Suspend() { } public bool TrySetApartmentState(System.Threading.ApartmentState state) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public void UnsafeStart() { } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public void UnsafeStart(object? parameter) { } public static byte VolatileRead(ref byte address) { throw null; } public static double VolatileRead(ref double address) { throw null; } diff --git a/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.csproj b/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.csproj index 8a2b6b27d720e..c0668c328e24e 100644 --- a/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.csproj +++ b/src/libraries/System.Threading.Thread/ref/System.Threading.Thread.csproj @@ -1,6 +1,8 @@ $(NetCoreAppCurrent) + false + $(DefineConstants);FEATURE_WASM_THREADS @@ -11,4 +13,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Threading.Thread/src/ApiCompatBaseline.txt b/src/libraries/System.Threading.Thread/src/ApiCompatBaseline.txt new file mode 100644 index 0000000000000..33ad3c96ed62b --- /dev/null +++ b/src/libraries/System.Threading.Thread/src/ApiCompatBaseline.txt @@ -0,0 +1,6 @@ +Compat issues with assembly System.Threading.Thread: +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.Start()' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.Start(System.Object)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.UnsafeStart()' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.UnsafeStart(System.Object)' in the contract but not the implementation. +Total Issues: 4 diff --git a/src/libraries/System.Threading.Thread/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.Threading.Thread/src/MatchingRefApiCompatBaseline.txt new file mode 100644 index 0000000000000..33ad3c96ed62b --- /dev/null +++ b/src/libraries/System.Threading.Thread/src/MatchingRefApiCompatBaseline.txt @@ -0,0 +1,6 @@ +Compat issues with assembly System.Threading.Thread: +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.Start()' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.Start(System.Object)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.UnsafeStart()' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.Thread.UnsafeStart(System.Object)' in the contract but not the implementation. +Total Issues: 4 diff --git a/src/libraries/System.Threading.Thread/src/System.Threading.Thread.csproj b/src/libraries/System.Threading.Thread/src/System.Threading.Thread.csproj index 49a3f302b2ef6..319f6f543bd99 100644 --- a/src/libraries/System.Threading.Thread/src/System.Threading.Thread.csproj +++ b/src/libraries/System.Threading.Thread/src/System.Threading.Thread.csproj @@ -2,8 +2,9 @@ true $(NetCoreAppCurrent) + false - \ No newline at end of file + diff --git a/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj b/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj new file mode 100644 index 0000000000000..8afbfd514f9b2 --- /dev/null +++ b/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj @@ -0,0 +1,22 @@ + + + System.Threading.ThreadPool + true + $(NetCoreAppCurrent) + true + + true + + false + Microsoft + true + $(DefineConstants);FEATURE_WASM_THREADS + + + + + + + + + diff --git a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs index 23411ce260f33..20ca9318b980c 100644 --- a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs +++ b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.cs @@ -10,7 +10,9 @@ public partial interface IThreadPoolWorkItem { void Execute(); } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public sealed partial class RegisteredWaitHandle : System.MarshalByRefObject { internal RegisteredWaitHandle() { } @@ -32,14 +34,22 @@ public static partial class ThreadPool public static bool QueueUserWorkItem(System.Threading.WaitCallback callBack) { throw null; } public static bool QueueUserWorkItem(System.Threading.WaitCallback callBack, object? state) { throw null; } public static bool QueueUserWorkItem(System.Action callBack, TState state, bool preferLocal) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle RegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, int millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle RegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, long millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle RegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, System.TimeSpan timeout, bool executeOnlyOnce) { throw null; } [System.CLSCompliantAttribute(false)] +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle RegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, uint millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; } public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { throw null; } public static bool SetMinThreads(int workerThreads, int completionPortThreads) { throw null; } @@ -49,14 +59,22 @@ public static partial class ThreadPool public static bool UnsafeQueueUserWorkItem(System.Threading.IThreadPoolWorkItem callBack, bool preferLocal) { throw null; } public static bool UnsafeQueueUserWorkItem(System.Threading.WaitCallback callBack, object? state) { throw null; } public static bool UnsafeQueueUserWorkItem(System.Action callBack, TState state, bool preferLocal) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, int millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, long millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; } +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, System.TimeSpan timeout, bool executeOnlyOnce) { throw null; } [System.CLSCompliantAttribute(false)] +#if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object? state, uint millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; } } public delegate void WaitCallback(object? state); diff --git a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj index 6978db726d7ff..8db89a9f99d7d 100644 --- a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj +++ b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj @@ -2,6 +2,8 @@ true $(NetCoreAppCurrent) + false + $(DefineConstants);FEATURE_WASM_THREADS diff --git a/src/libraries/System.Threading.ThreadPool/src/ApiCompatBaseline.txt b/src/libraries/System.Threading.ThreadPool/src/ApiCompatBaseline.txt new file mode 100644 index 0000000000000..3dfbafeadd916 --- /dev/null +++ b/src/libraries/System.Threading.ThreadPool/src/ApiCompatBaseline.txt @@ -0,0 +1,11 @@ +Compat issues with assembly System.Threading.ThreadPool: +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.RegisteredWaitHandle' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int32, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int64, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.TimeSpan, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.UInt32, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int32, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int64, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.TimeSpan, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.UInt32, System.Boolean)' in the contract but not the implementation. +Total Issues: 9 diff --git a/src/libraries/System.Threading.ThreadPool/src/MatchingRefApiCompatBaseline.txt b/src/libraries/System.Threading.ThreadPool/src/MatchingRefApiCompatBaseline.txt new file mode 100644 index 0000000000000..3dfbafeadd916 --- /dev/null +++ b/src/libraries/System.Threading.ThreadPool/src/MatchingRefApiCompatBaseline.txt @@ -0,0 +1,11 @@ +Compat issues with assembly System.Threading.ThreadPool: +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.RegisteredWaitHandle' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int32, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int64, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.TimeSpan, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.RegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.UInt32, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int32, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.Int64, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.TimeSpan, System.Boolean)' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.Versioning.UnsupportedOSPlatformAttribute' exists on 'System.Threading.ThreadPool.UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle, System.Threading.WaitOrTimerCallback, System.Object, System.UInt32, System.Boolean)' in the contract but not the implementation. +Total Issues: 9 diff --git a/src/libraries/testPackages/packageSettings/Microsoft.NET.WebAssembly.Threading/settings.targets b/src/libraries/testPackages/packageSettings/Microsoft.NET.WebAssembly.Threading/settings.targets new file mode 100644 index 0000000000000..a5e5b2e06a073 --- /dev/null +++ b/src/libraries/testPackages/packageSettings/Microsoft.NET.WebAssembly.Threading/settings.targets @@ -0,0 +1,5 @@ + + + true + + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index a2e7b80edfce7..344e383b58cf8 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -50,6 +50,11 @@ + + + + + diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 8a16000e3f5e1..18ec40cc3ca0b 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -113,11 +113,13 @@ $(DefineConstants);MONO_FEATURE_SRE true + true + true true true true - true - true + true + true true @@ -126,6 +128,8 @@ $(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS $(DefineConstants);FEATURE_PERFTRACING $(DefineConstants);FEATURE_OBJCMARSHAL + $(DefineConstants);FEATURE_WASM_THREADS + $(DefineConstants);FEATURE_WASM_PERFTRACING @@ -284,11 +288,15 @@ - - + + + + + - + + diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs deleted file mode 100644 index 435a155c4ed16..0000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.Versioning; - -namespace System.Threading -{ - public partial class Thread - { - [UnsupportedOSPlatformGuard("browser")] - internal static bool IsThreadStartSupported => false; - - [UnsupportedOSPlatform("browser")] - public void Start() => throw new PlatformNotSupportedException(); - - [UnsupportedOSPlatform("browser")] - public void Start(object parameter) => throw new PlatformNotSupportedException(); - - [UnsupportedOSPlatform("browser")] - public void UnsafeStart() => throw new PlatformNotSupportedException(); - - [UnsupportedOSPlatform("browser")] - public void UnsafeStart(object parameter) => throw new PlatformNotSupportedException(); - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs new file mode 100644 index 0000000000000..c1bcfa75b8c2f --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public sealed partial class ThreadPoolBoundHandle : IDisposable + { + private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); + } + } +} diff --git a/src/mono/mono.proj b/src/mono/mono.proj index e025820e33f57..83c32e6df65cc 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -9,6 +9,8 @@ - MonoAOTLLVMDir - [optional] the directory where LLVM is located, for an AOT-only Mono - MonoVerboseBuild - enable verbose build - MonoThreadSuspend - coop,hybrid,preemptive - default thread suspend mode + - MonoWasmThreads - build runtime with threading support for wasm + - MonoWasmThreadsNoUser - build runtime with threading support for wasm, but with only internal utility threads (MonoWasmThreads must be defined too) --> @@ -50,6 +52,8 @@ <_CompilerTargetArch Condition="'$(RealTargetArchitecture)' != ''">$(RealTargetArchitecture) $([MSBuild]::NormalizeDirectory('$(RepositoryEngineeringDir)', 'common')) $([MSBuild]::NormalizePath('$(RepositoryEngineeringCommonDir)', 'cross', 'toolchain.cmake')) + true + true @@ -321,17 +325,25 @@ <_MonoMinimal Condition="'$(Configuration)' == 'Release'">,debugger_agent,log_dest <_MonoMinimal Condition="'$(Configuration)' == 'Release' and '$(MonoEnableAssertMessages)' != 'true'">$(_MonoMinimal),assert_messages + <_MonoMinimal Condition="'$(MonoWasmThreads)' != 'true'">$(_MonoMinimal),threads + <_MonoMinimal Condition="'$(MonoWasmThreadsNoUser)' == 'true'">$(_MonoMinimal),wasm_user_threads - <_MonoCMakeArgs Include="-DENABLE_MINIMAL=jit,sgen_major_marksweep_conc,sgen_split_nursery,sgen_gc_bridge,sgen_toggleref,sgen_debug_helpers,sgen_binary_protocol,logging,interpreter,threads,qcalls$(_MonoMinimal)"/> + <_MonoCMakeArgs Include="-DENABLE_MINIMAL=jit,sgen_major_marksweep_conc,sgen_split_nursery,sgen_gc_bridge,sgen_toggleref,sgen_debug_helpers,sgen_binary_protocol,logging,interpreter,qcalls$(_MonoMinimal)"/> <_MonoCMakeArgs Include="-DENABLE_INTERP_LIB=1"/> <_MonoCMakeArgs Include="-DDISABLE_ICALL_TABLES=1"/> <_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/> <_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/> <_MonoCMakeArgs Include="-DENABLE_LLVM_RUNTIME=1"/> <_MonoCFLAGS Include="-fexceptions"/> + <_MonoCFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-pthread"/> + <_MonoCFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-D_GNU_SOURCE=1" /> <_MonoCXXFLAGS Include="-fexceptions"/> - <_MonoCFLAGS Include="$(EscapedQuoteW)-I$([MSBuild]::NormalizePath('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm', 'native', 'include'))$(EscapedQuoteW)"/> + <_MonoCXXFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-pthread"/> + <_MonoCXXFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-D_GNU_SOURCE=1" /> + <_MonoCFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="$(EscapedQuoteW)-I$([MSBuild]::NormalizePath('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm-threads', 'native', 'include'))$(EscapedQuoteW)"/> + + <_MonoCFLAGS Condition="'$(MonoWasmThreads)' != 'true'" Include="$(EscapedQuoteW)-I$([MSBuild]::NormalizePath('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm', 'native', 'include'))$(EscapedQuoteW)"/> @@ -457,6 +469,11 @@ <_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1"/> + + <_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS=1"/> + <_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1"/> + + <_MonoCMakeArgs Include="-DSTATIC_COMPONENTS=1" /> diff --git a/src/mono/mono/eventpipe/CMakeLists.txt b/src/mono/mono/eventpipe/CMakeLists.txt index fe64183c44905..30d3139e5879a 100644 --- a/src/mono/mono/eventpipe/CMakeLists.txt +++ b/src/mono/mono/eventpipe/CMakeLists.txt @@ -10,9 +10,9 @@ if(ENABLE_PERFTRACING) add_definitions(-DENABLE_PERFTRACING_PAL_TCP) endif (FEATURE_PERFTRACING_PAL_TCP) - if (FEATURE_PERFTRACING_DISABLE_LISTEN_PORTS) + if (FEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS) add_definitions(-DDISABLE_PERFTRACING_LISTEN_PORTS) - endif (FEATURE_PERFTRACING_DISABLE_LISTEN_PORTS) + endif (FEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS) if (FEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT) add_definitions(-DDISABLE_PERFTRACING_DEFAULT_LISTEN_PORT) diff --git a/src/mono/mono/eventpipe/ep-rt-mono.h b/src/mono/mono/eventpipe/ep-rt-mono.h index 3cf0214b351fb..40a7e15d3d26f 100644 --- a/src/mono/mono/eventpipe/ep-rt-mono.h +++ b/src/mono/mono/eventpipe/ep-rt-mono.h @@ -23,6 +23,7 @@ #include #include #include +#include "mono/utils/mono-logger-internals.h" #include #include diff --git a/src/mono/mono/metadata/assembly.c b/src/mono/mono/metadata/assembly.c index ba5f28a8bc2e6..8f74d2bfd5826 100644 --- a/src/mono/mono/metadata/assembly.c +++ b/src/mono/mono/metadata/assembly.c @@ -1511,6 +1511,7 @@ mono_assembly_open_from_bundle (MonoAssemblyLoadContext *alc, const char *filena * purpose assembly loading mechanism. */ MonoImage *image = NULL; + MONO_ENTER_GC_UNSAFE; gboolean is_satellite = culture && culture [0] != 0; if (is_satellite) @@ -1522,6 +1523,7 @@ mono_assembly_open_from_bundle (MonoAssemblyLoadContext *alc, const char *filena mono_image_addref (image); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", filename); } + MONO_EXIT_GC_UNSAFE; return image; } @@ -2844,11 +2846,15 @@ mono_assembly_load_full (MonoAssemblyName *aname, const char *basedir, MonoImage MonoAssembly* mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenStatus *status) { + MonoAssembly *result = NULL; + MONO_ENTER_GC_UNSAFE; MonoAssemblyByNameRequest req; mono_assembly_request_prepare_byname (&req, mono_alc_get_default ()); req.requesting_assembly = NULL; req.basedir = basedir; - return mono_assembly_request_byname (aname, &req, status); + result = mono_assembly_request_byname (aname, &req, status); + MONO_EXIT_GC_UNSAFE; + return result; } /** diff --git a/src/mono/mono/metadata/gc.c b/src/mono/mono/metadata/gc.c index 7434ab68d3cbc..fc4526d72de36 100644 --- a/src/mono/mono/metadata/gc.c +++ b/src/mono/mono/metadata/gc.c @@ -42,6 +42,7 @@ #include #include #include +#include #ifndef HOST_WIN32 #include #endif @@ -685,6 +686,21 @@ ves_icall_System_GCHandle_InternalSet (MonoGCHandle handle, MonoObjectHandle obj static MonoCoopSem finalizer_sem; static volatile gboolean finished; +#ifdef HOST_WASM + +static void +mono_wasm_gc_finalize_notify (void) +{ +#if 0 + /* use this if we are going to start the finalizer thread on wasm. */ + mono_coop_sem_post (&finalizer_sem); +#else + mono_threads_schedule_background_job (mono_runtime_do_background_work); +#endif +} + +#endif /* HOST_WASM */ + /* * mono_gc_finalize_notify: * @@ -705,7 +721,7 @@ mono_gc_finalize_notify (void) #if defined(HOST_WASI) // TODO: Schedule the background job on WASI. Threads aren't yet supported in this build. #elif defined(HOST_WASM) - mono_threads_schedule_background_job (mono_runtime_do_background_work); + mono_wasm_gc_finalize_notify (); #else mono_coop_sem_post (&finalizer_sem); #endif diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index 477b24f7f82a3..f6ce3463d32ef 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -3270,6 +3270,8 @@ mono_thread_state_init (MonoThreadUnwindState *ctx) #if defined(MONO_CROSS_COMPILE) ctx->valid = FALSE; //A cross compiler doesn't need to suspend. +#elif defined(HOST_WASM) + MONO_INIT_CONTEXT_FROM_FUNC (&(ctx->ctx), mono_thread_state_init); #elif defined(HOST_WASI) // TODO: For WASI, we need to review how thread state is initialized #elif MONO_ARCH_HAS_MONO_CONTEXT diff --git a/src/mono/mono/utils/CMakeLists.txt b/src/mono/mono/utils/CMakeLists.txt index 8ebc05f171d7a..d0a01e7793550 100644 --- a/src/mono/mono/utils/CMakeLists.txt +++ b/src/mono/mono/utils/CMakeLists.txt @@ -135,6 +135,7 @@ set(utils_common_sources mono-threads-android.c mono-threads-haiku.c mono-threads-aix.c + mono-threads-wasm.h mono-threads-wasm.c mono-threads-sunos.c mono-threads.h diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index e7415532cbf09..8da132a23b772 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -8,13 +8,19 @@ #include #include +#include #include #ifdef HOST_BROWSER +#include + #include #include +#ifndef DISABLE_THREADS +#include +#endif #define round_down(addr, val) ((void*)((addr) & ~((val) - 1))) @@ -110,7 +116,6 @@ mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2) return id1 == id2; } - MonoNativeThreadId mono_native_thread_id_get (void) { @@ -319,11 +324,22 @@ mono_memory_barrier_process_wide (void) G_EXTERN_C extern void schedule_background_exec (void); +/* jobs is not protected by a mutex, only access from a single thread! */ static GSList *jobs; void mono_threads_schedule_background_job (background_job_cb cb) { +#ifndef DISABLE_THREADS + if (!mono_threads_wasm_is_browser_thread ()) { + THREADS_DEBUG ("worker %p queued job %p\n", (gpointer)pthread_self(), (gpointer) cb); + mono_threads_wasm_async_run_in_main_thread_vi ((void (*)(gpointer))mono_threads_schedule_background_job, cb); + return; + } +#endif + + THREADS_DEBUG ("main thread queued job %p\n", (gpointer) cb); + if (!jobs) schedule_background_exec (); @@ -339,6 +355,9 @@ G_EXTERN_C EMSCRIPTEN_KEEPALIVE void mono_background_exec (void) { +#ifndef DISABLE_THREADS + g_assert (mono_threads_wasm_is_browser_thread ()); +#endif GSList *j = jobs, *cur; jobs = NULL; @@ -349,6 +368,40 @@ mono_background_exec (void) g_slist_free (j); } +gboolean +mono_threads_platform_is_main_thread (void) +{ +#ifdef DISABLE_THREADS + return TRUE; +#else + return emscripten_is_main_runtime_thread (); +#endif +} + +gboolean +mono_threads_wasm_is_browser_thread (void) +{ +#ifdef DISABLE_THREADS + return TRUE; +#else + return emscripten_is_main_browser_thread (); +#endif +} + +#ifndef DISABLE_THREADS +void +mono_threads_wasm_async_run_in_main_thread (void (*func) (void)) +{ + emscripten_async_run_in_main_runtime_thread (EM_FUNC_SIG_V, func); +} + +void +mono_threads_wasm_async_run_in_main_thread_vi (void (*func) (gpointer), gpointer user_data) +{ + emscripten_async_run_in_main_runtime_thread (EM_FUNC_SIG_VI, func, user_data); +} +#endif /* DISABLE_THREADS */ + #endif /* HOST_BROWSER */ #else diff --git a/src/mono/mono/utils/mono-threads-wasm.h b/src/mono/mono/utils/mono-threads-wasm.h new file mode 100644 index 0000000000000..ecad01a7ec603 --- /dev/null +++ b/src/mono/mono/utils/mono-threads-wasm.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +/** + * \file + * Low-level threading for Webassembly + */ + +#ifndef __MONO_THREADS_WASM_H__ +#define __MONO_THREADS_WASM_H__ + +#include + +#ifdef HOST_WASM + +/* + * declared in mono-threads.h + * + * gboolean + * mono_threads_platform_is_main_thread (void); + */ + +gboolean +mono_threads_wasm_is_browser_thread (void); + +#ifndef DISABLE_THREADS +/** + * Runs the given function asynchronously on the main thread. + * See emscripten/threading.h emscripten_async_run_in_main_runtime_thread + */ +void +mono_threads_wasm_async_run_in_main_thread (void (*func) (void)); + +/* + * Variant that takes an argument. Add more variants as needed. + */ +void +mono_threads_wasm_async_run_in_main_thread_vi (void (*func)(gpointer), gpointer user_data); +#endif /* DISABLE_THREADS */ + +#endif /* HOST_WASM*/ + +#endif /* __MONO_THREADS_WASM_H__ */ diff --git a/src/mono/sample/wasm/browser-mt-eventpipe/Makefile b/src/mono/sample/wasm/browser-mt-eventpipe/Makefile new file mode 100644 index 0000000000000..8fddd8371b830 --- /dev/null +++ b/src/mono/sample/wasm/browser-mt-eventpipe/Makefile @@ -0,0 +1,11 @@ +TOP=../../../../.. + +include ../wasm.mk + +ifneq ($(AOT),) +override MSBUILD_ARGS+=/p:RunAOTCompilation=true +endif + +PROJECT_NAME=Wasm.Browser.ThreadsEP.Sample.csproj + +run: run-browser diff --git a/src/mono/sample/wasm/browser-mt-eventpipe/Program.cs b/src/mono/sample/wasm/browser-mt-eventpipe/Program.cs new file mode 100644 index 0000000000000..8683f673d92ef --- /dev/null +++ b/src/mono/sample/wasm/browser-mt-eventpipe/Program.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Threading; +using System.Runtime.Versioning; + +namespace Sample +{ + public class Test + { + public static void Main(string[] args) + { + Console.WriteLine ("Hello, World!"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [SupportedOSPlatform("browser")] // ask the analyzer to warn if we use APIs not supported on browser-wasm + public static int TestMeaning() + { + List myList = new List{ 1, 2, 3, 4 }; + Console.WriteLine(myList); + + Thread t = new Thread(new ThreadStart(ThreadFuncTest)); + t.Start(); + + for (int i = 0; i < 5; i++) + { + Console.WriteLine("Main Thread is doing stuff too... " + i.ToString()); + Thread.Sleep(100); + GC.Collect(); + } + + return 42; + } + + public static void ThreadFuncTest() + { + Console.WriteLine("Hello from another thread"); + + for (int i = 0; i < 5; i++) + { + Console.WriteLine("Sleeping from another thread: " + i.ToString()); + Thread.Sleep(100); + GC.Collect(); + } + } + } +} diff --git a/src/mono/sample/wasm/browser-mt-eventpipe/Wasm.Browser.ThreadsEP.Sample.csproj b/src/mono/sample/wasm/browser-mt-eventpipe/Wasm.Browser.ThreadsEP.Sample.csproj new file mode 100644 index 0000000000000..124d312b33d21 --- /dev/null +++ b/src/mono/sample/wasm/browser-mt-eventpipe/Wasm.Browser.ThreadsEP.Sample.csproj @@ -0,0 +1,43 @@ + + + true + main.js + true + embedded + 1 + false + true + true + $(ExecXHarnessCmd) wasm test-browser --app=. --browser=Chrome $(XHarnessBrowserPathArg) --html-file=index.html --output-directory=$(XHarnessOutput) -- $(MSBuildProjectName).dll + true + + + + + + + + + <_SampleProject>Wasm.Browser.CJS.Sample.csproj + + + + + true + + + + + + + + + + + + + diff --git a/src/mono/sample/wasm/browser-mt-eventpipe/index.html b/src/mono/sample/wasm/browser-mt-eventpipe/index.html new file mode 100644 index 0000000000000..e0a5f09c94108 --- /dev/null +++ b/src/mono/sample/wasm/browser-mt-eventpipe/index.html @@ -0,0 +1,20 @@ + + + + + + + Sample CJS + + + + + + + Answer to the Ultimate Question of Life, the Universe, and Everything is : + + + + + + \ No newline at end of file diff --git a/src/mono/sample/wasm/browser-mt-eventpipe/main.js b/src/mono/sample/wasm/browser-mt-eventpipe/main.js new file mode 100644 index 0000000000000..ba5a4075fe7c4 --- /dev/null +++ b/src/mono/sample/wasm/browser-mt-eventpipe/main.js @@ -0,0 +1,66 @@ +function wasm_exit(exit_code) { + /* Set result in a tests_done element, to be read by xharness in runonly CI test */ + const tests_done_elem = document.createElement("label"); + tests_done_elem.id = "tests_done"; + tests_done_elem.innerHTML = exit_code.toString(); + document.body.appendChild(tests_done_elem); + + console.log(`WASM EXIT ${exit_code}`); +} + +function Uint8ToString(u8a){ + var CHUNK_SZ = 0x8000; + var c = []; + for (var i=0; i < u8a.length; i+=CHUNK_SZ) { + c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ))); + } + return c.join(""); +} + +async function loadRuntime() { + globalThis.exports = {}; + await import("./dotnet.js"); + return globalThis.exports.createDotnetRuntime; +} + +async function main() { + const createDotnetRuntime = await loadRuntime(); + const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime(() => { + console.log('user code in createDotnetRuntime') + return { + disableDotnet6Compatibility: true, + configSrc: "./mono-config.json", + preInit: () => { console.log('user code Module.preInit') }, + preRun: () => { console.log('user code Module.preRun') }, + onRuntimeInitialized: () => { console.log('user code Module.onRuntimeInitialized') }, + postRun: () => { console.log('user code Module.postRun') }, + } + }); + globalThis.__Module = Module; + globalThis.MONO = MONO; + console.log('after createDotnetRuntime') + + try { + const testMeaning = BINDING.bind_static_method("[Wasm.Browser.ThreadsEP.Sample] Sample.Test:TestMeaning"); + const ret = testMeaning(); + document.getElementById("out").innerHTML = `${ret} as computed on dotnet ver ${RuntimeBuildInfo.ProductVersion}`; + + console.debug(`ret: ${ret}`); + + let exit_code = ret == 42 ? 0 : 1; + Module._mono_wasm_exit(exit_code); + + wasm_exit(exit_code); + } catch (err) { + console.log(`WASM ERROR ${err}`); + + var b = Module.FS.readFile('/trace.nettrace'); + var bits = btoa((Uint8ToString(b))); + + window.open("data:application/octet-stream;base64," + bits); + + wasm_exit(2); + } +} + +setTimeout(main, 10000); diff --git a/src/mono/sample/wasm/wasm.mk b/src/mono/sample/wasm/wasm.mk index 1d9d407e3dff6..fbe782a3deb95 100644 --- a/src/mono/sample/wasm/wasm.mk +++ b/src/mono/sample/wasm/wasm.mk @@ -10,6 +10,9 @@ CONFIG?=Release WASM_DEFAULT_BUILD_ARGS?=/p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG) +# we set specific headers to enable SharedArrayBuffer support in browsers for threading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements +CORS_HEADERS?= -h "Cross-Origin-Opener-Policy:same-origin" -h "Cross-Origin-Embedder-Policy:require-corp" + # if we're in a container, don't try to open the browser ifneq ("$(wildcard /.dockerenv)", "") OPEN_BROWSER= @@ -22,10 +25,10 @@ endif all: publish build: - EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) build $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME) + $(DOTNET) build $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME) publish: - EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) -p:WasmBuildOnlyAfterPublish=true $(MSBUILD_ARGS) $(PROJECT_NAME) + $(DOTNET) publish $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) -p:WasmBuildOnlyAfterPublish=true $(MSBUILD_ARGS) $(PROJECT_NAME) clean: rm -rf bin $(TOP)/artifacts/obj/mono/$(PROJECT_NAME:%.csproj=%) @@ -35,7 +38,7 @@ run-browser: echo "The tool dotnet-serve could not be found. Install with: $(DOTNET) tool install --global dotnet-serve"; \ exit 1; \ else \ - $(DOTNET) serve -d:bin/$(CONFIG)/AppBundle $(OPEN_BROWSER) -p:8000; \ + $(DOTNET) serve -d:bin/$(CONFIG)/AppBundle $(CORS_HEADERS) $(OPEN_BROWSER) -p:8000; \ fi run-console: diff --git a/src/mono/wasm/build/WasmApp.InTree.props b/src/mono/wasm/build/WasmApp.InTree.props index 7f655768c8e73..76c089b8dd4b8 100644 --- a/src/mono/wasm/build/WasmApp.InTree.props +++ b/src/mono/wasm/build/WasmApp.InTree.props @@ -15,7 +15,7 @@ <_MonoRuntimeComponentDontLink Include="libmono-component-debugger-stub-static.a" /> - <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" /> + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" Condition="'$(FeatureWasmPerfTracing)' != 'true'"/> <_MonoRuntimeComponentDontLink Include="libmono-component-hot_reload-stub-static.a" /> diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 501cb7e156808..b92320d735bef 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -208,6 +208,7 @@ <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> <_EmccCFlags Include="@(_EmccCommonFlags)" /> + <_EmccCFlags Include="-DDISABLE_PERFTRACING_LISTEN_PORTS=1" /> <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(_WasmShouldAOT)' == 'true'" /> <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(_WasmShouldAOT)' == 'true'" /> <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> @@ -436,6 +437,7 @@ + diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 070195319d6b5..cf8b18508cd7a 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -260,6 +260,7 @@ icudt.dat <_HasDotnetWasm Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.wasm'">true + <_HasDotnetJsWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.worker.js'">true <_HasDotnetJsSymbols Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js.symbols'">true <_HasDotnetJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">true @@ -268,6 +269,7 @@ + diff --git a/src/mono/wasm/runtime/CMakeLists.txt b/src/mono/wasm/runtime/CMakeLists.txt index dfd9e0d8c01dd..dac8d63e719d3 100644 --- a/src/mono/wasm/runtime/CMakeLists.txt +++ b/src/mono/wasm/runtime/CMakeLists.txt @@ -2,10 +2,13 @@ cmake_minimum_required(VERSION 3.14.5) project(mono-wasm-runtime C) +option(DISABLE_THREADS "defined if the build does NOT support multithreading" ON) +option(DISABLE_WASM_USER_THREADS "defined if the build does not allow user threads to be created in a multithreaded build" OFF) + set(CMAKE_EXECUTABLE_SUFFIX ".js") add_executable(dotnet corebindings.c driver.c pinvoke.c) -target_include_directories(dotnet PUBLIC ${MONO_INCLUDES} ${MONO_OBJ_INCLUDES}) +target_include_directories(dotnet PUBLIC ${MONO_INCLUDES} ${MONO_OBJ_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}/include/wasm) target_compile_options(dotnet PUBLIC @${NATIVE_BIN_DIR}/src/emcc-default.rsp @${NATIVE_BIN_DIR}/src/emcc-compile.rsp -DCORE_BINDINGS -DGEN_PINVOKE=1) set_target_properties(dotnet PROPERTIES COMPILE_FLAGS ${CONFIGURATION_EMCC_FLAGS}) @@ -33,3 +36,5 @@ set_target_properties(dotnet PROPERTIES if(CMAKE_BUILD_TYPE STREQUAL "Release") add_custom_command(TARGET dotnet POST_BUILD COMMAND ${EMSDK_PATH}/upstream/bin/wasm-opt --enable-exception-handling --strip-dwarf ${NATIVE_BIN_DIR}/dotnet.wasm -o ${NATIVE_BIN_DIR}/dotnet.wasm) endif() + +configure_file(wasm-config.h.in include/wasm/wasm-config.h) diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js index 9326071c42e0a..daa0782d477ad 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js @@ -1,6 +1,7 @@ var require = require || undefined; // if loaded into global namespace and configured with global Module, we will self start in compatibility mode -let ENVIRONMENT_IS_GLOBAL = typeof globalThis.Module === "object" && globalThis.__dotnet_runtime === __dotnet_runtime; +const __isWorker = typeof globalThis.importScripts === "function"; +let ENVIRONMENT_IS_GLOBAL = !__isWorker && (typeof globalThis.Module === "object" && globalThis.__dotnet_runtime === __dotnet_runtime); if (ENVIRONMENT_IS_GLOBAL) { createDotnetRuntime(() => { return globalThis.Module; }).then((exports) => exports); } diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c index 8cc2ff15a1ed4..8d6d29cd94768 100644 --- a/src/mono/wasm/runtime/corebindings.c +++ b/src/mono/wasm/runtime/corebindings.c @@ -13,6 +13,7 @@ #include #include +#include "wasm-config.h" #include "gc-common.h" //JS funcs diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index 3e5083662d860..0a4a8d48e8e7f 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -382,6 +382,8 @@ declare const BINDING: { * @deprecated Renamed to conv_string_root */ conv_string_rooted: typeof conv_string_root; + mono_obj_array_new_ref: (size: number, result: MonoObjectRef) => void; + mono_obj_array_set_ref: (array: MonoObjectRef, idx: number, obj: MonoObjectRef) => void; js_string_to_mono_string_root: typeof js_string_to_mono_string_root; js_typed_array_to_array_root: typeof js_typed_array_to_array_root; js_to_mono_obj_root: typeof js_to_mono_obj_root; diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index 2007d25bc22ae..3a5977ee2c965 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -28,6 +28,7 @@ #include #include +#include "wasm-config.h" #include "pinvoke.h" #include "gc-common.h" @@ -60,6 +61,8 @@ void mono_free (void*); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); +static void mono_wasm_init_finalizer_thread (void); + #define MARSHAL_TYPE_NULL 0 #define MARSHAL_TYPE_INT 1 #define MARSHAL_TYPE_FP64 2 @@ -464,6 +467,12 @@ mono_wasm_load_runtime (const char *unused, int debug_level) mono_wasm_link_icu_shim (); #endif + // We should enable this as part of the wasm build later +#ifndef DISABLE_THREADS + monoeg_g_setenv ("MONO_THREADS_SUSPEND", "coop", 0); + monoeg_g_setenv ("MONO_SLEEP_ABORT_LIMIT", "250", 0); +#endif + #ifdef DEBUG // monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0); // monoeg_g_setenv ("MONO_LOG_MASK", "gc", 0); @@ -575,6 +584,10 @@ mono_wasm_load_runtime (const char *unused, int debug_level) mono_initialize_internals(); mono_thread_set_main (mono_thread_current ()); + + // TODO: we can probably delay starting the finalizer thread even longer - maybe from JS + // once we're done with loading and are about to begin running some managed code. + mono_wasm_init_finalizer_thread (); } EMSCRIPTEN_KEEPALIVE MonoAssembly* @@ -1203,6 +1216,7 @@ mono_wasm_exec_regression (int verbose_level, char *image) EMSCRIPTEN_KEEPALIVE int mono_wasm_exit (int exit_code) { + mono_jit_cleanup (root_domain); exit (exit_code); } @@ -1323,3 +1337,13 @@ mono_wasm_load_profiler_aot (const char *desc) } #endif + +static void +mono_wasm_init_finalizer_thread (void) +{ + // At this time we don't use a dedicated thread for finalization even if threading is enabled. + // Finalizers periodically run on the main thread +#if 0 + mono_gc_init_finalizer_thread (); +#endif +} diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index 37578887dded6..45124fc219aed 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -170,6 +170,7 @@ let exportedAPI: DotnetPublicAPI; // this is executed early during load of emscripten runtime // it exports methods to global objects MONO, BINDING and Module in backward compatible way +// At runtime this will be referred to as 'createDotnetRuntime' // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types function initializeImportsAndExports( imports: { isESM: boolean, isGlobal: boolean, isNode: boolean, isShell: boolean, isWeb: boolean, locateFile: Function, quit_: Function, ExitStatus: ExitStatusError, requirePromise: Promise }, @@ -293,6 +294,14 @@ function initializeImportsAndExports( configure_emscripten_startup(module, exportedAPI); + // HACK: Emscripten expects the return value of this function to always be the Module object, + // but we changed ours to return a set of exported namespaces. In order for the emscripten + // generated worker code to keep working, we detect that we're running in a worker (via the + // presence of globalThis.importScripts) and emulate the old behavior. Note that this will + // impact anyone trying to load us in a web worker directly, not just emscripten! + if (typeof ((globalThis)["importScripts"]) === "function") + return exportedAPI.Module; + return exportedAPI; } diff --git a/src/mono/wasm/runtime/pinvoke.c b/src/mono/wasm/runtime/pinvoke.c index 3804005cb5adb..eb42220482647 100644 --- a/src/mono/wasm/runtime/pinvoke.c +++ b/src/mono/wasm/runtime/pinvoke.c @@ -1,3 +1,4 @@ +#include "wasm-config.h" #include "pinvoke.h" #include diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 49427b3539c07..a8f3b7ce85b95 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -26,6 +26,29 @@ export const mono_wasm_runtime_is_initialized = new Promise((resolve, reject) => let ctx: DownloadAssetsContext | null = null; export function configure_emscripten_startup(module: DotnetModule, exportedAPI: DotnetPublicAPI): void { + // HACK: Emscripten expects us to provide it a fully qualified path where it can find + // our main script so that it can be loaded from inside of workers, because workers + // have their paths relative to the root instead of relative to our location + // In the browser we can create a hyperlink and set its href to a relative URL, + // and the browser will convert it into an absolute one for us + if ( + (typeof (globalThis.document) === "object") && + (typeof (globalThis.document.createElement) === "function") + ) { + // blazor injects a module preload link element for dotnet.[version].[sha].js + const blazorDotNetJS = Array.from (document.head.getElementsByTagName("link")).filter(elt => elt.rel !== undefined && elt.rel == "modulepreload" && elt.href !== undefined && elt.href.indexOf("dotnet") != -1 && elt.href.indexOf (".js") != -1); + if (blazorDotNetJS.length == 1) { + const hr = blazorDotNetJS[0].href; + console.log("determined url of main script to be " + hr); + (module)["mainScriptUrlOrBlob"] = hr; + } else { + const temp = globalThis.document.createElement("a"); + temp.href = "dotnet.js"; + console.log("determined url of main script to be " + temp.href); + (module)["mainScriptUrlOrBlob"] = temp.href; + } + } + // these could be overriden on DotnetModuleConfig if (!module.preInit) { module.preInit = []; @@ -715,4 +738,4 @@ export type DownloadAssetsContext = { resolved_promises: (MonoInitFetchResult | undefined)[] | null; loaded_files: { url?: string, file: string }[], loaded_assets: { [id: string]: [VoidPtr, number] }, -} \ No newline at end of file +} diff --git a/src/mono/wasm/runtime/wasm-config.h.in b/src/mono/wasm/runtime/wasm-config.h.in new file mode 100644 index 0000000000000..c5650ed200b45 --- /dev/null +++ b/src/mono/wasm/runtime/wasm-config.h.in @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +#ifndef __MONO_WASM_CONFIG_H__ +#define __MONO_WASM_CONFIG_H__ + +/* Support for threads is disabled */ +#cmakedefine DISABLE_THREADS + +/* Support for starting user threads is disabled */ +#cmakedefine DISABLE_WASM_USER_THREADS + +#endif/*__MONO_WASM_CONFIG_H__*/ diff --git a/src/mono/wasm/threads.md b/src/mono/wasm/threads.md new file mode 100644 index 0000000000000..afda117cb7459 --- /dev/null +++ b/src/mono/wasm/threads.md @@ -0,0 +1,80 @@ +# Threaded runtime # + +## Building ## + +Build with `/p:WasmEnableThreads=true` to enable support for multi-threading. + +Build with `/p:WasmEnablePerfTracing=true` to enable support for EventPipe diagnostics - this enabled threading, but only for "internal" utility threads. User code is not allowed to start threads. + +Do not combine these options, just turn on one or the other. + +## Libraries feature defines ## + +We use the `FeatureWasmThreads` property in the libraries projects to conditionally define +`FEATURE_WASM_THREADS` which is used to affect how the libraries are built for the multi-threaded +runtime. + +We use the `FeatureWasmPerfTracing` property in the libraries projects to +conditionally define `FEATURE_WASM_PERFTRACING` which is used to affect how the +libraries are built for a runtime that is single-threaded for users, but +internally can use multithreading for EventPipe diagnostics. + +### Ref asssemblies ### + +For ref assemblies that have APIs that are related to threading, we use +`[UnsupportedOSPlatform("browser")]` under a `FEATURE_WASM_THREADS` define to mark APIs that are not +supported with the single-threaded runtime. Each such ref assembly (for example +`System.Threading.Thread`) is defined in two places: `src/libraries/System.Threading.Thread/ref` for +the single-threaded ref assemblies, and +`src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/` for the multi-threaded ref +assemblies. By default users compile against the single-threaded ref assemblies, but by adding a +`PackageReference` to `Microsoft.NET.WebAssembly.Threading`, they get the multi-threaded ref +assemblies. + +### Implementation assemblies ### + +The implementation (in `System.Private.CoreLib`) we check +`System.Threading.Thread.IsThreadStartSupported` or call +`System.Threading.Thread.ThrowIfNoThreadStart()` to guard code paths that depends on +multi-threading. The property is a boolean constant that will allow the IL trimmer or the +JIT/interpreter/AOT to drop the multi-threaded implementation in the single-threaded CoreLib. + +The implementation should not use `[UnsupportedOSPlatform("browser")]` + +**TODO** For `FeatureWasmPerfTracing`, the implementation should check *some +runtime contant* and throw PNSE if diagnostics are not enabled. + +## Native runtime preprocessor defines ## + +In `src/mono/mono` and `src/mono/wasm` `DISABLE_THREADS` is defined for single-threaded builds (same +as mono's existing `-DENABLE_MINIMAL=threads` option). In multi-threaded builds, `DISABLE_THREADS` +is _not_ defined. + +For `WasmEnablePerfTracing`, `DISABLE_THREADS` is undefined (ie threading is enabled), but starting +user threads is not supported and `DISABLE_WASM_USER_THREADS` is defined (ie there is a +`-DENABLE_MINIMAL=wasm-user-threads` option) + +Additionally, `__EMSCRIPTEN_THREADS__` is defined by emscripten if threading is enabled. + +## Browser thread, main thread ## + +When the app starts, emscripten can optionally run `main` on a new worker instead of on the browser thread. + +Mono does _not_ use this at this time. + +## Running work on other threads ## + +Emscripten provides an API to queue up callbacks to run on the main thread, or on a particular +worker thread. See +[`emscripten/threading.h`](https://github.com/emscripten-core/emscripten/blob/main/system/include/emscripten/threading.h). + +Mono exposes these functions as `mono_threads_wasm_async_run_in_main_thread`, etc in +`mono/utils/mono-threads-wasm.h`. + +## Background tasks ## + +The runtime has a number of tasks that are scheduled with `mono_threads_schedule_background_job` +(pumping the threadpool task queue, running GC finalizers, etc). + +The background tasks will run on the main thread. Calling `mono_threads_schedule_background_job` on +a worker thread will use `async_run_in_main_thread` to queue up work for the main thread. diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index e63583af4aa8c..c89caa7139db7 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -6,7 +6,16 @@ browser-wasm $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'native', '$(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture)')) - $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm', 'native', 'lib')) + + + + true + true + + + + $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm', 'native', 'lib')) + $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm-threads', 'native', 'lib')) false false emcc @@ -129,6 +138,9 @@ <_EmccLinkFlags Include="-s NO_EXIT_RUNTIME=1" /> <_EmccLinkFlags Include="-s FORCE_FILESYSTEM=1" /> <_EmccLinkFlags Include="-s EXPORTED_RUNTIME_METHODS="['FS','print','ccall','cwrap','setValue','getValue','UTF8ToString','UTF8ArrayToString','FS_createPath','FS_createDataFile','removeRunDependency','addRunDependency', 'FS_readFile']"" /> + <_EmccCommonFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-s USE_PTHREADS=1" /> + <_EmccLinkFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-Wno-pthreads-mem-growth" /> + <_EmccLinkFlags Condition="'$(MonoWasmThreads)' == 'true'" Include="-s PTHREAD_POOL_SIZE=2" /> <_EmccLinkFlags Include="-s EXPORTED_FUNCTIONS=$(_DefaultExportedFunctions)" Condition="'$(_DefaultExportedFunctions)' != ''" /> <_EmccLinkFlags Include="--source-map-base http://example.com" /> <_EmccLinkFlags Include="-s STRICT_JS=1" /> @@ -178,6 +190,7 @@ $(CMakeConfigurationEmccFlags) -O2 + $(CMakeConfigurationLinkFlags) -Wno-pthreads-mem-growth $(CMakeConfigurationLinkFlags) --emit-symbol-map -DEMSDK_PATH="$(EMSDK_PATH.TrimEnd('\/'))" @@ -191,6 +204,8 @@ $(CMakeBuildRuntimeConfigureCmd) -DICU_LIB_DIR="$(ICULibDir.TrimEnd('\/'))" $(CMakeBuildRuntimeConfigureCmd) -DMONO_ARTIFACTS_DIR="$(MonoArtifactsPath.TrimEnd('\/'))" $(CMakeBuildRuntimeConfigureCmd) -DNATIVE_BIN_DIR="$(NativeBinDir.TrimEnd('\/'))" + $(CMakeBuildRuntimeConfigureCmd) -DDISABLE_THREADS=1 + $(CMakeBuildRuntimeConfigureCmd) -DDISABLE_WASM_USER_THREADS=1 $(CMakeBuildRuntimeConfigureCmd) $(CMakeConfigurationEmsdkPath) call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && $(CMakeBuildRuntimeConfigureCmd) @@ -259,6 +274,11 @@ DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)" SkipUnchangedFiles="true" /> + +