Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ public static void CancelPromise(Task promise)
GCHandle? promiseGCHandle = promise.AsyncState as GCHandle?;
if (promiseGCHandle == null) throw new InvalidOperationException("Expected Task converted from JS Promise");

#if FEATURE_WASM_THREADS
// TODO JSObject.AssertThreadAffinity(promise);
// in order to remember the thread ID of the promise, we would have to allocate holder object for any Task,
// which would hold thread ID and the GCHandle
// that would be pretty expensive, so we don't do it for now
// the consequences are that calling CancelPromise on wrong thread would do nothing
// because there would not be any object on JS registered under the same GCHandle
// perhaps that's the point when we could throw an exception on JS side.
#endif
_CancelPromise((IntPtr)promiseGCHandle.Value);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int should

public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject jsObject)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif

JSObject? res = null;

lock (JSHostImplementation.s_csOwnedObjects)
Expand Down Expand Up @@ -126,6 +130,9 @@ public static void GetTaskSourceTaskRef(int tcsGCHandle, out object result)

public static void SetupJSContinuationRef(in Task _task, JSObject continuationObj)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
// HACK: Attempting to use the in-param will produce CS1628, so we make a temporary copy
// on the stack that can be captured by our local functions below
var task = _task;
Expand Down Expand Up @@ -207,6 +214,9 @@ public static void CreateDateTimeRef(double ticks, out object result)
[Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "Done on purpose, see comment above.")]
public static void CreateUriRef(string uri, out object? result)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
if (uriType == null)
{
// StringBuilder to confuse ILLink, which is too smart otherwise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, i
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span<JSMarshalerArgument> arguments)
{
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(jsFunction);
#endif

IntPtr functionJSHandle = jsFunction.JSHandle;
fixed (JSMarshalerArgument* ptr = arguments)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;

namespace System.Runtime.InteropServices.JavaScript
{
public partial class JSObject
{
internal nint JSHandle;

#if FEATURE_WASM_THREADS
// the JavaScript object could only exist on the single web worker and can't migrate to other workers
internal int OwnerThreadId;
#endif
#if ENABLE_LEGACY_JS_INTEROP
internal GCHandle? InFlight;
internal int InFlightCounter;
Expand All @@ -19,6 +24,9 @@ public partial class JSObject
internal JSObject(IntPtr jsHandle)
{
JSHandle = jsHandle;
#if FEATURE_WASM_THREADS
OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
#endif
}

#if ENABLE_LEGACY_JS_INTEROP
Expand Down Expand Up @@ -56,6 +64,30 @@ internal void ReleaseInFlight()
}
#endif

#if FEATURE_WASM_THREADS
internal static void AssertThreadAffinity(object value)
{
if (value == null)
{
return;
}
if (value is JSObject jsObject)
{
if (jsObject.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
{
throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created.");
}
}
if (value is JSException jsException)
{
if(jsException.jsException!=null && jsException.jsException.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
{
throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created.");
}
}
}
#endif

/// <inheritdoc />
public override bool Equals([NotNullWhen(true)] object? obj) => obj is JSObject other && JSHandle == other.JSHandle;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public class Array : JSObject
public Array(params object[] _params)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(Array), _params))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public class ArrayBuffer : JSObject
public ArrayBuffer(int length)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(ArrayBuffer), new object[] { length }))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public class DataView : JSObject
public DataView(ArrayBuffer buffer)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer }))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand All @@ -28,6 +31,9 @@ public DataView(ArrayBuffer buffer)
public DataView(ArrayBuffer buffer, int byteOffset)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset }))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand All @@ -40,6 +46,9 @@ public DataView(ArrayBuffer buffer, int byteOffset)
public DataView(ArrayBuffer buffer, int byteOffset, int byteLength)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset, byteLength }))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public class Function : JSObject
public Function(params object[] args)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(Function), args))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using System.Threading;

namespace System.Runtime.InteropServices.JavaScript
{
Expand Down Expand Up @@ -220,5 +221,15 @@ public enum MappedType
Function = 4,
Uint8Array = 11,
}

#if FEATURE_WASM_THREADS
public static void ThrowIfLegacyWorkerThread()
{
if (Thread.CurrentThread.ManagedThreadId != 1)
{
throw new PlatformNotSupportedException("Legacy interop in not supported with WebAssembly threads."+Thread.CurrentThread.ManagedThreadId);
}
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public static object GetGlobalObject(string str)
/// </returns>
public static object Invoke(this JSObject self, string method, params object?[] args)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
ArgumentNullException.ThrowIfNull(self);
ObjectDisposedException.ThrowIf(self.IsDisposed, self);
Interop.Runtime.InvokeJSWithArgsRef(self.JSHandle, method, args, out int exception, out object res);
Expand Down Expand Up @@ -68,6 +71,9 @@ public static object Invoke(this JSObject self, string method, params object?[]
/// </returns>
public static object GetObjectProperty(this JSObject self, string name)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
ArgumentNullException.ThrowIfNull(self);
ObjectDisposedException.ThrowIf(self.IsDisposed, self);

Expand All @@ -92,6 +98,9 @@ public static object GetObjectProperty(this JSObject self, string name)
/// <param name="hasOwnProperty"></param>
public static void SetObjectProperty(this JSObject self, string name, object? value, bool createIfNotExists = true, bool hasOwnProperty = false)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
ArgumentNullException.ThrowIfNull(self);
ObjectDisposedException.ThrowIf(self.IsDisposed, self);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ public sealed class Uint8Array : JSObject
public Uint8Array(int length)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { length }))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

public Uint8Array(ArrayBuffer buffer)
: base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { buffer }))
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
LegacyHostImplementation.RegisterCSOwnedObject(this);
}

Expand Down Expand Up @@ -49,6 +55,9 @@ public byte[] ToArray()

public static unsafe Uint8Array From(ReadOnlySpan<byte> span)
{
#if FEATURE_WASM_THREADS
LegacyHostImplementation.ThrowIfLegacyWorkerThread();
#endif
// source has to be instantiated.
if (span == null)
{
Expand All @@ -65,7 +74,6 @@ public static unsafe Uint8Array From(ReadOnlySpan<byte> span)
r.ReleaseInFlight();
return r;
}

}

public enum TypedArrayTypeCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public unsafe void ToJS(Exception? value)
var jse = cpy as JSException;
if (jse != null && jse.jsException != null)
{
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(value);
#endif
// this is JSException roundtrip
ObjectDisposedException.ThrowIf(jse.jsException.IsDisposed, value);
slot.Type = MarshalerType.JSException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public void ToJS(JSObject? value)
}
else
{
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(value);
#endif
ObjectDisposedException.ThrowIf(value.IsDisposed, value);
slot.Type = MarshalerType.JSObject;
slot.JSHandle = value.JSHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ public void ToJS(Task? value)

void Complete()
{
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(promise);
#endif

// When this task was never resolved/rejected
// promise (held by this lambda) would be collected by GC after the Task is collected
// and would also allow the JS promise to be collected
Expand Down Expand Up @@ -304,6 +308,9 @@ public void ToJS<T>(Task<T>? value, ArgumentToJSCallback<T> marshaler)

void Complete()
{
#if FEATURE_WASM_THREADS
JSObject.AssertThreadAffinity(promise);
#endif
// When this task was never resolved/rejected
// promise (held by this lambda) would be collected by GC after the Task is collected
// and would also allow the JS promise to be collected
Expand Down
6 changes: 5 additions & 1 deletion src/mono/wasm/runtime/net6-legacy/method-binding.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import MonoWasmThreads from "consts:monoWasmThreads";
import { legacy_c_functions as cwraps } from "../cwraps";
import { Module } from "../imports";
import { ENVIRONMENT_IS_PTHREAD, Module } from "../imports";
import { parseFQN } from "../invoke-cs";
import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory";
import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots";
Expand Down Expand Up @@ -382,6 +383,9 @@ export function _decide_if_result_is_marshaled(converter: Converter, argc: numbe


export function mono_bind_method(method: MonoMethod, args_marshal: string/*ArgsMarshalString*/, has_this_arg: boolean, friendly_name?: string): Function {
if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) {
throw new Error("Legacy interop in not supported with WebAssembly threads.");
}
if (typeof (args_marshal) !== "string")
throw new Error("args_marshal argument invalid, expected string");

Expand Down
9 changes: 8 additions & 1 deletion src/mono/wasm/runtime/net6-legacy/method-calls.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import MonoWasmThreads from "consts:monoWasmThreads";
import { get_js_obj, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles";
import { Module, runtimeHelpers, INTERNAL } from "../imports";
import { Module, runtimeHelpers, INTERNAL, ENVIRONMENT_IS_PTHREAD } from "../imports";
import { wrap_error_root, wrap_no_error_root } from "../invoke-js";
import { _release_temp_frame } from "../memory";
import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots";
Expand Down Expand Up @@ -90,6 +91,9 @@ export function mono_call_assembly_entry_point(assembly: string, args?: any[], s
}

export function mono_wasm_invoke_js_with_args_ref(js_handle: JSHandle, method_name: MonoStringRef, args: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): any {
if (MonoWasmThreads && ENVIRONMENT_IS_PTHREAD) {
throw new Error("Legacy interop in not supported with WebAssembly threads.");
}
const argsRoot = mono_wasm_new_external_root<MonoArray>(args),
nameRoot = mono_wasm_new_external_root<MonoString>(method_name),
resultRoot = mono_wasm_new_external_root<MonoObject>(result_address);
Expand Down Expand Up @@ -282,6 +286,9 @@ export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_e
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo: any, arg0: any, arg1: any, arg2: any): void | number {
try {
if (MonoWasmThreads) {
throw new Error("Legacy interop in not supported with WebAssembly threads.");
}
const blazorExports = (<any>globalThis).Blazor;
if (!blazorExports) {
throw new Error("The blazor.webassembly.js library is not loaded.");
Expand Down