Skip to content

Commit

Permalink
[browser] streamline Task/Promise marshalling (#93010)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara authored Oct 16, 2023
1 parent ee9dd81 commit 018efc5
Show file tree
Hide file tree
Showing 35 changed files with 613 additions and 599 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal static unsafe partial class Runtime
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern unsafe void BindCSFunction(in string fully_qualified_name, int signature_hash, void* signature, out int is_exception, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void MarshalPromise(void* data);
public static extern void ResolveOrRejectPromise(void* data);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern IntPtr RegisterGCRoot(IntPtr start, int bytesSize, IntPtr name);
[MethodImpl(MethodImplOptions.InternalCall)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{F2C2C78A-CED
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{569E6837-0771-4C08-BB09-460281030538}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{569E6837-0771-4C08-BB09-460281030538}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F92020A9-28BE-4398-86FF-5CFE44C94882}"
EndProject
Expand Down Expand Up @@ -135,28 +135,32 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
{BFED925C-18F2-4C98-833E-66F205234598} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{ABA5A92B-CAD8-47E8-A7CE-D28A67FB69C0} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{765B4AA5-723A-44FF-BC4E-EB0F03103F6D} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
{FB12C247-AFEF-4772-BB0C-983969B6CF32} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
{09AA6758-0BD3-4312-9C07-AE9F1D50A3AD} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
{B4E3E774-2C16-4CBF-87EF-88C547529B94} = {F2C2C78A-CEDD-4DE0-9C3A-0195F00E0B4E}
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {7973EAA3-43B6-4D78-B24C-38BA6BC0D1E3}
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {C22C479B-769A-4859-B974-E9B9D65918DE}
{4D8B7538-D933-4F3A-818D-4E19ABA7E182} = {569E6837-0771-4C08-BB09-460281030538}
{6C60944F-4FE1-450F-884B-D523EDFCFAB3} = {569E6837-0771-4C08-BB09-460281030538}
{569E6837-0771-4C08-BB09-460281030538} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B} = {67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}
{42F9A600-BEC3-4F87-97EE-38E0DCAABC5A} = {67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}
{67F3A00A-AE6C-434C-927D-E5D38DE2DA2C} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{008873D5-9028-4FF3-8354-71F713748625} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{569E6837-0771-4C08-BB09-460281030538} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{67F3A00A-AE6C-434C-927D-E5D38DE2DA2C} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3FE64246-4AFA-424A-AE5D-7007E20451B5}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{42f9a600-bec3-4f87-97ee-38e0dcaabc5a}*SharedItemsImports = 5
..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{6c60944f-4fe1-450f-884b-d523edfcfab3}*SharedItemsImports = 5
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@
<data name="NullToManagedCallback" xml:space="preserve">
<value>ToManagedCallback is null.</value>
</data>
<data name="NullTaskCallback" xml:space="preserve">
<value>TaskCallback is null.</value>
<data name="NullPromiseHolder" xml:space="preserve">
<value>PromiseHolder is null.</value>
</data>
<data name="EmptyProfileData" xml:space="preserve">
<value>Empty profile data.</value>
Expand All @@ -162,8 +162,8 @@
<data name="FailedToMarshalException" xml:space="preserve">
<value>Failed to marshal exception.</value>
</data>
<data name="FailedToMarshalTaskCallback" xml:space="preserve">
<value>Failed to marshal Task callback.</value>
<data name="FailedToMarshalPromiseHolder" xml:space="preserve">
<value>Failed to marshal Promise callback.</value>
</data>
<data name="InvalidInFlightCounter" xml:space="preserve">
<value>Invalid InFlightCounter for JSObject {0}, expected: {1}, actual: {2}.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace System.Runtime.InteropServices.JavaScript
public static partial class CancelablePromise
{
[JSImport("INTERNAL.mono_wasm_cancel_promise")]
private static partial void _CancelPromise(IntPtr promiseGCHandle);
private static partial void _CancelPromise(IntPtr gcvHandle);

public static void CancelPromise(Task promise)
{
Expand All @@ -18,15 +18,15 @@ public static void CancelPromise(Task promise)
{
return;
}
JSHostImplementation.TaskCallback? holder = promise.AsyncState as JSHostImplementation.TaskCallback;
JSHostImplementation.PromiseHolder? holder = promise.AsyncState as JSHostImplementation.PromiseHolder;
if (holder == null) throw new InvalidOperationException("Expected Task converted from JS Promise");


#if FEATURE_WASM_THREADS
holder.SynchronizationContext!.Send(static (JSHostImplementation.TaskCallback holder) =>
holder.SynchronizationContext!.Send(static (JSHostImplementation.PromiseHolder holder) =>
{
#endif
_CancelPromise(holder.GCHandle);
_CancelPromise(holder.GCVHandle);
#if FEATURE_WASM_THREADS
}, holder);
#endif
Expand All @@ -39,15 +39,15 @@ public static void CancelPromise<T>(Task promise, Action<T> callback, T state)
{
return;
}
JSHostImplementation.TaskCallback? holder = promise.AsyncState as JSHostImplementation.TaskCallback;
JSHostImplementation.PromiseHolder? holder = promise.AsyncState as JSHostImplementation.PromiseHolder;
if (holder == null) throw new InvalidOperationException("Expected Task converted from JS Promise");


#if FEATURE_WASM_THREADS
holder.SynchronizationContext!.Send((JSHostImplementation.TaskCallback holder) =>
holder.SynchronizationContext!.Send((JSHostImplementation.PromiseHolder holder) =>
{
#endif
_CancelPromise(holder.GCHandle);
_CancelPromise(holder.GCVHandle);
callback.Invoke(state);
#if FEATURE_WASM_THREADS
}, holder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading.Tasks;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using static System.Runtime.InteropServices.JavaScript.JSHostImplementation;

namespace System.Runtime.InteropServices.JavaScript
{
Expand All @@ -31,7 +32,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
throw new MissingMethodException(SR.MissingManagedEntrypointHandle);
}

RuntimeMethodHandle methodHandle = JSHostImplementation.GetMethodHandleFromIntPtr(entrypointPtr);
RuntimeMethodHandle methodHandle = GetMethodHandleFromIntPtr(entrypointPtr);
// this would not work for generic types. But Main() could not be generic, so we are fine.
MethodInfo? method = MethodBase.GetMethodFromHandle(methodHandle) as MethodInfo;
if (method == null)
Expand Down Expand Up @@ -141,32 +142,18 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller
try
{
GCHandle handle = (GCHandle)arg_1.slot.GCHandle;

JSHostImplementation.ThreadJsOwnedObjects.Remove(handle.Target!);
handle.Free();
}
catch (Exception ex)
{
arg_exc.ToJS(ex);
}
}

// the marshaled signature is:
// GCHandle CreateTaskCallback()
public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value
try
{
JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback();
#if FEATURE_WASM_THREADS
holder.OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
holder.SynchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
#endif
arg_return.slot.Type = MarshalerType.Object;
arg_return.slot.GCHandle = holder.GCHandle = JSHostImplementation.GetJSOwnedObjectGCHandle(holder);
var gcHandle = arg_1.slot.GCHandle;
if (IsGCVHandle(gcHandle) && ThreadJsOwnedHolders.Remove(gcHandle, out PromiseHolder? holder))
{
holder.GCVHandle = IntPtr.Zero;
holder.Callback!(null);
}
else
{
GCHandle handle = (GCHandle)gcHandle;
ThreadJsOwnedObjects.Remove(handle.Target!);
handle.Free();
}
}
catch (Exception ex)
{
Expand All @@ -187,7 +174,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
try
{
GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle;
if (callback_gc_handle.Target is JSHostImplementation.ToManagedCallback callback)
if (callback_gc_handle.Target is ToManagedCallback callback)
{
// arg_2, arg_3, arg_4, arg_res are processed by the callback
callback(arguments_buffer);
Expand All @@ -204,7 +191,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
}

// the marshaled signature is:
// void CompleteTask<T>(GCHandle holder, Exception? exceptionResult, T? result)
// void CompleteTask<T>(GCVHandle holder, Exception? exceptionResult, T? result)
public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
Expand All @@ -213,15 +200,17 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
// arg_3 set by caller when this is SetResult call
try
{
GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle;
if (callback_gc_handle.Target is JSHostImplementation.TaskCallback holder && holder.Callback is not null)
var callback_gcv_handle = arg_1.slot.GCHandle;
if (ThreadJsOwnedHolders.Remove(callback_gcv_handle, out PromiseHolder? promiseHolder) && promiseHolder.Callback != null)
{
promiseHolder.GCVHandle = IntPtr.Zero;

// arg_2, arg_3 are processed by the callback
holder.Callback(arguments_buffer);
promiseHolder.Callback(arguments_buffer);
}
else
{
throw new InvalidOperationException(SR.NullTaskCallback);
throw new InvalidOperationException(SR.NullPromiseHolder);
}
}
catch (Exception ex)
Expand Down Expand Up @@ -264,7 +253,7 @@ public static void InstallSynchronizationContext (JSMarshalerArgument* arguments
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
try
{
JSHostImplementation.InstallWebWorkerInterop(true, true);
InstallWebWorkerInterop(true, true);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace System.Runtime.InteropServices.JavaScript
{
internal static unsafe partial class JavaScriptImports
{
public static void MarshalPromise(Span<JSMarshalerArgument> arguments)
public static void ResolveOrRejectPromise(Span<JSMarshalerArgument> arguments)
{
fixed (JSMarshalerArgument* ptr = arguments)
{
Interop.Runtime.MarshalPromise(ptr);
Interop.Runtime.ResolveOrRejectPromise(ptr);
ref JSMarshalerArgument exceptionArg = ref arguments[0];
if (exceptionArg.slot.Type != MarshalerType.None)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal static void PreventTrimming()

public static void GetCSOwnedObjectByJSHandleRef(nint jsHandle, int shouldAddInflight, out JSObject? result)
{
if (JSHostImplementation.ThreadCsOwnedObjects.TryGetValue((int)jsHandle, out WeakReference<JSObject>? reference))
if (JSHostImplementation.ThreadCsOwnedObjects.TryGetValue(jsHandle, out WeakReference<JSObject>? reference))
{
reference.TryGetTarget(out JSObject? jsObject);
if (shouldAddInflight != 0)
Expand Down Expand Up @@ -74,7 +74,7 @@ public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation

JSObject? res = null;

if (!JSHostImplementation.ThreadCsOwnedObjects.TryGetValue((int)jsHandle, out WeakReference<JSObject>? reference) ||
if (!JSHostImplementation.ThreadCsOwnedObjects.TryGetValue(jsHandle, out WeakReference<JSObject>? reference) ||
!reference.TryGetTarget(out res) ||
res.IsDisposed)
{
Expand All @@ -90,7 +90,7 @@ public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation
_ => throw new ArgumentOutOfRangeException(nameof(mappedType))
};
#pragma warning restore CS0612 // Type or member is obsolete
JSHostImplementation.ThreadCsOwnedObjects[(int)jsHandle] = new WeakReference<JSObject>(res, trackResurrection: true);
JSHostImplementation.ThreadCsOwnedObjects[jsHandle] = new WeakReference<JSObject>(res, trackResurrection: true);
}
if (shouldAddInflight != 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,28 @@ internal struct JSBindingHeader
internal struct JSBindingType
{
internal MarshalerType Type;
internal MarshalerType __ReservedB1;
internal MarshalerType __ReservedB2;
internal MarshalerType __ReservedB3;
internal IntPtr __Reserved;
internal IntPtr JSCustomMarshallerCode;
internal int JSCustomMarshallerCodeLength;
internal MarshalerType ResultMarshalerType;
internal MarshalerType __ReservedB4;
internal MarshalerType __ReservedB5;
internal MarshalerType __ReservedB6;
internal MarshalerType Arg1MarshalerType;
internal MarshalerType __ReservedB7;
internal MarshalerType __ReservedB8;
internal MarshalerType __ReservedB9;
internal MarshalerType Arg2MarshalerType;
internal MarshalerType __ReservedB10;
internal MarshalerType __ReservedB11;
internal MarshalerType __ReservedB12;
internal MarshalerType Arg3MarshalerType;
internal MarshalerType __ReservedB13;
internal MarshalerType __ReservedB14;
internal MarshalerType __ReservedB15;
}

internal unsafe int ArgumentCount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading;
using System.Threading.Tasks;

namespace System.Runtime.InteropServices.JavaScript
{
internal static partial class JSHostImplementation
{
internal unsafe delegate void ToManagedCallback(JSMarshalerArgument* arguments_buffer);

public sealed class TaskCallback
public sealed class PromiseHolder
{
public nint GCHandle;
public nint GCVHandle;
public ToManagedCallback? Callback;
#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;
internal SynchronizationContext? SynchronizationContext;
#endif

public PromiseHolder(nint gcvHandle)
{
this.GCVHandle = gcvHandle;
#if FEATURE_WASM_THREADS
this.OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
this.SynchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
#endif
}
}

[StructLayout(LayoutKind.Explicit)]
Expand Down
Loading

0 comments on commit 018efc5

Please sign in to comment.