/* * Minimal 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 #include #include #include #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