Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NQRTGoYgnZQsh7uCh6Jsxm
147 lines
4.1 KiB
C
147 lines
4.1 KiB
C
/*
|
|
* Minimal <sys/select.h> for the MontaukOS NetSurf port.
|
|
*
|
|
* MontaukOS has no select(). Readiness on a socket is expressed through the
|
|
* kernel's waitset (SYS_WAITSET_*), which is a different shape entirely, and
|
|
* stdio descriptors are not waitable objects at all.
|
|
*
|
|
* NetSurf's core uses select() in exactly one place -- content/fetch.c's
|
|
* fetch_fdset() collects descriptors from the registered fetchers, and the
|
|
* frontend's main loop selects over them plus its own input. With
|
|
* NETSURF_USE_CURL=NO no fetcher registers a descriptor, so the only fd that
|
|
* ever appears is the monkey frontend's stdin (fd 0).
|
|
*
|
|
* That makes a useful shim possible: fd 0 is answered from the libc's
|
|
* montauk_stdin_ready(), which reports whether a COMPLETE line is buffered.
|
|
* That is the readiness question monkey is really asking -- it goes on to call
|
|
* fgets(), so reporting "readable" on a half-typed line would park the whole
|
|
* browser inside stdio until Enter. With no line pending we sleep out the
|
|
* caller's timeout and return 0, which lets the core's scheduled callbacks run
|
|
* to completion: a fetch takes many main-loop iterations, and under the old
|
|
* always-ready shim it got exactly one per line typed.
|
|
*
|
|
* LIMITATION: only fd 0 has a readiness source. Any other read descriptor is
|
|
* still reported ready unconditionally, and write/except sets are always
|
|
* empty. With NETSURF_USE_CURL=NO no fetcher registers a descriptor, so fd 0
|
|
* is the only one that ever appears. When fetch_montauk lands it will register
|
|
* descriptors here and this needs a genuine waitset-backed implementation.
|
|
*/
|
|
#ifndef _MONTAUK_COMPAT_SYS_SELECT_H_
|
|
#define _MONTAUK_COMPAT_SYS_SELECT_H_
|
|
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#define FD_SETSIZE 64
|
|
|
|
typedef struct {
|
|
unsigned long fds_bits[(FD_SETSIZE + (8 * sizeof(unsigned long)) - 1) /
|
|
(8 * sizeof(unsigned long))];
|
|
} fd_set;
|
|
|
|
#define __NFDBITS (8 * (int) sizeof(unsigned long))
|
|
|
|
#define FD_ZERO(s) memset((s), 0, sizeof(fd_set))
|
|
#define FD_SET(fd, s) \
|
|
((void) ((s)->fds_bits[(fd) / __NFDBITS] |= \
|
|
(1UL << ((fd) % __NFDBITS))))
|
|
#define FD_CLR(fd, s) \
|
|
((void) ((s)->fds_bits[(fd) / __NFDBITS] &= \
|
|
~(1UL << ((fd) % __NFDBITS))))
|
|
#define FD_ISSET(fd, s) \
|
|
(((s)->fds_bits[(fd) / __NFDBITS] & (1UL << ((fd) % __NFDBITS))) != 0)
|
|
|
|
static inline int montauk_fdset_empty(const fd_set *s)
|
|
{
|
|
unsigned i;
|
|
|
|
if (s == NULL)
|
|
return 1;
|
|
for (i = 0; i < sizeof(s->fds_bits) / sizeof(s->fds_bits[0]); i++) {
|
|
if (s->fds_bits[i] != 0)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static inline int montauk_fdset_count(const fd_set *s, int nfds)
|
|
{
|
|
int fd, n = 0;
|
|
|
|
if (s == NULL)
|
|
return 0;
|
|
for (fd = 0; fd < nfds && fd < FD_SETSIZE; fd++) {
|
|
if (FD_ISSET(fd, s))
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static inline int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|
fd_set *exceptfds, struct timeval *timeout)
|
|
{
|
|
int stdin_wanted;
|
|
int ready;
|
|
|
|
if (writefds != NULL)
|
|
FD_ZERO(writefds);
|
|
if (exceptfds != NULL)
|
|
FD_ZERO(exceptfds);
|
|
|
|
stdin_wanted = (readfds != NULL) && (nfds > 0) && FD_ISSET(0, readfds);
|
|
if (stdin_wanted) {
|
|
FD_CLR(0, readfds);
|
|
if (montauk_stdin_ready()) {
|
|
FD_SET(0, readfds);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Descriptors other than stdin have no readiness source here, so they
|
|
* keep the old unconditional-ready answer.
|
|
*/
|
|
ready = montauk_fdset_count(readfds, nfds);
|
|
if (ready > 0)
|
|
return ready;
|
|
|
|
if (timeout == NULL) {
|
|
/*
|
|
* The core has nothing scheduled ("POLL BLOCKING"), so blocking
|
|
* for a command is right -- and returning 0 here instead would
|
|
* spin the CPU at 100%.
|
|
*/
|
|
if (stdin_wanted && montauk_stdin_wait()) {
|
|
FD_SET(0, readfds);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
{
|
|
unsigned long usec = (unsigned long) timeout->tv_sec * 1000000UL +
|
|
(unsigned long) timeout->tv_usec;
|
|
|
|
/*
|
|
* Sleep in slices rather than one long usleep: a scheduled
|
|
* timeout can be seconds long, and typing must not wait it out.
|
|
*/
|
|
while (usec > 0) {
|
|
unsigned long slice = (usec > 5000UL) ? 5000UL : usec;
|
|
|
|
usleep(slice);
|
|
usec -= slice;
|
|
|
|
if (stdin_wanted && montauk_stdin_ready()) {
|
|
FD_SET(0, readfds);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|