• alexmagnus (unregistered)

    #define min(t1,t2) -273.15

  • Anonymous Pedant (unregistered)

    nfds should be set to the highest-numbered file descriptor in any of the three sets, plus 1.

    The code would be correct if it were "max(pty_fd, net_fd)+1". Assuming, of course, that max() itself were correct.

  • (nodebb)

    #define 16 strlen("WTF")

  • Tim Ward (unregistered)

    Reminds me of the bug in some IBM360 assembler which was traced to

    R4 EQU 5

    (That's right, the name R4 addressed register 5. Not the usual way of doing things.)

  • Little Bobby Tables (unregistered)

    I was working on a system which built reports using a proprietary in-house system which was actually really well-designed and fun to use (ah, happy days). The text for the descriptors of the lines on those reports were held in a static file which was maintained by whoever it was tasked with the job.

    Then one day one of my team approached with: "I changed the labels, but when I re-ran the report, it still shows me all the old labels." Probably something silly, like the wrong labels file was being used, or the wrong program, or whatever. Nope, all looked correct. New labels file, old labels. Nothing untoward in the make file, everything in there as supposed to be. Time to inspect the code. Let's look at the init routine. WHAT! Someone had written a routine which deliberately overwrote the contents of the variables populated from the labels file with the exact same data as had been put there from the labels file. The upshot was that it just looked as though the labels file had not been changed.

    I wondered at that stage whether I'd been pranked. The possibility wasn't remote. But I calmly said, "There's your problem, right there. Go and find out who did this and for what reason, and you can take it from there, I trust?"

    I miss that job.

  • (nodebb)

    At a glance, this code just looks wrong. The first parameter to select is the number of file descriptors.

    Uhm, nope. Well, as Anonymous Pedant said it is wrong, but way less wrong than you think.

  • Pista (unregistered)

    There's nothing weird about select. You just have to carefully RTFM.

    But this WTF is a cute little time bomb :)

  • Andrew (unregistered)

    where the root issue lied

    truly we are in a post-truth society.

  • Officer Johnny Holzkopf (unregistered) in reply to Medinoc

    It is less wrong to say select() is the easiest system call to understand, but it's much wrongerer to say that max(x, y) is 16 (instead of a suspension bridge).

  • (nodebb)

    "and Alan assumed that was where the root issue lied."

    This means that the root issue told falsehoods. What you should have written was

    "and Alan assumed that was where the root issue lay."


    "and Alan assumed that was where the root issue was to be found."

  • Guester (unregistered)

    TRWTF is that Remy didn't read the docs for select.

    Remy: Oh, I did. But I didn't understand them. That's not the worst man page I've ever seen, but it's up there.

  • Anonymous') OR 1=1; DROP TABLE wtf; -- (unregistered)

    TRWTF is using select() instead of epoll(), where the performance is O(number of fds) instead of O(largest fd).

    Incidentally, this code would work just fine on Windows, since Winsock completely ignores the nfds parameter, which is included only for compatibility with BSD sockets. Windows socket handles are opaque pointer-sized handle values, so anything that scaled in O(that) would be completely useless.

  • Sole Purpose of VIsit (unregistered) in reply to Anonymous') OR 1=1; DROP TABLE wtf; --

    epoll() wasn't available in the early '90s. And, given that FD_SETSIZE is (presumably) 16 on the platform in question, it's fair to say that we're talking early '90s.

    Nope, the real WTF is what Remy says it is -- redefining the system macro MAX() just so your comms code works.

  • Anonymous') OR 1=1; DROP TABLE wtf; -- (unregistered) in reply to Sole Purpose of VIsit

    Perhaps I shouldn't have mixed a serious response with a tongue-in-cheek response. Yes, obviously redefining max() is TRWTF.

  • TheSelectGuy (unregistered)

    From the select() man page: “ nfds should be set to the highest-numbered file descriptor in any of the three sets, plus 1”

    So notwithstanding the redefinition of max(), the code is “correct”, as in it should be max(). Also, it basically tells the system call how high up to analyze the array of bits, so as long as the file descriptors are 15 or less then the code would actually work. As most Unix code in the 90s would basically fork() on an incoming connection, it’s very likely the FDs would have stayed under 15. I’m more concerned about the total lack of timeout in that code :)

    If the code does NOT fork on incoming connections, then it’s ripe for a buffer overflow if the fd gets higher than FD_SETSIZE.

  • (nodebb) in reply to Ross_Presser

    or maybe, "and Alan assumed that was who the root issue laid"

  • Just Some Guy (unregistered)

    I suspect this was caused by a developer on the team called Max who just needed a temporary name for his macro :-)

  • (nodebb)

    I'm more interested in this part:

    in the 90s, in an era when Swedish pop bands were getting regular US radio play

    As an ABBA fan, I'm curious to hear what other Swedish pop bands were around. Time to build a Spotify playlist!

  • (nodebb) in reply to jkshapiro

    ABBA preceded the 90s, but surely you remember Rednex or Ace of Base? (Yes, Rednex are best known for Swedish cultural classics such as "Cotton-Eyed Joe" and "Spirit of the Hawk". ;-) )

  • (nodebb)

    That max function reminds me ... I just read recently somewhere ... A programmer was assigned to "write a function that returns today's date" in a certain format. He wrote the function on Sept 12, 2014, so the complete body of his function was 'return "Sept 12, 2014"'. Because that was "today's date". He met the requirements.

  • Fernando (unregistered) in reply to TheSelectGuy

    'From the select() man page: “ nfds should be set to the highest-numbered file descriptor in any of the three sets, plus 1”. So notwithstanding the redefinition of max(), the code is “correct”, as in it should be max().'

    Um, no. If my two fds are 3 and 5, I need to pass nfds=6 so that select will look at the first 6 fds: 0, 1, 2, 3, 4, 5. So max()+1 is correct. for a sanity check, I scanned the code base for a networking program that I'm working on; "select(max_fd+1," appears several times.

  • (nodebb) in reply to Anonymous') OR 1=1; DROP TABLE wtf; --

    That's not quite true, yes, select() is O(nfds), where "nfds" is, as others have stated before, the max. fd plus one, s, and even if we assume nfds is limited to 1024 (glibc default), can be quite expensive. However, epoll() is not O(number of fds), instead, poll() is (approximately?) O(number of fds) whereas the epoll() API consists of multiple syscalls to set up the watch fd and then, presumably repeated, calls to epoll(). I'm not sure what the setup costs, but for N fds, it will hardly be less than O(N). The epoll() call itself, on the other hand, is claimed to be very efficient, possibly even O(1).

    epoll() is therefore much more efficient when there are many waits compared to the initial setup. select() and poll() on the other hand, require no setup but more honestly the setup is performed once on every waiting operation.

    You can try to derive some guidelines from this if you want … my own broad rules are:

    • Never use select() if you can use poll() – it's a better designed API IMO and, most importantly, can handle fds >= 1024 on GNU/Linux.*
    • Use epoll() only if you have many file descriptors and the set doesn't change a lot in relation to the number of waits. Permanent HTTP connections, BGP4, TCP time servers etc., you get the idea. Or you need that last ounce of performance and can probably demonstrate that epoll() pays off once the code using it is implemented. Reason: The epoll() API is substantially more effort to use than poll(), so you should count the costs and benefits before you use it.

    Of course, a probably even better idea is to use a portable event library that does much or all of that low level fd juggling for you, e.g. libevent2 (C/C++) or Boost.Asio (C++).

    *) If you need to wait for signals, then, of course, you should prefer pselect() over select() and ppoll() over poll().

    Addendum 2019-10-24 06:00: Another reason to prefer poll() over epoll() is that it is portable to *BSD, and that includes Darwin (i.e. Mac OS). The native *BSD equivalent of epoll() is kqueue.

  • (nodebb) in reply to Alexis_de_Torquemada
    ABBA preceded the 90s, but surely you remember Rednex or Ace of Base? (Yes, Rednex are best known for [...] "Cotton-Eyed Joe"

    Auuuugh!!!! Who let all these hamsters in here, and why are they line-dancing on my screen!?

Leave a comment on “A Select System Call”

Log In or post as a guest

Replying to comment #:

« Return to Article