Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara committed Sep 11, 2024
1 parent c6dc3da commit db54296
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 18 deletions.
2 changes: 2 additions & 0 deletions eng/testing/tests.wasi.targets
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasm --engine-arg=max-wasm-stack=134217728</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=http</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=inherit-network</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=tcp</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=udp</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=allow-ip-name-lookup</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--env --engine-arg=DOTNET_WASI_PRINT_EXIT_CODE=1</_XHarnessArgs>
<_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli)</_XHarnessArgs>
Expand Down
3 changes: 1 addition & 2 deletions src/libraries/System.Net.Sockets/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<PropertyGroup>
<StrongNameKeyId>Microsoft</StrongNameKeyId>
<IncludePlatformAttributes>true</IncludePlatformAttributes>
<!-- WASI until https:/dotnet/runtime/issues/98957 -->
<UnsupportedOSPlatforms>browser;wasi</UnsupportedOSPlatforms>
<UnsupportedOSPlatforms>browser</UnsupportedOSPlatforms>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'wasi' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
<Compile Include="System\Net\Sockets\SafeSocketHandle.Unix.cs" />
<Compile Include="System\Net\Sockets\Socket.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncContext.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Wasi.cs" Condition="'$(TargetPlatformIdentifier)' == 'wasi'"/>
<Compile Include="System\Net\Sockets\SocketAsyncEventArgs.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketPal.Unix.cs" />
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.Unix.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace System.Net.Sockets
{
internal sealed unsafe class SocketAsyncEngine : IThreadPoolWorkItem
internal sealed unsafe partial class SocketAsyncEngine : IThreadPoolWorkItem
{
private const int EventBufferCount =
#if DEBUG
Expand Down Expand Up @@ -143,6 +143,7 @@ public void UnregisterSocket(IntPtr socketHandle)
_handleToContextMap.TryRemove(socketHandle, out _);
}

#if !TARGET_WASI
private SocketAsyncEngine()
{
_port = (IntPtr)(-1);
Expand All @@ -161,9 +162,11 @@ private SocketAsyncEngine()
}
}

Console.WriteLine("SocketAsyncEngine C");

Check failure on line 165 in src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs

View check run for this annotation

Azure Pipelines / runtime (Build linux-x64 checked CLR_Tools_Tests)

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

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs(165,21): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'Console' does not exist in the current context
fixed (Interop.Sys.SocketEvent** bufferPtr = &_buffer)
{
err = Interop.Sys.CreateSocketEventBuffer(EventBufferCount, bufferPtr);
Console.WriteLine("SocketAsyncEngine D");

Check failure on line 169 in src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs

View check run for this annotation

Azure Pipelines / runtime (Build linux-x64 checked CLR_Tools_Tests)

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

src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs(169,21): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'Console' does not exist in the current context
if (err != Interop.Error.SUCCESS)
{
throw new InternalException(err);
Expand Down Expand Up @@ -217,6 +220,7 @@ private void EventLoop()
Environment.FailFast("Exception thrown from SocketAsyncEngine event loop: " + e.ToString(), e);
}
}
#endif

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

#if !TARGET_WASI
private void FreeNativeResources()
{
if (_buffer != null)
Expand All @@ -314,6 +319,7 @@ private void FreeNativeResources()
Interop.Sys.CloseSocketEventPort(_port);
}
}
#endif // !TARGET_WASI

// The JIT is allowed to arbitrarily extend the lifetime of locals, which may retain SocketAsyncContext references,
// indirectly preventing Socket instances to be finalized, despite being no longer referenced by user code.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace System.Net.Sockets
{
internal sealed partial class SocketAsyncEngine : IThreadPoolWorkItem
{
private SocketAsyncEngine()
{
#pragma warning disable CS4014
WasiSocketsEventLoop();
#pragma warning restore CS4014
}

private async Task WasiSocketsEventLoop()
{
try
{
SocketEventHandler handler = new SocketEventHandler(this);
while (true)
{
int numEvents = EventBufferCount;
await WaitForAnySocketPollable().ConfigureAwait(false);

// The native shim is responsible for ensuring this condition.
Debug.Assert(numEvents > 0, $"Unexpected numEvents: {numEvents}");

// Only enqueue a work item if the stage is NotScheduled.
// Otherwise there must be a work item already queued or another thread already handling parallelization.
if (handler.HandleSocketEvents(numEvents) &&
Interlocked.Exchange(
ref _eventQueueProcessingStage,
EventQueueProcessingStage.Scheduled) == EventQueueProcessingStage.NotScheduled)
{
ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false);
}
}
}
catch (Exception e)
{
Environment.FailFast("Exception thrown from SocketAsyncEngine event loop: " + e.ToString(), e);
}
}

#pragma warning disable CA1822 // TODO remove
private Task WaitForAnySocketPollable()
{
TaskCompletionSource todo = new TaskCompletionSource();
/* TODO something like this, but with handle->pollable conversion
Interop.Error err = Interop.Sys.WaitForSocketEvents(_port, handler.Buffer, &numEvents);
if (err != Interop.Error.SUCCESS)
{
throw new InternalException(err);
}*/
return todo.Task;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, IL
int startIndex = bufferIndex, startOffset = offset;

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

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

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

int buffersCount = buffers.Count;
#if TARGET_WASI // WASI doesn't have iovecs and sendmsg in preview2
buffersCount = Math.Max(buffersCount, 1);
#endif

bool allocOnStack = buffersCount <= IovStackThreshold;
Span<GCHandle> handles = allocOnStack ? stackalloc GCHandle[IovStackThreshold] : new GCHandle[buffersCount];
Span<Interop.Sys.IOVector> iovecs = allocOnStack ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[buffersCount];
Expand All @@ -554,17 +564,21 @@ private static unsafe int SysReceiveMessageFrom(
fixed (byte* sockAddr = socketAddress)
fixed (Interop.Sys.IOVector* iov = iovecs)
{
#if !TARGET_WASI // WASI doesn't have msg_control and sendmsg in preview2
int cmsgBufferLen = Interop.Sys.GetControlMessageBufferSize(Convert.ToInt32(isIPv4), Convert.ToInt32(isIPv6));
byte* cmsgBuffer = stackalloc byte[cmsgBufferLen];
#endif

var messageHeader = new Interop.Sys.MessageHeader
{
SocketAddress = sockAddr,
SocketAddressLen = socketAddress.Length,
IOVectors = iov,
IOVectorCount = iovCount,
#if !TARGET_WASI // WASI doesn't have msg_control and sendmsg in preview2
ControlBuffer = cmsgBuffer,
ControlBufferLen = cmsgBufferLen
#endif
};

long received = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-wasi</TargetFrameworks>
<IgnoreForCI Condition="'$(TargetOS)' == 'browser'">true</IgnoreForCI>
<EventSourceSupport Condition="'$(TestNativeAot)' == 'true'">true</EventSourceSupport>
<XunitShowProgress Condition="'$(TargetOS)' == 'wasi'">true</XunitShowProgress>
</PropertyGroup>
<ItemGroup>
<Compile Include="Accept.cs" />
Expand Down
130 changes: 130 additions & 0 deletions src/mono/wasi/mono-include/descriptor_table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// copy from https:/WebAssembly/wasi-libc/blob/230d4be6c54bec93181050f9e25c87150506bdd0/libc-bottom-half/headers/private/wasi/descriptor_table.h
// https:/WebAssembly/wasi-libc/blob/main/LICENSE-MIT

#ifndef DESCRIPTOR_TABLE_H
#define DESCRIPTOR_TABLE_H

#include <wasi/wasip2.h>

typedef struct {
int dummy;
} tcp_socket_state_unbound_t;
typedef struct {
int dummy;
} tcp_socket_state_bound_t;
typedef struct {
int dummy;
} tcp_socket_state_connecting_t;
typedef struct {
int dummy;
} tcp_socket_state_listening_t;

typedef struct {
streams_own_input_stream_t input;
poll_own_pollable_t input_pollable;
streams_own_output_stream_t output;
poll_own_pollable_t output_pollable;
} tcp_socket_state_connected_t;

typedef struct {
network_error_code_t error_code;
} tcp_socket_state_connect_failed_t;

// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
typedef struct {
enum {
TCP_SOCKET_STATE_UNBOUND,
TCP_SOCKET_STATE_BOUND,
TCP_SOCKET_STATE_CONNECTING,
TCP_SOCKET_STATE_CONNECTED,
TCP_SOCKET_STATE_CONNECT_FAILED,
TCP_SOCKET_STATE_LISTENING,
} tag;
union {
tcp_socket_state_unbound_t unbound;
tcp_socket_state_bound_t bound;
tcp_socket_state_connecting_t connecting;
tcp_socket_state_connected_t connected;
tcp_socket_state_connect_failed_t connect_failed;
tcp_socket_state_listening_t listening;
};
} tcp_socket_state_t;

typedef struct {
tcp_own_tcp_socket_t socket;
poll_own_pollable_t socket_pollable;
bool blocking;
bool fake_nodelay;
bool fake_reuseaddr;
network_ip_address_family_t family;
tcp_socket_state_t state;
} tcp_socket_t;

typedef struct {
udp_own_incoming_datagram_stream_t incoming;
poll_own_pollable_t incoming_pollable;
udp_own_outgoing_datagram_stream_t outgoing;
poll_own_pollable_t outgoing_pollable;
} udp_socket_streams_t;

typedef struct {
int dummy;
} udp_socket_state_unbound_t;
typedef struct {
int dummy;
} udp_socket_state_bound_nostreams_t;

typedef struct {
udp_socket_streams_t streams; // Streams have no remote_address
} udp_socket_state_bound_streaming_t;

typedef struct {
udp_socket_streams_t streams; // Streams have a remote_address
} udp_socket_state_connected_t;

// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
// The "bound" state is split up into two distinct tags:
// - "bound_nostreams": Bound, but no datagram streams set up (yet). That will be done the first time send or recv is called.
// - "bound_streaming": Bound with active streams.
typedef struct {
enum {
UDP_SOCKET_STATE_UNBOUND,
UDP_SOCKET_STATE_BOUND_NOSTREAMS,
UDP_SOCKET_STATE_BOUND_STREAMING,
UDP_SOCKET_STATE_CONNECTED,
} tag;
union {
udp_socket_state_unbound_t unbound;
udp_socket_state_bound_nostreams_t bound_nostreams;
udp_socket_state_bound_streaming_t bound_streaming;
udp_socket_state_connected_t connected;
};
} udp_socket_state_t;

typedef struct {
udp_own_udp_socket_t socket;
poll_own_pollable_t socket_pollable;
bool blocking;
network_ip_address_family_t family;
udp_socket_state_t state;
} udp_socket_t;

// This is a tagged union. When adding/removing/renaming cases, be sure to keep the tag and union definitions in sync.
typedef struct {
enum {
DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET,
DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET,
} tag;
union {
tcp_socket_t tcp_socket;
udp_socket_t udp_socket;
};
} descriptor_table_entry_t;

bool descriptor_table_insert(descriptor_table_entry_t entry, int *fd);

bool descriptor_table_get_ref(int fd, descriptor_table_entry_t **entry);

bool descriptor_table_remove(int fd, descriptor_table_entry_t *entry);

#endif
Loading

0 comments on commit db54296

Please sign in to comment.