Skip to content

[browser] native - no sockets, no threads, no blocking#123962

Draft
pavelsavara wants to merge 26 commits intodotnet:mainfrom
pavelsavara:browser_no_sockets
Draft

[browser] native - no sockets, no threads, no blocking#123962
pavelsavara wants to merge 26 commits intodotnet:mainfrom
pavelsavara:browser_no_sockets

Conversation

@pavelsavara
Copy link
Member

@pavelsavara pavelsavara commented Feb 3, 2026

Fixes #122506

@pavelsavara pavelsavara added this to the 11.0.0 milestone Feb 3, 2026
@pavelsavara pavelsavara self-assigned this Feb 3, 2026
@pavelsavara pavelsavara added arch-wasm WebAssembly architecture area-System.Net.Sockets os-browser Browser variant of arch-wasm labels Feb 3, 2026
Copilot AI review requested due to automatic review settings February 3, 2026 20:38
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for the browser (WASM) target by providing stub implementations for features not available in the browser environment, specifically: no sockets, no threads, and no blocking operations.

Changes:

  • Added browser-specific stub implementations for networking, interface addresses, network statistics, and console APIs
  • Updated error messages in WASM-specific files from "WASI" to "WASM" for clarity
  • Modified CoreCLR PAL to prevent blocking operations and thread suspension in single-threaded WASM
  • Updated build configuration to disable socket support for browser targets and include browser-specific source files
  • Added perftracing configuration for browser builds

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/native/libs/System.Native/pal_signal_wasm.c Updated error messages from "WASI" to "WASM"
src/native/libs/System.Native/pal_networkstatistics_wasm.c New file with stub implementations returning "not supported" for network statistics APIs
src/native/libs/System.Native/pal_networking_browser.c New file with comprehensive stub implementations for all networking APIs, returning ENOTSUP
src/native/libs/System.Native/pal_io.c Added early return for pipe() on WASM targets with ENOTSUP
src/native/libs/System.Native/pal_interfaceaddresses_browser.c New file with stub implementations for interface enumeration APIs
src/native/libs/System.Native/pal_dynamicload_wasm.c Updated error messages from "WASI" to "WASM"
src/native/libs/System.Native/pal_console_wasm.c New console implementation for WASM with some working features (stdin/isatty) and stubs for unsupported features
src/native/libs/System.Native/CMakeLists.txt Updated build configuration to differentiate between browser and WASI targets, using browser-specific source files
src/native/eventpipe/ds-ipc-pal-websocket.c Added string duplication helper functions for eventpipe
src/mono/mono/profiler/log.c Changed sys/socket.h include guard from HOST_WIN32 to HAVE_SYS_SOCKET_H
src/mono/mono/profiler/helper.c Changed sys/socket.h include guard from HOST_WIN32 to HAVE_SYS_SOCKET_H
src/mono/mono/profiler/CMakeLists.txt Disabled AOT profiler for browser targets
src/mono/mono/mini/cfgdump.c Added guard for sys/socket.h include
src/mono/browser/runtime/dotnet.d.ts Formatting fix: removed space before parentheses in function signatures
src/coreclr/pal/src/thread/threadsusp.cpp Added guard to prevent thread suspension in single-threaded WASM
src/coreclr/pal/src/thread/thread.cpp Added guard to prevent CREATE_SUSPENDED flag in single-threaded WASM
src/coreclr/pal/src/synchmgr/wait.cpp Added guards to prevent blocking waits and made Sleep return immediately in WASM
src/coreclr/pal/src/synchmgr/synchmanager.cpp Added assertion to prevent waiting on non-signaled objects in WASM
src/coreclr/pal/src/debug/debug.cpp Added WASM-specific implementation of PAL_ProbeMemory using emscripten heap size
eng/native/tryrun.browser.cmake Disabled HAVE_SYS_SOCKET_H for browser targets
eng/native.wasm.targets Added perftracing configuration flags for browser builds

@pavelsavara
Copy link
Member Author

Whole this PR makes emscripten emulator smaller only by 20KB of JavaScript - from 154KB to 134KB.
And only 4KB of .wasm with -O3. Those are CoreCLR numbers, it will have similar impact for Mono but I have not measured it yet.

I hoped for more, I'm not sure I want to merge this...

@pavelsavara
Copy link
Member Author

These are JS emulators of mmap that are slow and terrible, we should not use them in CoreCLR

adding _mmap_js (referenced by root reference (e.g. compiled C/C++ code))
adding _msync_js (referenced by root reference (e.g. compiled C/C++ code))
adding _munmap_js (referenced by root reference (e.g. compiled C/C++ code))

The rest of the JS support is OK. See https://gist.github.com/pavelsavara/f11dd1078548a16fc923de444f1c9d9b

All the symbols in the wasm files are https://gist.github.com/pavelsavara/b7653066a83554168739558630d34847

Among them these caught my eye

GetNativeSwiftPhysicalLowering

GetThreadStaticsByIndex
SwitchStackAndExecuteHandler
Thread::HandleThreadAbort
ThreadNative_Abort
ThreadNative_ResetAbort
ThreadNative_Start
ThreadSuspend::LockThreadStore

WKS::gc_heap and SVR::gc_heap

COMDouble
COMSingle
COMDelegate
OleVariant
RealCOMPlusThrow

@jkotas
Copy link
Member

jkotas commented Feb 4, 2026

These are JS emulators of mmap that are slow and terrible, we should not use them in CoreCLR

#117813 is the main offender.

Copilot AI review requested due to automatic review settings February 4, 2026 16:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated 7 comments.

Copilot AI review requested due to automatic review settings February 5, 2026 13:35
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 4 comments.

Copilot AI review requested due to automatic review settings February 6, 2026 12:27
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated 6 comments.

Comment on lines +9 to +22

#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

// Error codes from pal_errno.h
#define Error_ENOTSUP 95
#define Error_EFAULT 14
#define Error_EAFNOSUPPORT 97
#define Error_EINVAL 22
#define Error_EBADF 9
#define Error_SUCCESS 0

Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Error_* constants are hard-coded to raw Unix errno values (e.g., ENOTSUP=95, EFAULT=14). System.Native APIs are expected to return PAL Error enum values (see pal_error_common.h), which are different (e.g., Error_ENOTSUP is 0x1003D). Returning raw errno values here will be misinterpreted by managed code. Use the shared PAL Error constants (include pal_errno.h / pal_error_common.h) instead of redefining them.

Suggested change
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
// Error codes from pal_errno.h
#define Error_ENOTSUP 95
#define Error_EFAULT 14
#define Error_EAFNOSUPPORT 97
#define Error_EINVAL 22
#define Error_EBADF 9
#define Error_SUCCESS 0
#include "pal_errno.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

Copilot uses AI. Check for mistakes.
Comment on lines +660 to +680
if (createdSocket == NULL)
{
return Error_EFAULT;
}

// Allocate fake socket structure
FakeSocket* fs = (FakeSocket*)malloc(sizeof(FakeSocket));
if (fs == NULL)
{
*createdSocket = -1;
return Error_EFAULT;
}

fs->addressFamily = addressFamily;
fs->socketType = socketType;
fs->protocolType = protocolType;
fs->isListening = 0;
fs->magic = FAKE_SOCKET_MAGIC;

*createdSocket = (intptr_t)fs;
return Error_SUCCESS;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SystemNative_Socket returns a malloc'ed pointer cast to intptr_t as the "socket" handle. In the rest of System.Native (and managed code), sockets are treated as OS file descriptors (see ToFileDescriptor casts/asserts). Returning a pointer here risks invalid close()/fcntl usage (pointer truncation) and makes resource lifetime undefined (e.g., only freed in SystemNative_Shutdown). Consider returning Error_ENOTSUP (and setting *createdSocket = -1) so callers fail predictably, or ensure the handle is a real fd compatible with the existing PAL contract.

Suggested change
if (createdSocket == NULL)
{
return Error_EFAULT;
}
// Allocate fake socket structure
FakeSocket* fs = (FakeSocket*)malloc(sizeof(FakeSocket));
if (fs == NULL)
{
*createdSocket = -1;
return Error_EFAULT;
}
fs->addressFamily = addressFamily;
fs->socketType = socketType;
fs->protocolType = protocolType;
fs->isListening = 0;
fs->magic = FAKE_SOCKET_MAGIC;
*createdSocket = (intptr_t)fs;
return Error_SUCCESS;
(void)addressFamily;
(void)socketType;
(void)protocolType;
if (createdSocket == NULL)
{
return Error_EFAULT;
}
*createdSocket = -1;
return Error_ENOTSUP;

Copilot uses AI. Check for mistakes.
Comment on lines +624 to +656
int32_t SystemNative_SetSockOpt(
intptr_t socket, int32_t socketOptionLevel, int32_t socketOptionName, uint8_t* optionValue, int32_t optionLen)
{
FakeSocket* fs = GetFakeSocket(socket);
if (fs == NULL)
{
return Error_EBADF;
}

(void)socketOptionLevel;
(void)socketOptionName;
(void)optionValue;
(void)optionLen;
// Pretend setting options succeeds
return Error_SUCCESS;
}

int32_t SystemNative_SetRawSockOpt(
intptr_t socket, int32_t socketOptionLevel, int32_t socketOptionName, uint8_t* optionValue, int32_t optionLen)
{
FakeSocket* fs = GetFakeSocket(socket);
if (fs == NULL)
{
return Error_EBADF;
}

(void)socketOptionLevel;
(void)socketOptionName;
(void)optionValue;
(void)optionLen;
// Pretend setting options succeeds
return Error_SUCCESS;
}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SystemNative_SetSockOpt / SystemNative_SetRawSockOpt currently return success unconditionally for a fake socket. If socket options are not actually supported in the browser target, reporting success can cause managed code to continue under incorrect assumptions. Prefer returning Error_ENOTSUP (or only succeeding for an explicitly documented no-op subset) to keep behavior consistent with other unsupported networking APIs in this file.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +31
int32_t SystemNative_GetTcpGlobalStatistics(TcpGlobalStatistics* retStats)
{
if (retStats == NULL)
{
return -1;
}
memset(retStats, 0, sizeof(TcpGlobalStatistics));
return -1; // Not supported
}

int32_t SystemNative_GetIPv4GlobalStatistics(IPv4GlobalStatistics* retStats)
{
if (retStats == NULL)
{
return -1;
}
memset(retStats, 0, sizeof(IPv4GlobalStatistics));
return -1; // Not supported
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These stubs return -1 for unsupported operations but don't set errno (ENOTSUP/EFAULT). The existing fallback implementations in pal_networkstatistics.c set errno=ENOTSUP before returning -1, and managed callers may rely on errno/GetLastPInvokeError for exception details. Set errno appropriately on all failure returns (including the NULL retStats case).

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +91
return 0;
}

int32_t SystemNative_GetActiveTcpConnectionInfos(NativeTcpConnectionInformation* infos, int32_t* infoCount)
{
(void)infos;
if (infoCount != NULL)
{
*infoCount = 0;
}
return 0;
}

int32_t SystemNative_GetEstimatedUdpListenerCount(void)
{
return 0;
}

int32_t SystemNative_GetActiveUdpListeners(IPEndPointInfo* infos, int32_t* infoCount)
{
(void)infos;
if (infoCount != NULL)
{
*infoCount = 0;
}
return 0;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "estimated/active" count APIs return 0 (success) in this stub implementation. On other platforms, when network statistics aren't supported, these return -1 with errno=ENOTSUP (see pal_networkstatistics.c fallback). Returning 0 changes semantics from "not supported" to "supported with empty data", which can lead to incorrect behavior in managed code. Align these with the existing not-supported contract (return -1 and set errno=ENOTSUP).

Suggested change
return 0;
}
int32_t SystemNative_GetActiveTcpConnectionInfos(NativeTcpConnectionInformation* infos, int32_t* infoCount)
{
(void)infos;
if (infoCount != NULL)
{
*infoCount = 0;
}
return 0;
}
int32_t SystemNative_GetEstimatedUdpListenerCount(void)
{
return 0;
}
int32_t SystemNative_GetActiveUdpListeners(IPEndPointInfo* infos, int32_t* infoCount)
{
(void)infos;
if (infoCount != NULL)
{
*infoCount = 0;
}
return 0;
errno = ENOTSUP;
return -1;
}
int32_t SystemNative_GetActiveTcpConnectionInfos(NativeTcpConnectionInformation* infos, int32_t* infoCount)
{
(void)infos;
(void)infoCount;
errno = ENOTSUP;
return -1;
}
int32_t SystemNative_GetEstimatedUdpListenerCount(void)
{
errno = ENOTSUP;
return -1;
}
int32_t SystemNative_GetActiveUdpListeners(IPEndPointInfo* infos, int32_t* infoCount)
{
(void)infos;
(void)infoCount;
errno = ENOTSUP;
return -1;

Copilot uses AI. Check for mistakes.
int32_t SystemNative_GetNetworkInterfaces(int32_t* interfaceCount, NetworkInterfaceInfo** interfaces, int32_t* addressCount, IpAddressInfo** addressList)
{
if (interfaceCount == NULL || interfaces == NULL || addressCount == NULL || addressList == NULL)
{
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When argument validation fails (returning -1), this stub doesn't set errno. Other System.Native APIs typically set errno (e.g., EFAULT) to enable managed code to surface a meaningful P/Invoke error. Set errno=EFAULT (or another appropriate value) before returning -1 for invalid pointer arguments.

Suggested change
{
{
errno = EFAULT;

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 6, 2026 14:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-System.Net.Sockets os-browser Browser variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[browser][coreCLR] cleanup PAL

3 participants