Skip to content

Commit c7280f7

Browse files
committed
wip
1 parent c8ae841 commit c7280f7

File tree

13 files changed

+322
-18
lines changed

13 files changed

+322
-18
lines changed

eng/testing/tests.wasi.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasm --engine-arg=max-wasm-stack=134217728</_XHarnessArgs>
5151
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=http</_XHarnessArgs>
5252
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=inherit-network</_XHarnessArgs>
53+
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=tcp</_XHarnessArgs>
54+
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=udp</_XHarnessArgs>
5355
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=allow-ip-name-lookup</_XHarnessArgs>
5456
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--env --engine-arg=DOTNET_WASI_PRINT_EXIT_CODE=1</_XHarnessArgs>
5557
<_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli)</_XHarnessArgs>

src/libraries/System.Net.Sockets/Directory.Build.props

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<PropertyGroup>
44
<StrongNameKeyId>Microsoft</StrongNameKeyId>
55
<IncludePlatformAttributes>true</IncludePlatformAttributes>
6-
<!-- WASI until https://github.com/dotnet/runtime/issues/98957 -->
7-
<UnsupportedOSPlatforms>browser;wasi</UnsupportedOSPlatforms>
6+
<UnsupportedOSPlatforms>browser</UnsupportedOSPlatforms>
87
</PropertyGroup>
98
</Project>

src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)</TargetFrameworks>
4+
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-wasi;$(NetCoreAppCurrent)</TargetFrameworks>
55
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
66
<!-- SYSTEM_NET_SOCKETS_DLL is required to allow source-level code sharing for types defined within the
77
System.Net.Internals namespace. -->
@@ -15,6 +15,7 @@
1515
<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetPlatformIdentifier)' == ''">SR.SystemNetSockets_PlatformNotSupported</GeneratePlatformNotSupportedAssemblyMessage>
1616
<IsApplePlatform Condition="'$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">true</IsApplePlatform>
1717
<DefineConstants Condition="'$(IsApplePlatform)' == 'true'">$(DefineConstants);SYSTEM_NET_SOCKETS_APPLE_PLATFROM</DefineConstants>
18+
<DefineConstants Condition="'$(TargetPlatformIdentifier)' == 'wasi'">$(DefineConstants);TARGET_WASI</DefineConstants>
1819
</PropertyGroup>
1920

2021
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != ''">
@@ -183,11 +184,12 @@
183184
Link="Common\System\Net\CompletionPortHelper.Windows.cs" />
184185
</ItemGroup>
185186

186-
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
187+
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'wasi' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
187188
<Compile Include="System\Net\Sockets\SafeSocketHandle.Unix.cs" />
188189
<Compile Include="System\Net\Sockets\Socket.Unix.cs" />
189190
<Compile Include="System\Net\Sockets\SocketAsyncContext.Unix.cs" />
190191
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Unix.cs" />
192+
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Wasi.cs" Condition="'$(TargetPlatformIdentifier)' == 'wasi'"/>
191193
<Compile Include="System\Net\Sockets\SocketAsyncEventArgs.Unix.cs" />
192194
<Compile Include="System\Net\Sockets\SocketPal.Unix.cs" />
193195
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.Unix.cs" />

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,10 +1348,12 @@ public void SetHandleNonBlocking()
13481348
//
13491349
if (!_isHandleNonBlocking)
13501350
{
1351+
#if !TARGET_WASI // WASI-libc doesn't have emulate this for tcp handles, see https://github.com/WebAssembly/wasi-libc/issues/536
13511352
if (Interop.Sys.Fcntl.SetIsNonBlocking(_socket, 1) != 0)
13521353
{
13531354
throw new SocketException((int)SocketPal.GetSocketErrorForErrorCode(Interop.Sys.GetLastError()));
13541355
}
1356+
#endif
13551357

13561358
_isHandleNonBlocking = true;
13571359
}

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace System.Net.Sockets
1111
{
12-
internal sealed unsafe class SocketAsyncEngine : IThreadPoolWorkItem
12+
internal sealed unsafe partial class SocketAsyncEngine : IThreadPoolWorkItem
1313
{
1414
private const int EventBufferCount =
1515
#if DEBUG
@@ -143,6 +143,7 @@ public void UnregisterSocket(IntPtr socketHandle)
143143
_handleToContextMap.TryRemove(socketHandle, out _);
144144
}
145145

146+
#if !TARGET_WASI
146147
private SocketAsyncEngine()
147148
{
148149
_port = (IntPtr)(-1);
@@ -161,9 +162,11 @@ private SocketAsyncEngine()
161162
}
162163
}
163164

165+
Console.WriteLine("SocketAsyncEngine C");
164166
fixed (Interop.Sys.SocketEvent** bufferPtr = &_buffer)
165167
{
166168
err = Interop.Sys.CreateSocketEventBuffer(EventBufferCount, bufferPtr);
169+
Console.WriteLine("SocketAsyncEngine D");
167170
if (err != Interop.Error.SUCCESS)
168171
{
169172
throw new InternalException(err);
@@ -217,6 +220,7 @@ private void EventLoop()
217220
Environment.FailFast("Exception thrown from SocketAsyncEngine event loop: " + e.ToString(), e);
218221
}
219222
}
223+
#endif
220224

221225
private void UpdateEventQueueProcessingStage(bool isEventQueueEmpty)
222226
{
@@ -303,6 +307,7 @@ void IThreadPoolWorkItem.Execute()
303307
} while (Environment.TickCount - startTimeMs < 15 && eventQueue.TryDequeue(out ev));
304308
}
305309

310+
#if !TARGET_WASI
306311
private void FreeNativeResources()
307312
{
308313
if (_buffer != null)
@@ -314,6 +319,7 @@ private void FreeNativeResources()
314319
Interop.Sys.CloseSocketEventPort(_port);
315320
}
316321
}
322+
#endif // !TARGET_WASI
317323

318324
// The JIT is allowed to arbitrarily extend the lifetime of locals, which may retain SocketAsyncContext references,
319325
// indirectly preventing Socket instances to be finalized, despite being no longer referenced by user code.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Concurrent;
5+
using System.Diagnostics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace System.Net.Sockets
12+
{
13+
internal sealed partial class SocketAsyncEngine : IThreadPoolWorkItem
14+
{
15+
private SocketAsyncEngine()
16+
{
17+
#pragma warning disable CS4014
18+
WasiSocketsEventLoop();
19+
#pragma warning restore CS4014
20+
}
21+
22+
private async Task WasiSocketsEventLoop()
23+
{
24+
try
25+
{
26+
SocketEventHandler handler = new SocketEventHandler(this);
27+
while (true)
28+
{
29+
int numEvents = EventBufferCount;
30+
await WaitForAnySocketPollable().ConfigureAwait(false);
31+
32+
// The native shim is responsible for ensuring this condition.
33+
Debug.Assert(numEvents > 0, $"Unexpected numEvents: {numEvents}");
34+
35+
// Only enqueue a work item if the stage is NotScheduled.
36+
// Otherwise there must be a work item already queued or another thread already handling parallelization.
37+
if (handler.HandleSocketEvents(numEvents) &&
38+
Interlocked.Exchange(
39+
ref _eventQueueProcessingStage,
40+
EventQueueProcessingStage.Scheduled) == EventQueueProcessingStage.NotScheduled)
41+
{
42+
ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false);
43+
}
44+
}
45+
}
46+
catch (Exception e)
47+
{
48+
Environment.FailFast("Exception thrown from SocketAsyncEngine event loop: " + e.ToString(), e);
49+
}
50+
}
51+
52+
#pragma warning disable CA1822 // TODO remove
53+
private Task WaitForAnySocketPollable()
54+
{
55+
TaskCompletionSource todo = new TaskCompletionSource();
56+
/* TODO something like this, but with handle->pollable conversion
57+
Interop.Error err = Interop.Sys.WaitForSocketEvents(_port, handler.Buffer, &numEvents);
58+
if (err != Interop.Error.SUCCESS)
59+
{
60+
throw new InternalException(err);
61+
}*/
62+
return todo.Task;
63+
}
64+
}
65+
}

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, IL
289289
int startIndex = bufferIndex, startOffset = offset;
290290

291291
int maxBuffers = buffers.Count - startIndex;
292+
#if TARGET_WASI // WASI doesn't have iovecs and recvmsg in preview2
293+
maxBuffers = Math.Max(maxBuffers, 1);
294+
#endif
292295
bool allocOnStack = maxBuffers <= IovStackThreshold;
293296
Span<GCHandle> handles = allocOnStack ? stackalloc GCHandle[IovStackThreshold] : new GCHandle[maxBuffers];
294297
Span<Interop.Sys.IOVector> iovecs = allocOnStack ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[maxBuffers];
@@ -376,6 +379,9 @@ private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags,
376379
Debug.Assert(socket.IsSocket);
377380

378381
int maxBuffers = buffers.Count;
382+
#if TARGET_WASI // WASI doesn't have iovecs and recvmsg in preview2
383+
maxBuffers = Math.Max(maxBuffers, 1);
384+
#endif
379385
bool allocOnStack = maxBuffers <= IovStackThreshold;
380386

381387
// When there are many buffers, reduce the number of pinned buffers based on available bytes.
@@ -532,6 +538,10 @@ private static unsafe int SysReceiveMessageFrom(
532538
Debug.Assert(socket.IsSocket);
533539

534540
int buffersCount = buffers.Count;
541+
#if TARGET_WASI // WASI doesn't have iovecs and sendmsg in preview2
542+
buffersCount = Math.Max(buffersCount, 1);
543+
#endif
544+
535545
bool allocOnStack = buffersCount <= IovStackThreshold;
536546
Span<GCHandle> handles = allocOnStack ? stackalloc GCHandle[IovStackThreshold] : new GCHandle[buffersCount];
537547
Span<Interop.Sys.IOVector> iovecs = allocOnStack ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[buffersCount];
@@ -554,17 +564,21 @@ private static unsafe int SysReceiveMessageFrom(
554564
fixed (byte* sockAddr = socketAddress)
555565
fixed (Interop.Sys.IOVector* iov = iovecs)
556566
{
567+
#if !TARGET_WASI // WASI doesn't have msg_control and sendmsg in preview2
557568
int cmsgBufferLen = Interop.Sys.GetControlMessageBufferSize(Convert.ToInt32(isIPv4), Convert.ToInt32(isIPv6));
558569
byte* cmsgBuffer = stackalloc byte[cmsgBufferLen];
570+
#endif
559571

560572
var messageHeader = new Interop.Sys.MessageHeader
561573
{
562574
SocketAddress = sockAddr,
563575
SocketAddressLen = socketAddress.Length,
564576
IOVectors = iov,
565577
IOVectorCount = iovCount,
578+
#if !TARGET_WASI // WASI doesn't have msg_control and sendmsg in preview2
566579
ControlBuffer = cmsgBuffer,
567580
ControlBufferLen = cmsgBufferLen
581+
#endif
568582
};
569583

570584
long received = 0;

src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
<PropertyGroup>
33
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
44
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
5-
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser</TargetFrameworks>
5+
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-wasi</TargetFrameworks>
66
<IgnoreForCI Condition="'$(TargetOS)' == 'browser'">true</IgnoreForCI>
77
<EventSourceSupport Condition="'$(TestNativeAot)' == 'true'">true</EventSourceSupport>
8+
<XunitShowProgress Condition="'$(TargetOS)' == 'wasi'">true</XunitShowProgress>
89
</PropertyGroup>
910
<ItemGroup>
1011
<Compile Include="Accept.cs" />

src/mono/sample/wasi/Directory.Build.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040

4141
<WasiCommand Condition="'$(_WasiNeedsHttp)' == 'true'" >$(WasiCommand) --wasi http</WasiCommand>
42+
<WasiCommand Condition="'$(_WasiNeedsSocket)' == 'true'" >$(WasiCommand) --wasi inherit-network --wasi tcp --wasi udp --wasi allow-ip-name-lookup</WasiCommand>
4243
<WasiCommand Condition="'$(WasmSingleFileBundle)' != 'true'" >$(WasiCommand) --dir .</WasiCommand>
4344
<WasiCommand >$(WasiCommand) $(_DotnetWasmName)</WasiCommand>
4445
<WasiCommand Condition="'$(WasmSingleFileBundle)' != 'true'" >$(WasiCommand) $(_SampleProjectName)</WasiCommand>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Net.Http.Headers;
6+
using System.Net.Http;
7+
using System.Threading.Tasks;
8+
using System.Threading;
9+
using System.Runtime.CompilerServices;
10+
using System.Net;
11+
using System.Net.Sockets;
12+
using System.Text;
13+
14+
public static class WasiMainWrapper
15+
{
16+
public static async Task<int> MainAsync(string[] args)
17+
{
18+
IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("example.com");
19+
IPAddress ipAddress = ipHostInfo.AddressList[0];
20+
Console.WriteLine($"IP Address: {ipAddress}");
21+
22+
IPEndPoint ipEndPoint = new(ipAddress, 80);
23+
using Socket client = new(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
24+
25+
await client.ConnectAsync(ipEndPoint);
26+
27+
// Send message.
28+
var message = @"GET / HTTP/1.1
29+
Host: example.com
30+
Accept: */*
31+
32+
";
33+
var messageBytes = Encoding.UTF8.GetBytes(message);
34+
var start = 0;
35+
while (start < messageBytes.Length)
36+
{
37+
start += await client.SendAsync(messageBytes.AsMemory(start), SocketFlags.None);
38+
Console.WriteLine("TODO poll here");
39+
}
40+
Console.WriteLine("GET sent");
41+
42+
// Receive ack.
43+
var buffer = new byte[2048];
44+
var received = await client.ReceiveAsync(buffer, SocketFlags.None);
45+
var response = Encoding.UTF8.GetString(buffer, 0, received);
46+
Console.WriteLine(response);
47+
48+
client.Shutdown(SocketShutdown.Both);
49+
50+
return 0;
51+
}
52+
53+
public static int Main(string[] args)
54+
{
55+
return PollWasiEventLoopUntilResolved((Thread)null!, MainAsync(args));
56+
57+
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "PollWasiEventLoopUntilResolved")]
58+
static extern T PollWasiEventLoopUntilResolved<T>(Thread t, Task<T> mainTask);
59+
}
60+
61+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
4+
<_WasiNeedsHttp>true</_WasiNeedsHttp>
5+
<_WasiNeedsSocket>true</_WasiNeedsSocket>
6+
<!--
7+
<WasmSingleFileBundle>true</WasmSingleFileBundle>
8+
<InvariantGlobalization>true</InvariantGlobalization>
9+
-->
10+
</PropertyGroup>
11+
12+
<Target Name="RunSample" DependsOnTargets="RunSampleWithWasmtime" />
13+
</Project>

0 commit comments

Comments
 (0)