| 22 Dec 2025 |
Randy Eckenrode | What if you just set that define before including any headers? | 17:13:44 |
Randy Eckenrode | With both select and pselect? | 17:14:00 |
Randy Eckenrode | https://developer.apple.com/library/archive/releasenotes/Darwin/SymbolVariantsRelNotes/index.html | 17:16:13 |
Randy Eckenrode |
Setting the _DARWIN_UNLIMITED_SELECT macro will select the extension variants of select() and pselect(), which uses the $DARWIN_EXTSN suffix. The extended versions do not fail if the first argument is greater than FD_SETSIZE. This was the original BSD behavior.
| 17:17:01 |
Ihar Hrachyshka | yes defining just _DARWIN_UNLIMITED_SELECT works too. | 17:21:01 |
Ihar Hrachyshka | I will try to enable it for glib and see if it fixes qemu. that said...
any reason to ever not have it set when running on darwin? | 17:29:02 |
Randy Eckenrode | Not sure. Probably not. The check is for POSIX compliance. | 17:31:49 |
Randy Eckenrode | https://devblogs.microsoft.com/oldnewthing/20221102-00/?p=107343 | 17:34:21 |
Randy Eckenrode | * https://devblogs.microsoft.com/oldnewthing/20221102-00/? | 17:34:30 |
Randy Eckenrode | * | 17:34:35 |
Ihar Hrachyshka | claims on how this works
Apple's libc has a pretty wild feature (guarded by _DARWIN_UNLIMITED_SELECT, on by default) which allows fds above FD_SETSIZE. It works by checking the address of the fd_set: if it's within the current thread's stack, then the call will fail, under the assumption that it's a stack-allocated fd_set.
But if the address is NOT within the current thread's stack, select() assumes you know what you're doing and will allow the call, trusting you have allocated sufficient memory for the high fds in the fd_set.
so there may be some condition to fulfill to make it work.
which I think is not satisfied for qemu main loop fds?
| 17:34:35 |
Randy Eckenrode | That Old New Thing article provides a good explanation why things are the way they are. | 17:37:21 |
Ihar Hrachyshka | (taht said, in my test program I also define fds[] on stack and it "succeeds" so maybe claims are wrong) | 17:38:40 |
Randy Eckenrode | What about the emulation? How does it allocate the set? | 17:39:46 |
Ihar Hrachyshka | "emulation"? you mean g_poll macos implementation? | 17:40:35 |
Randy Eckenrode | The implication in the Darwin headers seems to suggest that it’s using a 64-bit type, which is a lot of fds. | 17:40:36 |
Randy Eckenrode | Yeah. | 17:40:45 |
Ihar Hrachyshka | not sure I understand. the fds are passed by caller. it's up to caller. | 17:41:44 |
Ihar Hrachyshka | complete macos g_poll:
gint
g_poll (GPollFD *fds,
guint nfds,
gint timeout)
{
struct timeval tv;
fd_set rset, wset, xset;
GPollFD *f;
int ready;
int maxfd = 0;
FD_ZERO (&rset);
FD_ZERO (&wset);
FD_ZERO (&xset);
for (f = fds; f < &fds[nfds]; ++f)
if (f->fd >= 0)
{
if (f->events & G_IO_IN)
FD_SET (f->fd, &rset);
if (f->events & G_IO_OUT)
FD_SET (f->fd, &wset);
if (f->events & G_IO_PRI)
FD_SET (f->fd, &xset);
if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI)))
maxfd = f->fd;
}
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
ready = select (maxfd + 1, &rset, &wset, &xset,
timeout == -1 ? NULL : &tv);
if (ready > 0)
for (f = fds; f < &fds[nfds]; ++f)
{
f->revents = 0;
if (f->fd >= 0)
{
if (FD_ISSET (f->fd, &rset))
f->revents |= G_IO_IN;
if (FD_ISSET (f->fd, &wset))
f->revents |= G_IO_OUT;
if (FD_ISSET (f->fd, &xset))
f->revents |= G_IO_PRI;
}
}
return ready;
}
| 17:42:17 |
Ihar Hrachyshka | you mean fd_sets? | 17:43:04 |
Randy Eckenrode | Yeah. | 17:43:51 |
Randy Eckenrode | That comment about where the sets are allocated doesn’t make sense to me. | 17:44:42 |
Ihar Hrachyshka | guess I should just test and see. I have a reproducer, just need to recompile glib to qemu and see. | 17:44:57 |
Randy Eckenrode | * | 17:44:59 |
Ihar Hrachyshka | source == hacker news nobody | 17:45:27 |
Randy Eckenrode | https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/bsd/sys/_types/_fd_def.h#L52_L54 | 17:46:06 |
Randy Eckenrode | Fish shell developer? | 17:47:01 |
Ihar Hrachyshka | I was actually also looking at the same struct def. so looks like we may need to also define the __DARWIN_FD_SETSIZE otherwise we write past the end of the struct (I think) | 17:47:57 |
Randy Eckenrode | Or malloc it based on the size of the incoming set. | 17:49:12 |
Randy Eckenrode | Glib supports using cleanup functions to make that less messy. | 17:49:27 |