Skip to content

Commit 3bae822

Browse files
authored
[browser][MT] mono_wasm_schedule_synchronization_context (#100251)
1 parent 8763f6d commit 3bae822

File tree

15 files changed

+79
-112
lines changed

15 files changed

+79
-112
lines changed

src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal static unsafe partial class Runtime
4242

4343
#if FEATURE_WASM_MANAGED_THREADS
4444
[MethodImpl(MethodImplOptions.InternalCall)]
45-
public static extern void InstallWebWorkerInterop(nint proxyContextGCHandle, void* beforeSyncJSImport, void* afterSyncJSImport);
45+
public static extern void InstallWebWorkerInterop(nint proxyContextGCHandle, void* beforeSyncJSImport, void* afterSyncJSImport, void* pumpHandler);
4646
[MethodImpl(MethodImplOptions.InternalCall)]
4747
public static extern void UninstallWebWorkerInterop();
4848

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public static unsafe JSSynchronizationContext InstallWebWorkerInterop(bool isMai
6868

6969
Interop.Runtime.InstallWebWorkerInterop(proxyContext.ContextHandle,
7070
(delegate* unmanaged[Cdecl]<JSMarshalerArgument*, void>)&JavaScriptExports.BeforeSyncJSExport,
71-
(delegate* unmanaged[Cdecl]<JSMarshalerArgument*, void>)&JavaScriptExports.AfterSyncJSExport);
71+
(delegate* unmanaged[Cdecl]<JSMarshalerArgument*, void>)&JavaScriptExports.AfterSyncJSExport,
72+
(delegate* unmanaged[Cdecl]<void>)&PumpHandler);
7273

7374
return ctx;
7475
}
@@ -170,7 +171,7 @@ private unsafe void ScheduleJSPump()
170171
{
171172
// While we COULD pump here, we don't want to. We want the pump to happen on the next event loop turn.
172173
// Otherwise we could get a chain where a pump generates a new work item and that makes us pump again, forever.
173-
TargetThreadScheduleBackgroundJob(ProxyContext.NativeTID, (delegate* unmanaged[Cdecl]<void>)&BackgroundJobHandler);
174+
ScheduleSynchronizationContext(ProxyContext.NativeTID);
174175
}
175176

176177
public override void Post(SendOrPostCallback d, object? state)
@@ -236,13 +237,13 @@ public override void Send(SendOrPostCallback d, object? state)
236237
}
237238

238239
[MethodImplAttribute(MethodImplOptions.InternalCall)]
239-
internal static extern unsafe void TargetThreadScheduleBackgroundJob(IntPtr targetTID, void* callback);
240+
internal static extern unsafe void ScheduleSynchronizationContext(IntPtr targetTID);
240241

241242
#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
242243
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
243244
#pragma warning restore CS3016
244245
// this callback will arrive on the target thread, called from mono_background_exec
245-
private static void BackgroundJobHandler()
246+
private static void PumpHandler()
246247
{
247248
var ctx = JSProxyContext.AssertIsInteropThread();
248249
ctx.SynchronizationContext.Pump();
@@ -286,7 +287,7 @@ private void Pump()
286287
}
287288
catch (Exception e)
288289
{
289-
Environment.FailFast($"JSSynchronizationContext.BackgroundJobHandler failed, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {e.StackTrace}");
290+
Environment.FailFast($"JSSynchronizationContext.Pump failed, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {e.StackTrace}");
290291
}
291292
}
292293

src/mono/browser/runtime/corebindings.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void mono_wasm_resolve_or_reject_promise_post (pthread_t target_tid, void *args)
4242
void mono_wasm_cancel_promise_post (pthread_t target_tid, int task_holder_gc_handle);
4343

4444
extern void mono_wasm_install_js_worker_interop (int context_gc_handle);
45-
void mono_wasm_install_js_worker_interop_wrapper (int context_gc_handle, void* beforeSyncJSImport, void* afterSyncJSImport);
45+
void mono_wasm_install_js_worker_interop_wrapper (int context_gc_handle, void* beforeSyncJSImport, void* afterSyncJSImport, void* pumpHandler);
4646
extern void mono_wasm_uninstall_js_worker_interop ();
4747
extern void mono_wasm_invoke_jsimport_MT (void* signature, void* args);
4848
void mono_wasm_invoke_jsimport_async_post (pthread_t target_tid, void* signature, void* args);
@@ -258,11 +258,13 @@ void mono_wasm_get_assembly_export (char *assembly_name, char *namespace, char *
258258

259259
void* before_sync_js_import;
260260
void* after_sync_js_import;
261+
void* synchronization_context_pump_handler;
261262

262-
void mono_wasm_install_js_worker_interop_wrapper (int context_gc_handle, void* beforeSyncJSImport, void* afterSyncJSImport)
263+
void mono_wasm_install_js_worker_interop_wrapper (int context_gc_handle, void* beforeSyncJSImport, void* afterSyncJSImport, void* pumpHandler)
263264
{
264265
before_sync_js_import = beforeSyncJSImport;
265266
after_sync_js_import = afterSyncJSImport;
267+
synchronization_context_pump_handler = pumpHandler;
266268
mono_wasm_install_js_worker_interop (context_gc_handle);
267269
}
268270

src/mono/browser/runtime/cwraps.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const threading_cwraps: SigLine[] = WasmEnableThreads ? [
3333
[true, "mono_wasm_register_ui_thread", "void", []],
3434
[true, "mono_wasm_register_io_thread", "void", []],
3535
[true, "mono_wasm_print_thread_dump", "void", []],
36+
[true, "mono_wasm_synchronization_context_pump", "void", []],
3637
[true, "mono_threads_wasm_sync_run_in_target_thread_done", "void", ["number"]],
3738
] : [];
3839

@@ -157,6 +158,7 @@ export interface t_ThreadingCwraps {
157158
mono_wasm_register_ui_thread(): void;
158159
mono_wasm_register_io_thread(): void;
159160
mono_wasm_print_thread_dump(): void;
161+
mono_wasm_synchronization_context_pump(): void;
160162
mono_threads_wasm_sync_run_in_target_thread_done(sem: VoidPtr): void;
161163
}
162164

src/mono/browser/runtime/driver.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,10 @@ mono_wasm_invoke_jsexport_async_post (void* target_thread, MonoMethod *method, v
287287

288288

289289
typedef void (*js_interop_event)(void* args);
290+
typedef void (*sync_context_pump)(void);
290291
extern js_interop_event before_sync_js_import;
291292
extern js_interop_event after_sync_js_import;
293+
extern sync_context_pump synchronization_context_pump_handler;
292294

293295
// this is running on the target thread
294296
EMSCRIPTEN_KEEPALIVE void
@@ -306,6 +308,11 @@ mono_wasm_invoke_jsexport_sync_send (void* target_thread, MonoMethod *method, vo
306308
mono_threads_wasm_sync_run_in_target_thread_vii (target_thread, (void (*)(gpointer, gpointer))mono_wasm_invoke_jsexport_sync, method, args);
307309
}
308310

311+
EMSCRIPTEN_KEEPALIVE void mono_wasm_synchronization_context_pump (void)
312+
{
313+
synchronization_context_pump_handler ();
314+
}
315+
309316
#endif /* DISABLE_THREADS */
310317

311318
EMSCRIPTEN_KEEPALIVE void

src/mono/browser/runtime/exports-binding.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
mono_wasm_pthread_on_pthread_registered, mono_wasm_pthread_set_name, mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop, mono_wasm_start_io_thread_async
3333
} from "./pthreads";
3434
import { mono_wasm_dump_threads } from "./pthreads/ui-thread";
35+
import { mono_wasm_schedule_synchronization_context } from "./pthreads/shared";
3536

3637

3738
// the JS methods would be visible to EMCC linker and become imports of the WASM module
@@ -44,6 +45,7 @@ export const mono_wasm_threads_imports = !WasmEnableThreads ? [] : [
4445
mono_wasm_pthread_set_name,
4546
mono_wasm_start_deputy_thread_async,
4647
mono_wasm_start_io_thread_async,
48+
mono_wasm_schedule_synchronization_context,
4749

4850
// mono-threads.c
4951
mono_wasm_dump_threads,

src/mono/browser/runtime/pthreads/deputy-thread.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { monoThreadInfo, postMessageToMain, update_thread_info } from "./shared"
99
import { Module, loaderHelpers, runtimeHelpers } from "../globals";
1010
import { start_runtime } from "../startup";
1111
import { WorkerToMainMessageType } from "../types/internal";
12+
import { forceThreadMemoryViewRefresh } from "../memory";
1213

1314
export function mono_wasm_start_deputy_thread_async () {
1415
if (!WasmEnableThreads) return;
@@ -28,6 +29,7 @@ export function mono_wasm_start_deputy_thread_async () {
2829
Module.runtimeKeepalivePush();
2930
Module.safeSetTimeout(async () => {
3031
try {
32+
forceThreadMemoryViewRefresh();
3133

3234
await start_runtime();
3335

src/mono/browser/runtime/pthreads/shared.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { set_thread_prefix } from "../logging";
1111
import { bindings_init } from "../startup";
1212
import { forceDisposeProxies } from "../gc-handles";
1313
import { monoMessageSymbol, GCHandleNull, PThreadPtrNull, WorkerToMainMessageType } from "../types/internal";
14+
import { threads_c_functions as tcwraps } from "../cwraps";
15+
import { forceThreadMemoryViewRefresh } from "../memory";
1416

1517
// A duplicate in loader/assets.ts
1618
export const worker_empty_prefix = " - ";
@@ -105,6 +107,19 @@ export function update_thread_info (): void {
105107
}
106108
}
107109

110+
export function exec_synchronization_context_pump (): void {
111+
if (!loaderHelpers.is_runtime_running()) {
112+
return;
113+
}
114+
forceThreadMemoryViewRefresh();
115+
tcwraps.mono_wasm_synchronization_context_pump();
116+
}
117+
118+
export function mono_wasm_schedule_synchronization_context (): void {
119+
if (!WasmEnableThreads) return;
120+
Module.safeSetTimeout(exec_synchronization_context_pump, 0);
121+
}
122+
108123
export function mono_wasm_pthread_ptr (): PThreadPtr {
109124
if (!WasmEnableThreads) return PThreadPtrNull;
110125
return (<any>Module)["_pthread_self"]();

src/mono/browser/runtime/scheduling.ts

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
import WasmEnableThreads from "consts:wasmEnableThreads";
55

66
import cwraps from "./cwraps";
7-
import { ENVIRONMENT_IS_WORKER, Module, loaderHelpers } from "./globals";
7+
import { Module, loaderHelpers } from "./globals";
88
import { forceThreadMemoryViewRefresh } from "./memory";
9-
import { is_thread_available } from "./pthreads";
109

1110
let spread_timers_maximum = 0;
1211
let pump_count = 0;
1312

1413
export function prevent_timer_throttling (): void {
14+
if (WasmEnableThreads) return;
1515
if (!loaderHelpers.isChromium) {
1616
return;
1717
}
@@ -30,66 +30,46 @@ export function prevent_timer_throttling (): void {
3030
}
3131

3232
function prevent_timer_throttling_tick () {
33+
if (WasmEnableThreads) return;
3334
Module.maybeExit();
3435
if (!loaderHelpers.is_runtime_running()) {
3536
return;
3637
}
37-
if (WasmEnableThreads) {
38-
forceThreadMemoryViewRefresh();
39-
}
4038
cwraps.mono_wasm_execute_timer();
4139
pump_count++;
4240
mono_background_exec_until_done();
4341
}
4442

4543
function mono_background_exec_until_done () {
44+
if (WasmEnableThreads) return;
4645
Module.maybeExit();
4746
if (!loaderHelpers.is_runtime_running()) {
4847
return;
4948
}
50-
if (WasmEnableThreads) {
51-
forceThreadMemoryViewRefresh();
52-
}
5349
while (pump_count > 0) {
5450
--pump_count;
5551
cwraps.mono_background_exec();
5652
}
5753
}
5854

5955
export function schedule_background_exec (): void {
56+
if (WasmEnableThreads) return;
6057
++pump_count;
61-
let max_postpone_count = 10;
62-
function postpone_schedule_background () {
63-
if (max_postpone_count < 0 || is_thread_available()) {
64-
Module.safeSetTimeout(mono_background_exec_until_done, 0);
65-
} else {
66-
max_postpone_count--;
67-
Module.safeSetTimeout(postpone_schedule_background, 10);
68-
}
69-
}
70-
71-
if (WasmEnableThreads && !ENVIRONMENT_IS_WORKER) {
72-
// give threads chance to load before we run more synchronous code on UI thread
73-
postpone_schedule_background();
74-
} else {
75-
Module.safeSetTimeout(mono_background_exec_until_done, 0);
76-
}
58+
Module.safeSetTimeout(mono_background_exec_until_done, 0);
7759
}
7860

7961
let lastScheduledTimeoutId: any = undefined;
8062
export function mono_wasm_schedule_timer (shortestDueTimeMs: number): void {
63+
if (WasmEnableThreads) return;
8164
if (lastScheduledTimeoutId) {
8265
globalThis.clearTimeout(lastScheduledTimeoutId);
8366
lastScheduledTimeoutId = undefined;
84-
// NOTE: Multi-threaded Module.safeSetTimeout() does the runtimeKeepalivePush()
85-
// and non-Multi-threaded Module.safeSetTimeout does not runtimeKeepalivePush()
86-
// but clearTimeout does not runtimeKeepalivePop() so we need to do it here in MT only.
87-
if (WasmEnableThreads) Module.runtimeKeepalivePop();
8867
}
8968
lastScheduledTimeoutId = Module.safeSetTimeout(mono_wasm_schedule_timer_tick, shortestDueTimeMs);
9069
}
9170

9271
function mono_wasm_schedule_timer_tick () {
72+
if (WasmEnableThreads) return;
9373
Module.maybeExit();
9474
if (WasmEnableThreads) {
9575
forceThreadMemoryViewRefresh();

src/mono/mono/mini/mini-wasm.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -444,14 +444,17 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
444444

445445
//functions exported to be used by JS
446446
G_BEGIN_DECLS
447-
EMSCRIPTEN_KEEPALIVE void mono_wasm_execute_timer (void);
448447

449448
//JS functions imported that we use
449+
#ifdef DISABLE_THREADS
450+
EMSCRIPTEN_KEEPALIVE void mono_wasm_execute_timer (void);
451+
EMSCRIPTEN_KEEPALIVE void mono_background_exec (void);
450452
extern void mono_wasm_schedule_timer (int shortestDueTimeMs);
453+
#else
454+
extern void mono_target_thread_schedule_synchronization_context(MonoNativeThreadId target_thread);
455+
#endif // DISABLE_THREADS
451456
G_END_DECLS
452457

453-
void mono_background_exec (void);
454-
455458
#endif // HOST_BROWSER
456459

457460
gpointer
@@ -588,6 +591,8 @@ mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoThreadInfo
588591
return FALSE;
589592
}
590593

594+
#ifdef DISABLE_THREADS
595+
591596
// this points to System.Threading.TimerQueue.TimerHandler C# method
592597
static void *timer_handler;
593598

@@ -605,7 +610,6 @@ mono_wasm_execute_timer (void)
605610
MONO_EXIT_GC_UNSAFE;
606611
}
607612

608-
#ifdef DISABLE_THREADS
609613
void
610614
mono_wasm_main_thread_schedule_timer (void *timerHandler, int shortestDueTimeMs)
611615
{
@@ -626,7 +630,7 @@ mono_arch_register_icall (void)
626630
mono_add_internal_call_internal ("System.Threading.TimerQueue::MainThreadScheduleTimer", mono_wasm_main_thread_schedule_timer);
627631
mono_add_internal_call_internal ("System.Threading.ThreadPool::MainThreadScheduleBackgroundJob", mono_main_thread_schedule_background_job);
628632
#else
629-
mono_add_internal_call_internal ("System.Runtime.InteropServices.JavaScript.JSSynchronizationContext::TargetThreadScheduleBackgroundJob", mono_target_thread_schedule_background_job);
633+
mono_add_internal_call_internal ("System.Runtime.InteropServices.JavaScript.JSSynchronizationContext::ScheduleSynchronizationContext", mono_target_thread_schedule_synchronization_context);
630634
#endif /* DISABLE_THREADS */
631635
#endif /* HOST_BROWSER */
632636
}

src/mono/mono/mini/mini-wasm.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,18 @@ typedef struct {
100100
// sdks/wasm/driver.c is C and uses this
101101
G_EXTERN_C void mono_wasm_enable_debugging (int log_level);
102102

103+
#ifdef HOST_BROWSER
104+
105+
//JS functions imported that we use
103106
#ifdef DISABLE_THREADS
107+
void mono_wasm_execute_timer (void);
104108
void mono_wasm_main_thread_schedule_timer (void *timerHandler, int shortestDueTimeMs);
105109
#endif // DISABLE_THREADS
106110

107111
void mono_wasm_print_stack_trace (void);
112+
#endif // HOST_BROWSER
113+
114+
108115

109116
gboolean
110117
mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype);

0 commit comments

Comments
 (0)