Go to sleep, now, you little fool



  • Like many other people, I have found numerous examples of other developers reinventing the wheel rather than using built in libraries. More to the point, I have worked at two different companies where I found someone had written his own sleep method.Trivial things, I know, but all too typical.

    There was this one:

    procedure Sleep(Seconds : integer) ;
    var
      st : TDateTime ;
    begin
      st := now * 100000;
      repeat
        application.processmessages ;
      until (((now*100000) - st) >= Seconds) ;
    end ;

    and, later, this one:

    public void Sleep(int seconds)
    { 
      waittime = DateTime.Now.AddSeconds(seconds);
    while (DateTime.Now < waittime)
    {
    //just waiting
    }
    } 




  •  At least they could be consistant and use a do...while-loop in the second one.



  • Those came from two different companies, so it's probably a bit much to expect consistency... :)



  •  The really stupid part is that all they do is spin and waste resources instead of actually giving up the processor.  Sitting and spinning != sleep.  Were they really that concurrency retarded to not realize that?



  • Just wondering...is the title was a deliberate reference to The Decemberists' The Island.

     

     



  • Once in my lifetime, I felt the need to use sleep() in ASP.
    Since no function exists (why would anyone need to sleep in ASP?), I had to write my own.

    var wsh;
    function sleep(n) {
    	if(!wsh) wsh=Server.CreateObject("WScript.Shell");
    	wsh.Run("ping -n "+n+" -w 1 127.0.0.1",0,true);
    }


  • @Charlie Marlow said:

    There was this one:

    procedure Sleep(Seconds : integer) ;
    var
      st : TDateTime ;
    begin
      st := now * 100000;
      repeat
        application.processmessages ;
      until (((now*100000) - st) >= Seconds) ;
    end ;

    Well, that one has some sort of profit... the default Delphi sleep method does not respond to messages sent from Windows while sleeping (therefore, cannot be aborted by the user), this one does.

    Okay, given that there is no abort condition, it will still not abort, but it can be implemented easily. Well... whatever, go on.



  • @SeekerDarksteel said:

    Just wondering...is the title was a deliberate reference to The Decemberists' The Island.

     

     

    Yes.



  • That's just great, a Sleep() method that does the exact opposite of what the name implies.



  • @derula said:

    Well, that one has some sort of profit... the default Delphi sleep method does not respond to messages sent from Windows while sleeping (therefore, cannot be aborted by the user), this one does.

     

    I was going to say that the standard solution for this is to have a thread wake up every 100 ms or 1 s to check for scheduled events, and wait on that thread; if you need to abort, you just abort the scheduling thread and the main thread gets signaled anyway (with a different status).

    Then I realized that in order to implement this, you would need to already have a Sleep function or method.  Good thing that the Windows API has one built-in, then.



  •  Here's a bonus Delphi function:

    function IndexOfComma( const S: string ): integer;
    var
      wList: TStringList;
      i: integer;
    begin
      result := -1;
      if Empty( S ) then
        Exit;
      wList := TStringList.Create;
      try
        TokenizeString( S, wList );
        for i := 0 to Pred( wList.Count ) do begin
          if wList.Strings[ i ][ Length( wList.Strings[ i ] ) ] = ',' then
            result := i;
        end;
      finally
        wList.Free;
      end; 
    end;



  • @Charlie Marlow said:

    procedure Sleep(Seconds : integer) ;
    var
      st : TDateTime ;
    begin
      st := now * 100000;
      repeat
        application.processmessages ;
      until (((now*100000) - st) >= Seconds) ;
    end ;
     

     

    Ohh, and I thought I was the only (ex) Delphi coder here! I'll be honest and say I don't like Delphi, but enough of that.

    Anyway, that code snippet has a subtle but very major WTF: it spins the Windows message loop. The rest of this application's code and any 3rd party libraries better be reentrant (the VCL isn't BTW...), else say hello to mysterious bugs. Spinning the message loop is staggeringly buggy way to create only the illusion of concurrency.

      @Charlie Marlow said:

    public void Sleep(int seconds)
    { 
      waittime = DateTime.Now.AddSeconds(seconds);
    while (DateTime.Now < waittime)
    {
    //just waiting
    }
    } 


    And now here's one of my favourite* petty but real bugs: using the system's time of day as a timer. If the system time is adjusted during the loop, this procedure fails to wait the correct amount of time.

     

     

    * Assuming "DateTime.Now" means what I think it does.

     

     



  • @derula said:

    @Charlie Marlow said:

    There was this one:

    procedure Sleep(Seconds : integer) ;
    var
      st : TDateTime ;
    begin
      st := now * 100000;
      repeat
        application.processmessages ;
      until (((now*100000) - st) >= Seconds) ;
    end ;

    Well, that one has some sort of profit... the default Delphi sleep method does not respond to messages sent from Windows while sleeping (therefore, cannot be aborted by the user), this one does.

     


    But you could use a TTimer!

     



  • @dtfinch said:

    Once in my lifetime, I felt the need to use sleep() in ASP.
     

    pray tell us the reason why... sounds like frontpage material... 



  • @Chuck Mango said:

    And now here's one of my favourite* petty but real bugs: using the system's time of day as a timer. If the system time is adjusted during the loop, this procedure fails to wait the correct amount of time.

    Can you name even *one* system in use today which is not subject to that bug?  I'm fairly confident that most, if not all, of the systems I work on have that problem.  Further, I'm not sure I can conceive of a simple and efficient way to *not* be subject to it.



  • @tgape said:

    @Chuck Mango said:

    And now here's one of my favourite* petty but real bugs: using the system's time of day as a timer. If the system time is adjusted during the loop, this procedure fails to wait the correct amount of time.

    Can you name even *one* system in use today which is not subject to that bug?  I'm fairly confident that most, if not all, of the systems I work on have that problem.  Further, I'm not sure I can conceive of a simple and efficient way to *not* be subject to it.

    I think that question is wrong. No system clock is immune to that bug, because that's what system clocks do.

    There are other facilities designed specifically for giving elapsed time, not wall time - e.g. C/C++ clock(), Win32 GetTickCount().

    Of course, there's still room for problems with overflow.



  • @aihtdikh said:

    There are other facilities designed specifically for giving elapsed time, not wall time - e.g. C/C++ clock(), Win32 GetTickCount().

    From man 3 clock: "The value returned is the CPU time used so far as a clock_t."  Not really appropriate if you want a delay, since ideally you should consume very little CPU time while waiting.  It more or less works in a busyloop, but even then the delay in wall clock time is dependent on the system load level.


  • @tgape said:

    @Chuck Mango said:

    And now here's one of my favourite* petty but real bugs: using the system's time of day as a timer. If the system time is adjusted during the loop, this procedure fails to wait the correct amount of time.

    Can you name even *one* system in use today which is not subject to that bug?  I'm fairly confident that most, if not all, of the systems I work on have that problem.  Further, I'm not sure I can conceive of a simple and efficient way to *not* be subject to it.

    SQL Server's T-SQL "waitfor" command uses negligable CPU and functions independently from the system's time of day (Although CPU loaders confuse it).



  • @aihtdikh said:

    @tgape said:

    @Chuck Mango said:

    And now here's one of my favourite* petty but real bugs: using the system's time of day as a timer. If the system time is adjusted during the loop, this procedure fails to wait the correct amount of time.

    Can you name even *one* system in use today which is not subject to that bug?  I'm fairly confident that most, if not all, of the systems I work on have that problem.  Further, I'm not sure I can conceive of a simple and efficient way to *not* be subject to it.

    I think that question is wrong. No system clock is immune to that bug, because that's what system clocks do.

    There are other facilities designed specifically for giving elapsed time, not wall time - e.g. C/C++ clock(), Win32 GetTickCount().

    Of course, there's still room for problems with overflow.

    In a typical Microsoft fashion (even if it's not broken, let's fix it), daily time adjustment (to compensate for clock imprecision) also changes the system tick count (returned by GetTickCount). This was introduced in Windows XP. Thus, the tick count can even jump back.

     



  • @Aaron said:

    I was going to say that the standard solution for this is to have a thread wake up every 100 ms or 1 s to check for scheduled events, and wait on that thread; if you need to abort, you just abort the scheduling thread and the main thread gets signaled anyway (with a different status).

    Then I realized that in order to implement this, you would need to already have a Sleep function or method.  Good thing that the Windows API has one built-in, then.

    Delphi does have the WinAPI's sleep method available; however this is still not a good idea if you'll have to access anything relevant from the thread, because Delphi's VCL isn't thread-safe.



  • @tgape said:

    @Chuck Mango said:

    And now here's one of my favourite* petty but real bugs: using the system's time of day as a timer. If the system time is adjusted during the loop, this procedure fails to wait the correct amount of time.

    Can you name even *one* system in use today which is not subject to that bug?

     

     

    Yes, the one I'm the lead developer for :p

     

    @tgape said:


    I'm fairly confident that most, if not all, of the systems I work on have that problem.

     

     But that's on your to-do list, right? ;)

     

    @tgape said:

    Further, I'm not sure I can conceive of a simple and efficient way to *not* be subject to it.

     

     

    As mentioned by another poster, there are functions to measure elapsed time.



  • @derula said:

    Delphi does have the WinAPI's sleep method available; however this is still not a good idea if you'll have to access anything relevant from the thread, because Delphi's VCL isn't thread-safe.

     

    'Course it's not - message queues or other interactive threads rarely are, otherwise you'd have all sorts of craziness with event orders and reentrancy.

    If you need to enter a wait state that's time-limited and abortable then you need two threads.  Using ProcessMessages is just begging for trouble.  If you want, you can have your consumer thread wait on the timer with a timeout in a loop, checking for termination on each iteration, but the first way is easier because you can implement it in a worker base class.



  • @alegr said:

    In a typical Microsoft fashion (even if it's not broken, let's fix it), daily time adjustment (to compensate for clock imprecision) also changes the system tick count (returned by GetTickCount). This was introduced in Windows XP. Thus, the tick count can even jump back.

    That's because if you really wanted to know what time it was, you'd query the Naval Observatory's server. And if you can't access the internet, you probably don't care what time it is.



  • @CDarklock said:

    That's because if you really wanted to know what time it was, you'd query the Naval Observatory's server. And if you can't access the internet, you probably don't care what time it is.
    We don't care about what time it actually is. We care about "we were supposed to check in with the widget-maker every 49.6 microfortnights; well, is it time to check in yet?" What's normally a 10-minute count can become a year-long ordeal if someone at the console goes "lolwut" and sets the clock back to 2008, to e.g. play their favorite time-limited shareware game. And then the widget-maker shrills out a timeout warning and the widget plant emergency-shuts-down and Fabrikam loses >$Ⅿↂ, and someone's out of a job.

    The underlying problem is there's no reliable way to check ElapsedTime as anything other than ΔClockTime, and even that is no longer reliable.

    I wonder if QueryPerformanceCounter/QueryPerformanceFrequency is affected too...


Log in to reply