mirror of
https://github.com/neovim/neovim.git
synced 2026-08-14 03:04:54 +00:00
build(wasm): update wasm_stubs with browser libuv polling support
Replace the placeholder polling implementation with a browser-backed libuv polling backend and add the required platform compatibility stubs for the WebAssembly build.
This commit is contained in:
215
src/wasm_stubs.c
215
src/wasm_stubs.c
@@ -1,5 +1,8 @@
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/threading.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -9,6 +12,23 @@
|
||||
#include <time.h>
|
||||
#include <uv.h>
|
||||
|
||||
#ifndef POLLIN
|
||||
# define POLLIN 0x001
|
||||
#endif
|
||||
#ifndef POLLOUT
|
||||
# define POLLOUT 0x004
|
||||
#endif
|
||||
#ifndef POLLERR
|
||||
# define POLLERR 0x008
|
||||
#endif
|
||||
#ifndef POLLHUP
|
||||
# define POLLHUP 0x010
|
||||
#endif
|
||||
|
||||
void uv__io_cb(uv_loop_t *loop, uv__io_t *w, unsigned events);
|
||||
|
||||
// System info stubs
|
||||
|
||||
uint64_t uv_get_free_memory(void)
|
||||
{
|
||||
return 8 * 1024 * 1024;
|
||||
@@ -26,7 +46,6 @@ uint64_t uv_get_constrained_memory(void)
|
||||
return uv_get_total_memory();
|
||||
}
|
||||
|
||||
// Report an idle virtual system
|
||||
void uv_loadavg(double avg[3])
|
||||
{
|
||||
avg[0] = 0.0; avg[1] = 0.0; avg[2] = 0.0;
|
||||
@@ -46,7 +65,7 @@ int uv_resident_set_memory(size_t *rss)
|
||||
if (!rss) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
*rss = uv_get_total_memory() / 2;
|
||||
*rss = uv_get_total_memory() / 2; // fake value
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -67,13 +86,8 @@ int uv_exepath(char *buffer, size_t *size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv__io_fork(uv_loop_t *loop)
|
||||
{
|
||||
(void)loop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t * *cpu_infos, int *count)
|
||||
// Browsers have no native CPU information. So return a single virtual CPU
|
||||
int uv_cpu_info(uv_cpu_info_t **cpu_infos, int *count)
|
||||
{
|
||||
if (!cpu_infos || !count) {
|
||||
return UV_EINVAL;
|
||||
@@ -92,7 +106,8 @@ int uv_cpu_info(uv_cpu_info_t * *cpu_infos, int *count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv_interface_addresses(uv_interface_address_t * *addresses, int *count)
|
||||
// Browsers do not expose host network interfaces. Report only loopback.
|
||||
int uv_interface_addresses(uv_interface_address_t **addresses, int *count)
|
||||
{
|
||||
if (!addresses || !count) {
|
||||
return UV_EINVAL;
|
||||
@@ -115,25 +130,138 @@ int uv_interface_addresses(uv_interface_address_t * *addresses, int *count)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t *loop, int fd)
|
||||
#define UV_BROWSER_MAX_FD 256
|
||||
|
||||
typedef struct {
|
||||
_Atomic uint32_t generation; // woken on any fd state change
|
||||
_Atomic int32_t readable[UV_BROWSER_MAX_FD];
|
||||
_Atomic int32_t writable[UV_BROWSER_MAX_FD];
|
||||
} uv_browser_shared_state_t;
|
||||
|
||||
static uv_browser_shared_state_t g_shared_state;
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void *uv_browser_get_shared_state_ptr(void)
|
||||
{
|
||||
(void)loop; (void)fd;
|
||||
}
|
||||
int uv__io_check_fd(uv_loop_t *loop, void *w)
|
||||
{
|
||||
(void)loop; (void)w; return 0;
|
||||
return (void *)&g_shared_state;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void uv_browser_set_readable(int fd, int32_t nbytes)
|
||||
{
|
||||
if (fd < 0 || fd >= UV_BROWSER_MAX_FD) {
|
||||
return;
|
||||
}
|
||||
atomic_store(&g_shared_state.readable[fd], nbytes);
|
||||
atomic_fetch_add(&g_shared_state.generation, 1);
|
||||
emscripten_futex_wake(&g_shared_state.generation, 1);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void uv_browser_set_writable(int fd, int32_t nbytes)
|
||||
{
|
||||
if (fd < 0 || fd >= UV_BROWSER_MAX_FD) {
|
||||
return;
|
||||
}
|
||||
atomic_store(&g_shared_state.writable[fd], nbytes);
|
||||
atomic_fetch_add(&g_shared_state.generation, 1);
|
||||
emscripten_futex_wake(&g_shared_state.generation, 1);
|
||||
}
|
||||
|
||||
static int uv_browser_scan_ready(uv_loop_t *loop, unsigned *out_idx, int *out_revents)
|
||||
{
|
||||
unsigned i;
|
||||
uv__io_t *w;
|
||||
int revents;
|
||||
|
||||
for (i = 0; i < loop->nwatchers && i < UV_BROWSER_MAX_FD; i++) {
|
||||
w = loop->watchers[i];
|
||||
if (w == NULL || w->pevents == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
revents = 0;
|
||||
if ((w->pevents & POLLIN) && atomic_load(&g_shared_state.readable[i]) > 0) {
|
||||
revents |= POLLIN;
|
||||
}
|
||||
if ((w->pevents & POLLOUT) && atomic_load(&g_shared_state.writable[i]) > 0) {
|
||||
revents |= POLLOUT;
|
||||
}
|
||||
|
||||
if (revents) {
|
||||
*out_idx = i;
|
||||
*out_revents = revents;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Replaces the kernel poll() call
|
||||
void uv__io_poll(uv_loop_t *loop, int timeout)
|
||||
{
|
||||
if (timeout > 0) {
|
||||
emscripten_sleep(timeout);
|
||||
uv_update_time(loop);
|
||||
emscripten_sleep(10);
|
||||
uv_update_time(loop);
|
||||
} else if (timeout == 0) {
|
||||
emscripten_sleep(0);
|
||||
double deadline;
|
||||
double remaining;
|
||||
uint32_t g0;
|
||||
unsigned idx;
|
||||
int revents;
|
||||
int have_signal;
|
||||
uv__io_t *w;
|
||||
|
||||
deadline = (timeout < 0) ? -1.0 : (emscripten_get_now() + (double)timeout);
|
||||
have_signal = 0;
|
||||
|
||||
while (true) {
|
||||
/* Any state change that lands between this load and the scan below is still caught by the scan
|
||||
itself. Any change that lands after the scan will bump generation past g0, so the futex_wait call
|
||||
falls through immediately instead of sleeping through it. This closes the narrow window where a notify
|
||||
could otherwise land between "nothing is ready" and "waiting". */
|
||||
|
||||
g0 = atomic_load(&g_shared_state.generation);
|
||||
|
||||
if (uv_browser_scan_ready(loop, &idx, &revents)) {
|
||||
have_signal = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (deadline >= 0.0) {
|
||||
remaining = deadline - emscripten_get_now();
|
||||
if (remaining <= 0.0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
remaining = 60000.0;
|
||||
}
|
||||
|
||||
int rc = emscripten_futex_wait(&g_shared_state.generation, g0, remaining);
|
||||
}
|
||||
|
||||
uv_update_time(loop);
|
||||
|
||||
if (!have_signal) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < loop->nwatchers && idx < UV_BROWSER_MAX_FD; idx++) {
|
||||
w = loop->watchers[idx];
|
||||
if (w == NULL || w->pevents == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
revents = 0;
|
||||
if ((w->pevents & POLLIN) && atomic_load(&g_shared_state.readable[idx]) > 0) {
|
||||
revents |= POLLIN;
|
||||
}
|
||||
if ((w->pevents & POLLOUT) && atomic_load(&g_shared_state.writable[idx]) > 0) {
|
||||
revents |= POLLOUT;
|
||||
}
|
||||
|
||||
revents &= w->pevents | POLLERR | POLLHUP;
|
||||
|
||||
if (revents == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uv__io_cb(loop, w, revents);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +279,29 @@ void uv__platform_loop_delete(uv_loop_t *loop)
|
||||
(void)loop;
|
||||
}
|
||||
|
||||
// Fakes thread names when nvim tracks or debugs its internal processes
|
||||
void uv__platform_invalidate_fd(uv_loop_t *loop, int fd)
|
||||
{
|
||||
if (fd < 0 || fd >= UV_BROWSER_MAX_FD) {
|
||||
return;
|
||||
}
|
||||
atomic_store(&g_shared_state.readable[fd], 0);
|
||||
atomic_store(&g_shared_state.writable[fd], 0);
|
||||
}
|
||||
|
||||
int uv__io_check_fd(uv_loop_t *loop, int fd)
|
||||
{
|
||||
(void)loop; (void)fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uv__io_fork(uv_loop_t *loop)
|
||||
{
|
||||
(void)loop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// pthread shims
|
||||
|
||||
int pthread_getname_np(pthread_t thread, char *name, size_t len)
|
||||
{
|
||||
(void)thread;
|
||||
@@ -165,17 +315,12 @@ int pthread_getname_np(pthread_t thread, char *name, size_t len)
|
||||
|
||||
int pthread_setname_np(pthread_t thread, const char *name)
|
||||
{
|
||||
(void)thread; (void)name; return 0;
|
||||
(void)thread; (void)name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
|
||||
{
|
||||
(void)thread; (void)policy; (void)param; return 0;
|
||||
}
|
||||
int sched_get_priority_max(int policy)
|
||||
{
|
||||
(void)policy; return 1;
|
||||
}
|
||||
int sched_get_priority_min(int policy)
|
||||
{
|
||||
(void)policy; return 1;
|
||||
(void)thread; (void)policy; (void)param;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user