• Tim Ward (unregistered)

    Assuming that the lack of comment was in the original, how much did it cost to work out WTF this code was intended to do?

  • RTFM (unregistered)

    This is a well known use of select(), e.g. https://stackoverflow.com/questions/3125645/why-use-select-instead-of-sleep https://howthisstuffworks.blogspot.com/2007/11/how-to-sleep-using-select-call.html etc. This is even in the select() documentation (man select).

    whether it's WTF or not depends on the context. If one needs to wait for tens or hundreds of services sequentially, then 750ms on each will matter a lot.

  • Network Noadle (unregistered)

    Presumably the cost of spooling up the perl(1) process for each invocation is considered negligible? Depending on the system's workload, it could easily take more than 500ms just to load the interpreter, parse the select call and start executing the program. It smells, to me, like someone was trying to show-off their l33t skillz.

  • Brian Boorman (unregistered) in reply to RTFM
    This is a well known use of select()

    I might buy that if this was perl code. But it's not. It's shell code that calls into Perl (and creates a dependency that the system have Perl installed).

    Any excessive delay could be handled by shutting down all the services in parallel and then checking them sequentially. Actual delay should only then be longest-service-shutdown-time plus 1 second (plus negligible time to check each service's status). Hardly a time waster for the user.

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

    Depending on the system's workload, it could easily take more than 500ms just to load the interpreter, parse the select call and start executing the program.

    More likely it depends on whether or not the perl binary and all of its shared libs and other dependencies are in the disk cache or not. On my machine right now, I just ran perl --version from a cold cache, and it took 425 ms to run the first time. On the second time, once everything was loaded into the disk cache, it took a much more reasonable 10 ms to run.

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

    And since this will be potentially running every 750 ms, that will keep perl in the disk cache much of the time. So the cache misses should be rare.

    It also takes time to load the sleep binary into memory.

  • ooOOooGa (unregistered)

    It also takes time to load the sleep binary into memory.

    If everything is using sleep like they should, sleep is probably already loaded into memory. Also, I expect that the sleep binary is much smaller and faster to load than perl is.

  • (nodebb) in reply to ooOOooGa

    If you were using "ksh", then sleep is a shell built-in...so very efficient sleeping.

  • Yazeran (unregistered)

    The are surely doing it wrong.

    Anyone knows that in order to sleep with subsecond accuracy you shuld use the Time::HiRes module!

    So of cause the sleep should be:

    perl -e 'use Time::HiRes qw(sleep); printf("Entering sleep\n");sleep(0.25);printf("Exiting sleep\n")'

    (Prints added for proper enterprisiness :-) )

    Yours Yazeran.

  • (nodebb)

    From the Winsock Lame List at https://tangentsoft.net/wskfaq/articles/lame-list.html

    Calling select() with three empty fd_sets and a valid TIMEOUT structure as a sleazy delay function. Inexcusably lame. Reason: The select() function is intended as a network function, not a general purpose timer. Alternative: Use a legitimate system timer service.

  • some ron (unregistered)

    So this WTF contains three parts:

    1. using Perl is WTF, because it may not be loaded or even installed
    2. sleeping for .25 seconds instead of a full second
    3. using select to sleep .25 seconds

    It might surprise the younglings, but back in the day everyone and everything used Perl and if you use any Debian-based system you do, too. AIX always has been rather old-school, so it would not surprise me if Perl was a hard system dependency. I'd frown more upon using Python because of its availability than Perl.

    That said, the OP assumes that the version using Perl was the first and some kind of premature optimization, but what if the developer actually tested and maybe even got user feedback about that, so what we see is already the result of a proper development cycle? Then it would be a sign of being on the wrong side of the Dunning-Kruger curve to criticize this. Since we don't know, we should probably ignore this part.

    If we accept that using Perl is fine and using a .25 seconds sleep is necessary, you'd have to provide a better solution than the given one, to WTF the select-based one. If you can't, you might be calling a solution stupid, which is the best one, that is possible, which again puts you at the wrong side of the Dunning-Kruger curve.

  • I'm not a robot (unregistered) in reply to Steve_The_Cynic
    From the Winsock Lame List
    No idea why you think that would be in any way relevant to AIX code, but OK.
  • Worf (unregistered) in reply to Steve_The_Cynic

    That's only lame because Win32 select() is lame. Windows select() is only for sockets. You can't use it on say, a file handle (CreateFile).

    On POSIX compatible systems, select() is a general system call - it handles any file descriptor, whether it was opened using open(), creat(), socket(), pipe() or other call that returns a file descriptor (int fd). (And yes, you can use it on files for non-blocking).

    As such, it's generally understood that using select() for sub-second sleeps is a supported function of select(). BSD systems should use poll() instead for the same effect.

  • (nodebb) in reply to Steve_The_Cynic

    @Steve_The_Cynic: As you point out, this is for Winsock, meaning Windows, which has a millisecond-level sleep API. The original code was for Unix, which frequently doesn't, and specifically for AIX, a vaguely Unix-compatible OS from IBM, which definitely doesn't unless they've added it recently.

  • (nodebb) in reply to Worf

    As such, it's generally understood that using select() for sub-second sleeps is a supported function of select().

    It's supported even on Windows, in the sense that it works and does what you expect it to do. On the other hand, if you can't sub-second sleep on a UNIX-type system without abusing I/O multiplexer calls, well, that, too, is inexcusably lame.

  • RTFM (unregistered) in reply to Brian Boorman

    Any excessive delay could be handled by shutting down all the services in parallel

    Of course. If you're willing to rewrite AIX to use systemd, perhaps. Practically, the OS shuts down services the way it does, you cannot change it.

  • ip-guru (unregistered)

    Not even sure why you need to wait for the service to shut down. on Linux you can delete a running service with no adverser effect anyway. Files only get truly deleted once no process is using them

  • Alan (unregistered)

    The OS here: thanks to all who responded in a spirit of devil's advocacy, but I can confirm that:

    1. The original code was uncommented
    2. The package contains only a single service and runs on the leaf nodes of a networked installation
    3. This code only runs in the package deinstaller so the time taken is subsumed in that for updating the RPM database and modifying the file system and would naturally get parallelized over multiple leaf nodes even if it were part of a larger automation
    4. This is the only Perl code in the entire package
    5. The project includes its own Python interpreter which (as this is %preun) is still available for a Python uSleep if you really want one
  • (nodebb) in reply to ip-guru

    Not even sure why you need to wait for the service to shut down

    File locks, modified shared object libraries, sockets, sub processes, etc etc etc.

    on Linux you can delete a running service with no adverser effect anyway. Files only get truly deleted once no process is using them

    I love the way people like to say It works on $DIFFERENT_OPERATING_SYSTEM as though it's a valid point.

    This is AIX. It's a flavour of Unix. It probably works the same.

  • nasch (unregistered)

    "If one needs to wait for tens or hundreds of services sequentially, then 750ms on each will matter a lot."

    Only if you actually have to wait. If those services are independent then the chance that each of them will not be ready when you first check, but will be ready 250-999 milliseconds later, is extremely small (and smaller as the number of services, thus the significance of the wait, goes up). The scenario where waiting 250ms vs 1000 will actually matter is very much an edge case. Of course it's possible the one in question is such an edge case, but there's no reason to suppose so.

  • Rudolf Polzer (unregistered)

    This way is explicitly recommended by perldoc -f sleep:

    For delays of finer granularity than one second, the Time::HiRes module (from CPAN, and starting from Perl 5.8 part of the standard distribution) provides usleep. You may also use Perl's four-argument version of select leaving the first three arguments undefined, or you might be able to use the syscall interface to access setitimer(2) if your system supports it. See perlfaq8 for details.

    Time::HiRes was likely not always installed on something as arcane as AIX, which likely ran Perl 5.6 when this was written.

    Using setitimer via the syscall function, with manually packed structs, and catching the signal doesn't make a nice one-liner at all and would be a much bigger WTF.

Leave a comment on “Efficiently Waiting”

Log In or post as a guest

Replying to comment #:

« Return to Article