The select() and pselect() system calls modify their arguments (the ‘fd_set *‘ arguments), so the input value tells the system which file descriptors to check and the return values tell the programmer which file descriptors are currently usable.
If you are going to call them repeatedly for the same set of file descriptors, you need to ensure that you have a fresh copy of the descriptors for each call. The obvious way to do that is to use a structure copy:
fd_set ref_set_rd;
fd_set ref_set_wr;
fd_set ref_set_er;
...
...code to set the reference fd_set_xx values...
...
while (!done)
{
fd_set act_set_rd = ref_set_rd;
fd_set act_set_wr = ref_set_wr;
fd_set act_set_er = ref_set_er;
int bits_set = select(max_fd, &act_set_rd, &act_set_wr,
&act_set_er, &timeout);
if (bits_set > 0)
{
...process the output values of act_set_xx...
}
}
(Edited to remove incorrect struct fd_set references – as pointed out by ‘R..’.)
My question:
- Are there any platforms where it is not safe to do a structure copy of the
fd_setvalues as shown?
I’m concerned lest there be hidden memory allocation or anything unexpected like that. (There are macros/functions FD_SET(), FD_CLR(), FD_ZERO() and FD_ISSET() to mask the internals from the application.)
I can see that MacOS X (Darwin) is safe; other BSD-based systems are likely to be safe, therefore. You can help by documenting other systems that you know are safe in your answers.
(I do have minor concerns about how well the fd_set would work with more than 8192 open file descriptors – the default maximum number of open files is only 256, but the maximum number is ‘unlimited’. Also, since the structures are 1 KB, the copying code is not dreadfully efficient, but then running through a list of file descriptors to recreate the input mask on each cycle is not necessarily efficient either. Maybe you can’t do select() when you have that many file descriptors open, though that is when you are most likely to need the functionality.)
There’s a related SO question – asking about ‘poll() vs select()’ which addresses a different set of issues from this question.
Note that on MacOS X – and presumably BSD more generally – there is an FD_COPY() macro or function, with the effective prototype:
extern void FD_COPY(const restrict fd_set *from, restrict fd_set *to);.
It might be worth emulating on platforms where it is not already available.
Since
struct fd_setis just a regular C structure, that should always be fine. I personally don’t like doing structure copying via the=operator, since I’ve worked on plenty of platforms that didn’t have access to the normal set of compiler intrinsics. Usingmemcpy()explicitly rather than having the compiler insert a function call is a better way to go, in my book.From the C spec, section 6.5.16.1 Simple assignment (edited here for brevity):
So there you go, as long as
struct fd_setis a actually a regular Cstruct, you’re guaranteed success. It does depend, however, on your compiler emitting some kind of code to do it, or relying on whatevermemcpy()intrinsic it uses for structure assignment. If your platform can’t link against the compiler’s intrinsic libraries for some reason, it may not work.You will have to play some tricks if you have more open file descriptors than will fit into
struct fd_set. The linux man page says:As mentioned below, it might not be worth the effort to prove that your code is safe on all systems.
FD_COPY()is provided for just such a use, and is, presumably, always guaranteed: