Skip to content

Commit db54296

Browse files
committed
wip
1 parent c6dc3da commit db54296

File tree

9 files changed

+242
-18
lines changed

9 files changed

+242
-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/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" />
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// copy from https://github.com/WebAssembly/wasi-libc/blob/230d4be6c54bec93181050f9e25c87150506bdd0/libc-bottom-half/headers/private/wasi/descriptor_table.h
2+
// https://github.com/WebAssembly/wasi-libc/blob/main/LICENSE-MIT
3+
4+
#ifndef DESCRIPTOR_TABLE_H
5+
#define DESCRIPTOR_TABLE_H
6+
7+
#include <wasi/wasip2.h>
8+
9+
typedef struct {
10+
int dummy;
11+
} tcp_socket_state_unbound_t;
12+
typedef struct {
13+
int dummy;
14+
} tcp_socket_state_bound_t;
15+
typedef struct {
16+
int dummy;
17+
} tcp_socket_state_connecting_t;
18+
typedef struct {
19+
int dummy;
20+
} tcp_socket_state_listening_t;
21+
22+
typedef struct {
23+
streams_own_input_stream_t input;
24+
poll_own_pollable_t input_pollable;
25+
streams_own_output_stream_t output;
26+
poll_own_pollable_t output_pollable;
27+
} tcp_socket_state_connected_t;
28+
29+
typedef struct {
30+
network_error_code_t error_code;
31+
} tcp_socket_state_connect_failed_t;
32+
33+
// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
34+
typedef struct {
35+
enum {
36+
TCP_SOCKET_STATE_UNBOUND,
37+
TCP_SOCKET_STATE_BOUND,
38+
TCP_SOCKET_STATE_CONNECTING,
39+
TCP_SOCKET_STATE_CONNECTED,
40+
TCP_SOCKET_STATE_CONNECT_FAILED,
41+
TCP_SOCKET_STATE_LISTENING,
42+
} tag;
43+
union {
44+
tcp_socket_state_unbound_t unbound;
45+
tcp_socket_state_bound_t bound;
46+
tcp_socket_state_connecting_t connecting;
47+
tcp_socket_state_connected_t connected;
48+
tcp_socket_state_connect_failed_t connect_failed;
49+
tcp_socket_state_listening_t listening;
50+
};
51+
} tcp_socket_state_t;
52+
53+
typedef struct {
54+
tcp_own_tcp_socket_t socket;
55+
poll_own_pollable_t socket_pollable;
56+
bool blocking;
57+
bool fake_nodelay;
58+
bool fake_reuseaddr;
59+
network_ip_address_family_t family;
60+
tcp_socket_state_t state;
61+
} tcp_socket_t;
62+
63+
typedef struct {
64+
udp_own_incoming_datagram_stream_t incoming;
65+
poll_own_pollable_t incoming_pollable;
66+
udp_own_outgoing_datagram_stream_t outgoing;
67+
poll_own_pollable_t outgoing_pollable;
68+
} udp_socket_streams_t;
69+
70+
typedef struct {
71+
int dummy;
72+
} udp_socket_state_unbound_t;
73+
typedef struct {
74+
int dummy;
75+
} udp_socket_state_bound_nostreams_t;
76+
77+
typedef struct {
78+
udp_socket_streams_t streams; // Streams have no remote_address
79+
} udp_socket_state_bound_streaming_t;
80+
81+
typedef struct {
82+
udp_socket_streams_t streams; // Streams have a remote_address
83+
} udp_socket_state_connected_t;
84+
85+
// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
86+
// The "bound" state is split up into two distinct tags:
87+
// - "bound_nostreams": Bound, but no datagram streams set up (yet). That will be done the first time send or recv is called.
88+
// - "bound_streaming": Bound with active streams.
89+
typedef struct {
90+
enum {
91+
UDP_SOCKET_STATE_UNBOUND,
92+
UDP_SOCKET_STATE_BOUND_NOSTREAMS,
93+
UDP_SOCKET_STATE_BOUND_STREAMING,
94+
UDP_SOCKET_STATE_CONNECTED,
95+
} tag;
96+
union {
97+
udp_socket_state_unbound_t unbound;
98+
udp_socket_state_bound_nostreams_t bound_nostreams;
99+
udp_socket_state_bound_streaming_t bound_streaming;
100+
udp_socket_state_connected_t connected;
101+
};
102+
} udp_socket_state_t;
103+
104+
typedef struct {
105+
udp_own_udp_socket_t socket;
106+
poll_own_pollable_t socket_pollable;
107+
bool blocking;
108+
network_ip_address_family_t family;
109+
udp_socket_state_t state;
110+
} udp_socket_t;
111+
112+
// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
113+
typedef struct {
114+
enum {
115+
DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET,
116+
DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET,
117+
} tag;
118+
union {
119+
tcp_socket_t tcp_socket;
120+
udp_socket_t udp_socket;
121+
};
122+
} descriptor_table_entry_t;
123+
124+
bool descriptor_table_insert(descriptor_table_entry_t entry, int *fd);
125+
126+
bool descriptor_table_get_ref(int fd, descriptor_table_entry_t **entry);
127+
128+
bool descriptor_table_remove(int fd, descriptor_table_entry_t *entry);
129+
130+
#endif

0 commit comments

Comments
 (0)