Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara committed Nov 30, 2023
1 parent 563664e commit 78647a8
Show file tree
Hide file tree
Showing 24 changed files with 416 additions and 129 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34004.107
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections", "..\System.Collections\ref\System.Collections.csproj", "{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}"
Expand Down Expand Up @@ -43,11 +47,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{569E6837-077
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{32CDDDCD-5319-494C-AB41-42B87C467F04}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{32CDDDCD-5319-494C-AB41-42B87C467F04}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F92020A9-28BE-4398-86FF-5CFE44C94882}"
EndProject
Expand Down Expand Up @@ -135,28 +139,32 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {569E6837-0771-4C08-BB09-460281030538}
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}
{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} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {569E6837-0771-4C08-BB09-460281030538}
{FB12C247-AFEF-4772-BB0C-983969B6CF32} = {569E6837-0771-4C08-BB09-460281030538}
{09AA6758-0BD3-4312-9C07-AE9F1D50A3AD} = {569E6837-0771-4C08-BB09-460281030538}
{B4E3E774-2C16-4CBF-87EF-88C547529B94} = {569E6837-0771-4C08-BB09-460281030538}
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
{44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
{4D8B7538-D933-4F3A-818D-4E19ABA7E182} = {39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}
{6C60944F-4FE1-450F-884B-D523EDFCFAB3} = {39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}
{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B} = {D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}
{42F9A600-BEC3-4F87-97EE-38E0DCAABC5A} = {D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}
{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{008873D5-9028-4FF3-8354-71F713748625} = {32CDDDCD-5319-494C-AB41-42B87C467F04}
{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
{32CDDDCD-5319-494C-AB41-42B87C467F04} = {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 @@ -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 gcvHandle);
private static partial void _CancelPromise(IntPtr gcHandle);

public static void CancelPromise(Task promise)
{
Expand All @@ -26,7 +26,7 @@ public static void CancelPromise(Task promise)
holder.SynchronizationContext!.Send(static (JSHostImplementation.PromiseHolder holder) =>
{
#endif
_CancelPromise(holder.GCVHandle);
_CancelPromise(holder.GCHandle);
#if FEATURE_WASM_THREADS
}, holder);
#endif
Expand All @@ -47,7 +47,7 @@ public static void CancelPromise<T>(Task promise, Action<T> callback, T state)
holder.SynchronizationContext!.Send((JSHostImplementation.PromiseHolder holder) =>
{
#endif
_CancelPromise(holder.GCVHandle);
_CancelPromise(holder.GCHandle);
callback.Invoke(state);
#if FEATURE_WASM_THREADS
}, holder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,27 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments
try
{
var gcHandle = arg_1.slot.GCHandle;
if (IsGCVHandle(gcHandle) && ThreadJsOwnedHolders.Remove(gcHandle, out PromiseHolder? holder))
if (IsGCVHandle(gcHandle))
{
holder.GCVHandle = IntPtr.Zero;
holder.Callback!(null);
if (ThreadJsOwnedHolders.Remove(gcHandle, out PromiseHolder? holder))
{
holder.GCHandle = IntPtr.Zero;
holder.Callback!(null);
}
}
else
{
GCHandle handle = (GCHandle)gcHandle;
ThreadJsOwnedObjects.Remove(handle.Target!);
var target = handle.Target!;
if (target is PromiseHolder holder)
{
holder.GCHandle = IntPtr.Zero;
holder.Callback!(null);
}
else
{
ThreadJsOwnedObjects.Remove(target);
}
handle.Free();
}
}
Expand Down Expand Up @@ -191,7 +203,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
}

// the marshaled signature is:
// void CompleteTask<T>(GCVHandle holder, Exception? exceptionResult, T? result)
// void CompleteTask<T>(GCHandle 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 @@ -200,17 +212,31 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
// arg_3 set by caller when this is SetResult call
try
{
var callback_gcv_handle = arg_1.slot.GCHandle;
if (ThreadJsOwnedHolders.Remove(callback_gcv_handle, out PromiseHolder? promiseHolder) && promiseHolder.Callback != null)
var holderGCHandle = arg_1.slot.GCHandle;
if (IsGCVHandle(holderGCHandle))
{
promiseHolder.GCVHandle = IntPtr.Zero;

// arg_2, arg_3 are processed by the callback
promiseHolder.Callback(arguments_buffer);
if (ThreadJsOwnedHolders.Remove(holderGCHandle, out PromiseHolder? holder))
{
holder.GCHandle = IntPtr.Zero;
// arg_2, arg_3 are processed by the callback
holder.Callback!(arguments_buffer);
}
}
else
{
throw new InvalidOperationException(SR.NullPromiseHolder);
GCHandle handle = (GCHandle)holderGCHandle;
var target = handle.Target!;
if (target is PromiseHolder holder)
{
holder.GCHandle = IntPtr.Zero;
// arg_2, arg_3 are processed by the callback
holder.Callback!(arguments_buffer);
}
else
{
ThreadJsOwnedObjects.Remove(target);
}
handle.Free();
}
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public override string? StackTrace
}

#if FEATURE_WASM_THREADS
if (jsException.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
var currentTID = JSSynchronizationContext.CurrentJSSynchronizationContext?.TargetTID;
if (jsException.OwnerTID != currentTID)
{
return bs;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, i
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span<JSMarshalerArgument> arguments)
internal static unsafe void InvokeJSFunction(JSObject jsFunction, Span<JSMarshalerArgument> arguments)
{
ObjectDisposedException.ThrowIf(jsFunction.IsDisposed, jsFunction);
#if FEATURE_WASM_THREADS
Expand All @@ -220,6 +220,19 @@ internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span<JSMarshalerAr
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span<JSMarshalerArgument> arguments)
{
if (signature.IsAsync)
{
// pre-allocate the result handle and Task
#if FEATURE_WASM_THREADS
JSSynchronizationContext.AssertWebWorkerContext();
var holder = new JSHostImplementation.PromiseHolder(JSSynchronizationContext.CurrentJSSynchronizationContext!);
#else
var holder = new JSHostImplementation.PromiseHolder();
#endif
arguments[1].slot.Type = MarshalerType.Task;
arguments[1].slot.GCHandle = holder.GCHandle;
}

fixed (JSMarshalerArgument* ptr = arguments)
{
Interop.Runtime.InvokeJSImport(signature.ImportHandle, ptr);
Expand All @@ -229,6 +242,15 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span
JSHostImplementation.ThrowException(ref exceptionArg);
}
}
if (signature.IsAsync)
{
// if js synchronously returned null
if (arguments[1].slot.Type == MarshalerType.None)
{
var holderHandle = (GCHandle)arguments[1].slot.GCHandle;
holderHandle.Free();
}
}
}

internal static unsafe JSFunctionBinding BindJSFunctionImpl(string functionName, string moduleName, ReadOnlySpan<JSMarshalerType> signatures)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,32 @@ internal static partial class JSHostImplementation

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

#if FEATURE_WASM_THREADS
public PromiseHolder(JSSynchronizationContext targetContext)
{
GCHandle = (IntPtr)InteropServices.GCHandle.Alloc(this, GCHandleType.Normal);
SynchronizationContext = targetContext;
}
#else
public PromiseHolder()
{
GCHandle = (IntPtr)InteropServices.GCHandle.Alloc(this, GCHandleType.Normal);
}
#endif

public PromiseHolder(nint gcvHandle)
{
this.GCVHandle = gcvHandle;
GCHandle = gcvHandle;
#if FEATURE_WASM_THREADS
this.OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
this.SynchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
JSSynchronizationContext.AssertWebWorkerContext();
SynchronizationContext = JSSynchronizationContext.CurrentJSSynchronizationContext!;
#endif
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ public static List<nint> JSVHandleFreeList

public static nint AllocJSVHandle()
{
#if FEATURE_WASM_THREADS
// TODO, when Task is passed to JSImport as parameter, it could be sent from another thread (in the future)
// and so we need to use JSVHandleFreeList of the target thread
JSSynchronizationContext.AssertWebWorkerContext();
#endif

if (JSVHandleFreeList.Count > 0)
{
var jsvHandle = JSVHandleFreeList[JSVHandleFreeList.Count];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public SynchronizationContext SynchronizationContext

#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 nint OwnerTID;
#endif
#if !DISABLE_LEGACY_JS_INTEROP
internal GCHandle? InFlight;
Expand All @@ -42,12 +42,13 @@ internal JSObject(IntPtr jsHandle)
{
JSHandle = jsHandle;
#if FEATURE_WASM_THREADS
OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
m_SynchronizationContext = JSSynchronizationContext.CurrentJSSynchronizationContext;
if (m_SynchronizationContext == null)
var ctx = JSSynchronizationContext.CurrentJSSynchronizationContext;
if (ctx == null)
{
throw new InvalidOperationException(); // should not happen
Environment.FailFast("Missing CurrentJSSynchronizationContext");
}
m_SynchronizationContext = ctx;
OwnerTID = ctx!.TargetTID;
#endif
}

Expand Down Expand Up @@ -93,16 +94,19 @@ internal static void AssertThreadAffinity(object value)
{
return;
}
else if (value is JSObject jsObject)
JSSynchronizationContext.AssertWebWorkerContext();
var currentTID = JSSynchronizationContext.CurrentJSSynchronizationContext!.TargetTID;

if (value is JSObject jsObject)
{
if (jsObject.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
if (jsObject.OwnerTID != currentTID)
{
throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created.");
}
}
else if (value is JSException jsException)
{
if (jsException.jsException != null && jsException.jsException.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
if (jsException.jsException != null && jsException.jsException.OwnerTID != currentTID)
{
throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created.");
}
Expand Down Expand Up @@ -131,8 +135,21 @@ private void DisposeThis()
if (!_isDisposed)
{
#if FEATURE_WASM_THREADS
SynchronizationContext.Send(static (JSObject self) =>
if (SynchronizationContext == SynchronizationContext.Current)
{
lock (_thisLock)
{
JSHostImplementation.ReleaseCSOwnedObject(JSHandle);
_isDisposed = true;
JSHandle = IntPtr.Zero;
m_SynchronizationContext = null;
} //lock
return;
}

SynchronizationContext.Post(static (object? s) =>
{
var self = (JSObject)s!;
lock (self._thisLock)
{
JSHostImplementation.ReleaseCSOwnedObject(self.JSHandle);
Expand Down
Loading

0 comments on commit 78647a8

Please sign in to comment.