Skip to content

Commit 79c854b

Browse files
committed
sockets for wasi-libc
1 parent 621dea0 commit 79c854b

4 files changed

Lines changed: 89 additions & 7 deletions

File tree

src/libraries/Common/src/Interop/Wasi/System.Native/Interop.SocketEvent.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ internal enum SocketEvents : int
2020
}
2121

2222
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetWasiSocketDescriptor")]
23-
internal static unsafe partial Error GetWasiSocketDescriptor(IntPtr socket, IntPtr* entry);
23+
internal static unsafe partial Error GetWasiSocketDescriptor(IntPtr socket, IntPtr* entry, int* socketType);
24+
25+
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_WasiSubscribeSocketPollable")]
26+
internal static partial int WasiSubscribeSocketPollable(int kind, int handle);
2427
}
2528
}

src/native/libs/System.Native/entrypoints.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ static const Entry s_sysNative[] =
196196
DllImportEntry(SystemNative_TryChangeSocketEventRegistration)
197197
DllImportEntry(SystemNative_WaitForSocketEvents)
198198
DllImportEntry(SystemNative_GetWasiSocketDescriptor)
199+
DllImportEntry(SystemNative_WasiSubscribeSocketPollable)
199200
DllImportEntry(SystemNative_PlatformSupportsDualModeIPv4PacketInfo)
200201
DllImportEntry(SystemNative_GetDomainSocketSizes)
201202
DllImportEntry(SystemNative_GetMaximumAddressSize)

src/native/libs/System.Native/pal_networking.c

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,34 +3462,110 @@ static int32_t WaitForSocketEventsInner(int32_t port, SocketEvent* buffer, int32
34623462

34633463
#if defined(TARGET_WASI)
34643464
// from https://github.com/WebAssembly/wasi-libc/blob/161b3195fc25/libc-bottom-half/headers/private/wasi/descriptor_table.h
3465+
// The descriptor table entry is a "fat pointer":
3466+
// typedef struct { void* data; descriptor_vtable_t* vtable; } descriptor_table_entry_t;
3467+
// where `data` points to the descriptor-specific state (a tcp_socket_t* or udp_socket_t*).
34653468
void* descriptor_table_get_ref(int fd);
34663469

34673470
// this method is invading private implementation details of wasi-libc
34683471
// we could get rid of it when https://github.com/WebAssembly/wasi-libc/issues/542 is resolved
34693472
// or after WASIp3 promises are implemented, whatever comes first
3470-
int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry)
3473+
//
3474+
// Returns the descriptor-specific `data` pointer in *entry and the kind of socket in
3475+
// *socketType (1 = TCP/stream, 2 = UDP/datagram, 0 = unknown). The vtable that identifies
3476+
// the socket kind is a private static symbol in wasi-libc, so we discriminate via SO_TYPE.
3477+
int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry, int32_t* socketType)
34713478
{
3472-
if (entry == NULL)
3479+
if (entry == NULL || socketType == NULL)
34733480
{
34743481
return Error_EFAULT;
34753482
}
34763483

34773484
int fd = ToFileDescriptor(socket);
3478-
void* ref = descriptor_table_get_ref(fd);
3485+
// The returned pointer is a descriptor_table_entry_t*; its first word is the `data` pointer.
3486+
void** ref = (void**)descriptor_table_get_ref(fd);
34793487
if (ref == NULL)
34803488
{
34813489
return Error_EFAULT;
34823490
}
3483-
*entry = ref;
3491+
*entry = ref[0];
3492+
3493+
int type = 0;
3494+
socklen_t length = sizeof(type);
3495+
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &length) != 0)
3496+
{
3497+
return Error_EFAULT;
3498+
}
3499+
3500+
if (type == SOCK_STREAM)
3501+
{
3502+
*socketType = 1;
3503+
}
3504+
else if (type == SOCK_DGRAM)
3505+
{
3506+
*socketType = 2;
3507+
}
3508+
else
3509+
{
3510+
*socketType = 0;
3511+
}
3512+
34843513
return Error_SUCCESS;
34853514
}
3515+
3516+
// In the new wasi-libc descriptor-table design, the pollables embedded in the socket state
3517+
// (socket_pollable / input_pollable / output_pollable / incoming_pollable / outgoing_pollable)
3518+
// are created lazily: their handle is 0 until the corresponding `subscribe` import is called.
3519+
// The managed event loop needs the actual pollable handle to merge it into wasi:io/poll.poll,
3520+
// so it asks us to lazily subscribe when it observes a 0 handle.
3521+
//
3522+
// All of the wasi component-model handle types are ABI-identical: a struct wrapping a single
3523+
// int32_t handle, passed and returned directly. We mirror that with WasiPollHandle_t so we can
3524+
// call the (private) wasi-libc subscribe imports without pulling in the generated headers.
3525+
typedef struct { int32_t __handle; } WasiPollHandle_t;
3526+
extern WasiPollHandle_t streams_method_input_stream_subscribe(WasiPollHandle_t self);
3527+
extern WasiPollHandle_t streams_method_output_stream_subscribe(WasiPollHandle_t self);
3528+
extern WasiPollHandle_t tcp_method_tcp_socket_subscribe(WasiPollHandle_t self);
3529+
extern WasiPollHandle_t udp_method_udp_socket_subscribe(WasiPollHandle_t self);
3530+
extern WasiPollHandle_t udp_method_incoming_datagram_stream_subscribe(WasiPollHandle_t self);
3531+
extern WasiPollHandle_t udp_method_outgoing_datagram_stream_subscribe(WasiPollHandle_t self);
3532+
3533+
// kind: 0 = input-stream, 1 = output-stream, 2 = tcp-socket, 3 = udp-socket,
3534+
// 4 = incoming-datagram-stream, 5 = outgoing-datagram-stream
3535+
// `handle` is the borrowed stream/socket handle read from the socket state. Returns the newly
3536+
// created pollable handle (the caller stores it back into the socket state so wasi-libc owns
3537+
// and eventually drops it), or 0 for an unknown kind.
3538+
int32_t SystemNative_WasiSubscribeSocketPollable(int32_t kind, int32_t handle)
3539+
{
3540+
WasiPollHandle_t self = { handle };
3541+
WasiPollHandle_t pollable;
3542+
switch (kind)
3543+
{
3544+
case 0: pollable = streams_method_input_stream_subscribe(self); break;
3545+
case 1: pollable = streams_method_output_stream_subscribe(self); break;
3546+
case 2: pollable = tcp_method_tcp_socket_subscribe(self); break;
3547+
case 3: pollable = udp_method_udp_socket_subscribe(self); break;
3548+
case 4: pollable = udp_method_incoming_datagram_stream_subscribe(self); break;
3549+
case 5: pollable = udp_method_outgoing_datagram_stream_subscribe(self); break;
3550+
default: return 0;
3551+
}
3552+
return pollable.__handle;
3553+
}
34863554
#else
3487-
int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry)
3555+
int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry, int32_t* socketType)
34883556
{
34893557
(void)socket;
34903558
(void)entry;
3559+
(void)socketType;
34913560
return Error_ENOSYS;
34923561
}
3562+
3563+
int32_t SystemNative_WasiSubscribeSocketPollable(int32_t kind, int32_t handle)
3564+
{
3565+
(void)kind;
3566+
(void)handle;
3567+
return 0;
3568+
}
34933569
#endif // TARGET_WASI
34943570

34953571
int32_t SystemNative_CreateSocketEventPort(intptr_t* port)

src/native/libs/System.Native/pal_networking.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,9 @@ PALEXPORT int32_t SystemNative_GetAtOutOfBandMark(intptr_t socket, int32_t* avai
405405

406406
PALEXPORT int32_t SystemNative_GetBytesAvailable(intptr_t socket, int32_t* available);
407407

408-
PALEXPORT int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry);
408+
PALEXPORT int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry, int32_t* socketType);
409+
410+
PALEXPORT int32_t SystemNative_WasiSubscribeSocketPollable(int32_t kind, int32_t handle);
409411

410412
PALEXPORT int32_t SystemNative_CreateSocketEventPort(intptr_t* port);
411413

0 commit comments

Comments
 (0)