Skip to content

Commit 78647a8

Browse files
committed
wip
1 parent 563664e commit 78647a8

24 files changed

+416
-129
lines changed

src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
Microsoft Visual Studio Solution File, Format Version 12.00
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.8.34004.107
5+
MinimumVisualStudioVersion = 10.0.40219.1
26
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272}"
37
EndProject
48
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections", "..\System.Collections\ref\System.Collections.csproj", "{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3}"
@@ -43,11 +47,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{569E6837-077
4347
EndProject
4448
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}"
4549
EndProject
46-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}"
50+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}"
4751
EndProject
48-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}"
52+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}"
4953
EndProject
50-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{32CDDDCD-5319-494C-AB41-42B87C467F04}"
54+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{32CDDDCD-5319-494C-AB41-42B87C467F04}"
5155
EndProject
5256
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F92020A9-28BE-4398-86FF-5CFE44C94882}"
5357
EndProject
@@ -135,28 +139,32 @@ Global
135139
EndGlobalSection
136140
GlobalSection(NestedProjects) = preSolution
137141
{ED86AB26-1CFB-457D-BF87-B7A0D8FAF272} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
142+
{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
143+
{CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {569E6837-0771-4C08-BB09-460281030538}
144+
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
145+
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}
138146
{BFED925C-18F2-4C98-833E-66F205234598} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
139147
{ABA5A92B-CAD8-47E8-A7CE-D28A67FB69C0} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
140148
{765B4AA5-723A-44FF-BC4E-EB0F03103F6D} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
141-
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
142-
{8B1D80E9-AE0D-4E3C-9F91-E6862B49AEF3} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
143-
{28278E01-BF5C-4AB6-AA7A-8DD4E6C04DB1} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
144-
{44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
145-
{CE5E53C1-F9B5-41EE-8D00-837913EC57D1} = {569E6837-0771-4C08-BB09-460281030538}
146149
{FB12C247-AFEF-4772-BB0C-983969B6CF32} = {569E6837-0771-4C08-BB09-460281030538}
147150
{09AA6758-0BD3-4312-9C07-AE9F1D50A3AD} = {569E6837-0771-4C08-BB09-460281030538}
148151
{B4E3E774-2C16-4CBF-87EF-88C547529B94} = {569E6837-0771-4C08-BB09-460281030538}
149-
{71A845ED-4344-41FC-8FCA-3AC9B6BA6C45} = {67F3A00A-AE6C-434C-927D-E5D38DE2DA2C}
152+
{EC3ADEFA-1FF3-482C-8CCB-FE4C77292532} = {26A72FFB-871A-4F2F-A513-B2F6E09F358C}
153+
{44BAE6F1-94C2-415B-9A16-3B8EC429B09B} = {1DFF019B-6B73-4E5A-A6DA-5EBEF4AA7EBF}
150154
{4D8B7538-D933-4F3A-818D-4E19ABA7E182} = {39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}
151155
{6C60944F-4FE1-450F-884B-D523EDFCFAB3} = {39B30F44-B141-44E9-B7A7-B1A9EDB1A61C}
152-
{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
153156
{B8F2E56D-6571-466D-9EF2-9FCAD3FC6E5B} = {D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}
154157
{42F9A600-BEC3-4F87-97EE-38E0DCAABC5A} = {D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A}
155-
{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
156158
{008873D5-9028-4FF3-8354-71F713748625} = {32CDDDCD-5319-494C-AB41-42B87C467F04}
159+
{39B30F44-B141-44E9-B7A7-B1A9EDB1A61C} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
160+
{D819DEB6-6F6B-484B-9F0F-BDBCEDC83A1A} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
157161
{32CDDDCD-5319-494C-AB41-42B87C467F04} = {F92020A9-28BE-4398-86FF-5CFE44C94882}
158162
EndGlobalSection
159163
GlobalSection(ExtensibilityGlobals) = postSolution
160164
SolutionGuid = {3FE64246-4AFA-424A-AE5D-7007E20451B5}
161165
EndGlobalSection
166+
GlobalSection(SharedMSBuildProjectFiles) = preSolution
167+
..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{42f9a600-bec3-4f87-97ee-38e0dcaabc5a}*SharedItemsImports = 5
168+
..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{6c60944f-4fe1-450f-884b-d523edfcfab3}*SharedItemsImports = 5
169+
EndGlobalSection
162170
EndGlobal

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace System.Runtime.InteropServices.JavaScript
99
public static partial class CancelablePromise
1010
{
1111
[JSImport("INTERNAL.mono_wasm_cancel_promise")]
12-
private static partial void _CancelPromise(IntPtr gcvHandle);
12+
private static partial void _CancelPromise(IntPtr gcHandle);
1313

1414
public static void CancelPromise(Task promise)
1515
{
@@ -26,7 +26,7 @@ public static void CancelPromise(Task promise)
2626
holder.SynchronizationContext!.Send(static (JSHostImplementation.PromiseHolder holder) =>
2727
{
2828
#endif
29-
_CancelPromise(holder.GCVHandle);
29+
_CancelPromise(holder.GCHandle);
3030
#if FEATURE_WASM_THREADS
3131
}, holder);
3232
#endif
@@ -47,7 +47,7 @@ public static void CancelPromise<T>(Task promise, Action<T> callback, T state)
4747
holder.SynchronizationContext!.Send((JSHostImplementation.PromiseHolder holder) =>
4848
{
4949
#endif
50-
_CancelPromise(holder.GCVHandle);
50+
_CancelPromise(holder.GCHandle);
5151
callback.Invoke(state);
5252
#if FEATURE_WASM_THREADS
5353
}, holder);

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,27 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments
143143
try
144144
{
145145
var gcHandle = arg_1.slot.GCHandle;
146-
if (IsGCVHandle(gcHandle) && ThreadJsOwnedHolders.Remove(gcHandle, out PromiseHolder? holder))
146+
if (IsGCVHandle(gcHandle))
147147
{
148-
holder.GCVHandle = IntPtr.Zero;
149-
holder.Callback!(null);
148+
if (ThreadJsOwnedHolders.Remove(gcHandle, out PromiseHolder? holder))
149+
{
150+
holder.GCHandle = IntPtr.Zero;
151+
holder.Callback!(null);
152+
}
150153
}
151154
else
152155
{
153156
GCHandle handle = (GCHandle)gcHandle;
154-
ThreadJsOwnedObjects.Remove(handle.Target!);
157+
var target = handle.Target!;
158+
if (target is PromiseHolder holder)
159+
{
160+
holder.GCHandle = IntPtr.Zero;
161+
holder.Callback!(null);
162+
}
163+
else
164+
{
165+
ThreadJsOwnedObjects.Remove(target);
166+
}
155167
handle.Free();
156168
}
157169
}
@@ -191,7 +203,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer)
191203
}
192204

193205
// the marshaled signature is:
194-
// void CompleteTask<T>(GCVHandle holder, Exception? exceptionResult, T? result)
206+
// void CompleteTask<T>(GCHandle holder, Exception? exceptionResult, T? result)
195207
public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
196208
{
197209
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
@@ -200,17 +212,31 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
200212
// arg_3 set by caller when this is SetResult call
201213
try
202214
{
203-
var callback_gcv_handle = arg_1.slot.GCHandle;
204-
if (ThreadJsOwnedHolders.Remove(callback_gcv_handle, out PromiseHolder? promiseHolder) && promiseHolder.Callback != null)
215+
var holderGCHandle = arg_1.slot.GCHandle;
216+
if (IsGCVHandle(holderGCHandle))
205217
{
206-
promiseHolder.GCVHandle = IntPtr.Zero;
207-
208-
// arg_2, arg_3 are processed by the callback
209-
promiseHolder.Callback(arguments_buffer);
218+
if (ThreadJsOwnedHolders.Remove(holderGCHandle, out PromiseHolder? holder))
219+
{
220+
holder.GCHandle = IntPtr.Zero;
221+
// arg_2, arg_3 are processed by the callback
222+
holder.Callback!(arguments_buffer);
223+
}
210224
}
211225
else
212226
{
213-
throw new InvalidOperationException(SR.NullPromiseHolder);
227+
GCHandle handle = (GCHandle)holderGCHandle;
228+
var target = handle.Target!;
229+
if (target is PromiseHolder holder)
230+
{
231+
holder.GCHandle = IntPtr.Zero;
232+
// arg_2, arg_3 are processed by the callback
233+
holder.Callback!(arguments_buffer);
234+
}
235+
else
236+
{
237+
ThreadJsOwnedObjects.Remove(target);
238+
}
239+
handle.Free();
214240
}
215241
}
216242
catch (Exception ex)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public override string? StackTrace
4747
}
4848

4949
#if FEATURE_WASM_THREADS
50-
if (jsException.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
50+
var currentTID = JSSynchronizationContext.CurrentJSSynchronizationContext?.TargetTID;
51+
if (jsException.OwnerTID != currentTID)
5152
{
5253
return bs;
5354
}

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, i
198198
}
199199

200200
[MethodImpl(MethodImplOptions.AggressiveInlining)]
201-
internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span<JSMarshalerArgument> arguments)
201+
internal static unsafe void InvokeJSFunction(JSObject jsFunction, Span<JSMarshalerArgument> arguments)
202202
{
203203
ObjectDisposedException.ThrowIf(jsFunction.IsDisposed, jsFunction);
204204
#if FEATURE_WASM_THREADS
@@ -220,6 +220,19 @@ internal static unsafe void InvokeJSImpl(JSObject jsFunction, Span<JSMarshalerAr
220220
[MethodImpl(MethodImplOptions.AggressiveInlining)]
221221
internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span<JSMarshalerArgument> arguments)
222222
{
223+
if (signature.IsAsync)
224+
{
225+
// pre-allocate the result handle and Task
226+
#if FEATURE_WASM_THREADS
227+
JSSynchronizationContext.AssertWebWorkerContext();
228+
var holder = new JSHostImplementation.PromiseHolder(JSSynchronizationContext.CurrentJSSynchronizationContext!);
229+
#else
230+
var holder = new JSHostImplementation.PromiseHolder();
231+
#endif
232+
arguments[1].slot.Type = MarshalerType.Task;
233+
arguments[1].slot.GCHandle = holder.GCHandle;
234+
}
235+
223236
fixed (JSMarshalerArgument* ptr = arguments)
224237
{
225238
Interop.Runtime.InvokeJSImport(signature.ImportHandle, ptr);
@@ -229,6 +242,15 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span
229242
JSHostImplementation.ThrowException(ref exceptionArg);
230243
}
231244
}
245+
if (signature.IsAsync)
246+
{
247+
// if js synchronously returned null
248+
if (arguments[1].slot.Type == MarshalerType.None)
249+
{
250+
var holderHandle = (GCHandle)arguments[1].slot.GCHandle;
251+
holderHandle.Free();
252+
}
253+
}
232254
}
233255

234256
internal static unsafe JSFunctionBinding BindJSFunctionImpl(string functionName, string moduleName, ReadOnlySpan<JSMarshalerType> signatures)

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.Types.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,32 @@ internal static partial class JSHostImplementation
1212

1313
public sealed class PromiseHolder
1414
{
15-
public nint GCVHandle;
15+
public nint GCHandle;
1616
public ToManagedCallback? Callback;
1717
#if FEATURE_WASM_THREADS
1818
// the JavaScript object could only exist on the single web worker and can't migrate to other workers
19-
internal int OwnerThreadId;
20-
internal SynchronizationContext? SynchronizationContext;
19+
internal JSSynchronizationContext SynchronizationContext;
20+
#endif
21+
22+
#if FEATURE_WASM_THREADS
23+
public PromiseHolder(JSSynchronizationContext targetContext)
24+
{
25+
GCHandle = (IntPtr)InteropServices.GCHandle.Alloc(this, GCHandleType.Normal);
26+
SynchronizationContext = targetContext;
27+
}
28+
#else
29+
public PromiseHolder()
30+
{
31+
GCHandle = (IntPtr)InteropServices.GCHandle.Alloc(this, GCHandleType.Normal);
32+
}
2133
#endif
2234

2335
public PromiseHolder(nint gcvHandle)
2436
{
25-
this.GCVHandle = gcvHandle;
37+
GCHandle = gcvHandle;
2638
#if FEATURE_WASM_THREADS
27-
this.OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
28-
this.SynchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
39+
JSSynchronizationContext.AssertWebWorkerContext();
40+
SynchronizationContext = JSSynchronizationContext.CurrentJSSynchronizationContext!;
2941
#endif
3042
}
3143
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ public static List<nint> JSVHandleFreeList
8484

8585
public static nint AllocJSVHandle()
8686
{
87+
#if FEATURE_WASM_THREADS
88+
// TODO, when Task is passed to JSImport as parameter, it could be sent from another thread (in the future)
89+
// and so we need to use JSVHandleFreeList of the target thread
90+
JSSynchronizationContext.AssertWebWorkerContext();
91+
#endif
92+
8793
if (JSVHandleFreeList.Count > 0)
8894
{
8995
var jsvHandle = JSVHandleFreeList[JSVHandleFreeList.Count];

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public SynchronizationContext SynchronizationContext
3030

3131
#if FEATURE_WASM_THREADS
3232
// the JavaScript object could only exist on the single web worker and can't migrate to other workers
33-
internal int OwnerThreadId;
33+
internal nint OwnerTID;
3434
#endif
3535
#if !DISABLE_LEGACY_JS_INTEROP
3636
internal GCHandle? InFlight;
@@ -42,12 +42,13 @@ internal JSObject(IntPtr jsHandle)
4242
{
4343
JSHandle = jsHandle;
4444
#if FEATURE_WASM_THREADS
45-
OwnerThreadId = Thread.CurrentThread.ManagedThreadId;
46-
m_SynchronizationContext = JSSynchronizationContext.CurrentJSSynchronizationContext;
47-
if (m_SynchronizationContext == null)
45+
var ctx = JSSynchronizationContext.CurrentJSSynchronizationContext;
46+
if (ctx == null)
4847
{
49-
throw new InvalidOperationException(); // should not happen
48+
Environment.FailFast("Missing CurrentJSSynchronizationContext");
5049
}
50+
m_SynchronizationContext = ctx;
51+
OwnerTID = ctx!.TargetTID;
5152
#endif
5253
}
5354

@@ -93,16 +94,19 @@ internal static void AssertThreadAffinity(object value)
9394
{
9495
return;
9596
}
96-
else if (value is JSObject jsObject)
97+
JSSynchronizationContext.AssertWebWorkerContext();
98+
var currentTID = JSSynchronizationContext.CurrentJSSynchronizationContext!.TargetTID;
99+
100+
if (value is JSObject jsObject)
97101
{
98-
if (jsObject.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
102+
if (jsObject.OwnerTID != currentTID)
99103
{
100104
throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created.");
101105
}
102106
}
103107
else if (value is JSException jsException)
104108
{
105-
if (jsException.jsException != null && jsException.jsException.OwnerThreadId != Thread.CurrentThread.ManagedThreadId)
109+
if (jsException.jsException != null && jsException.jsException.OwnerTID != currentTID)
106110
{
107111
throw new InvalidOperationException("The JavaScript object can be used only on the thread where it was created.");
108112
}
@@ -131,8 +135,21 @@ private void DisposeThis()
131135
if (!_isDisposed)
132136
{
133137
#if FEATURE_WASM_THREADS
134-
SynchronizationContext.Send(static (JSObject self) =>
138+
if (SynchronizationContext == SynchronizationContext.Current)
139+
{
140+
lock (_thisLock)
141+
{
142+
JSHostImplementation.ReleaseCSOwnedObject(JSHandle);
143+
_isDisposed = true;
144+
JSHandle = IntPtr.Zero;
145+
m_SynchronizationContext = null;
146+
} //lock
147+
return;
148+
}
149+
150+
SynchronizationContext.Post(static (object? s) =>
135151
{
152+
var self = (JSObject)s!;
136153
lock (self._thisLock)
137154
{
138155
JSHostImplementation.ReleaseCSOwnedObject(self.JSHandle);

0 commit comments

Comments
 (0)