www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is this a good way of setting up a timer?

reply Andrej Mitrovic <none none.none> writes:
I can't find any timers in phobos, basically I want some loop to run for a
predetermined amount of time. Currently I use this:

import core.time;
import std.datetime;
import core.thread;

void main()
{
    auto finalTime = Clock.currTime + dur!"seconds"(4);
    
    while (true)
    {
        Thread.sleep(dur!("seconds")(1));
        if (Clock.currTime > finalTime)
            break;   
    }
}

Is that check doing any conversions in the background, or am I safe
(performance-wise)? Well I'm probably wasting performance on waking up the
thread every second.. hmm.
Jun 03 2011
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On 2011-06-03 14:22, Andrej Mitrovic wrote:
 I can't find any timers in phobos, basically I want some loop to run for a
 predetermined amount of time. Currently I use this:
 
 import core.time;
 import std.datetime;
 import core.thread;
 
 void main()
 {
 auto finalTime = Clock.currTime + dur!"seconds"(4);
 
 while (true)
 {
 Thread.sleep(dur!("seconds")(1));
 if (Clock.currTime > finalTime)
 break;
 }
 }
 
 Is that check doing any conversions in the background, or am I safe
 (performance-wise)? Well I'm probably wasting performance on waking up the
 thread every second.. hmm.

Generally, you'd just put it to sleep for the period of time that you want to wait for. The only reason that I see to keep waking it up is if it could be interrupted and effectively told to wake up - in which case you would be sleeping and waking up over and over again, checking to see if enough time had passed or if you had been signaled to stop waiting. Now, if what you're trying to do is run the code in the loop for a predetermined length of time, then you'd just check the time every X iterations of the loop. If the loop is short, then X would be larger (to avoid asking for the time needlessly often and wasting cycles), whereas if the loop is long enough, then X might even be 1. As your code stands though, I'm not sure what you're really trying to. Oh, and by the way, std.datetime publicly imports core.time, so if you import std.datetime, you don't need to import core.time. - Jonathan M Davis
Jun 03 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/3/11, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 Generally, you'd just put it to sleep for the period of time that you want
 to
 wait for. The only reason that I see to keep waking it up is if it could be
 interrupted and effectively told to wake up - in which case you would be
 sleeping and waking up over and over again, checking to see if enough time
 had
 passed or if you had been signaled to stop waiting.

Yeah, I should have made a better example. Basically I have a loop that ends either after a specific time period, or it could end by receiving a signal. I just use a shared book for that, this would be it: while (engineActive) // shared bool { if (Clock.currTime > finalTime) break; Thread.sleep(dur!("seconds")(1)); }
 Oh,
 and
 by the way, std.datetime publicly imports core.time, so if you import
 std.datetime, you don't need to import core.time.

 - Jonathan M Davis

Ok thanks!
Jun 03 2011
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On 2011-06-03 14:37, Andrej Mitrovic wrote:
 On 6/3/11, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 Generally, you'd just put it to sleep for the period of time that you
 want to
 wait for. The only reason that I see to keep waking it up is if it could
 be interrupted and effectively told to wake up - in which case you would
 be sleeping and waking up over and over again, checking to see if enough
 time had
 passed or if you had been signaled to stop waiting.

Yeah, I should have made a better example. Basically I have a loop that ends either after a specific time period, or it could end by receiving a signal. I just use a shared book for that, this would be it: while (engineActive) // shared bool { if (Clock.currTime > finalTime) break; Thread.sleep(dur!("seconds")(1)); }

Yeah. That looks fine, though currTime isn't a property (it takes an optional TimeZone object), so technically it should have parens. That's obviously not enforced at the moment though. In any case, it's essentially fine, but has 2 potential issues, depending on how accurate you want the timing to be or how responsive you want the loop to be in exiting. First off, you pay no attention whatsoever to how close the current time is to the final time such that there could be a few microseconds of difference between them and you'd still sleep for a whole second before exiting. If you want it to be more accurate, then it should be something more like while(engineActive) { auto curr = Clock.currTime(); if(curr > finalTime) break; Thread.sleep(dur!"seconds(min(1, (finalTime - currTime).total!"seconds"()))); } Incidentally, this use case shows that I should probably add overloads for some of the basic math functions for Duration... In any case, such an implementation would make it so that it doesn't sleep for longer than it needs to. The other potential issue is responsiveness. If you want the loop to exit quickly after engineActive has been set to true, then you should probably be setting sleep to something more like 100ms (though if you set the value too low, then the loop wakes up more often than might be desirable, so it's a bit of a balancing act). Regardless, the only potential issues lie with how quickly you want the loop to exit after the exit condition has been reached. - Jonathan M Davis
Jun 03 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I didn't even think about issue #1. Thanks again!
Jun 03 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Hey, so is there a reason I'm not allowed to use immutable here:

immutable finalTime = Clock.currTime + dur!"seconds"(5);

Error: cannot implicitly convert expression
(currTime(cast(immutable(TimeZone))opCall()).opBinary(dur(5L))) of
type SysTime to immutable(SysTime)
Jun 04 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-06-04 11:14, Andrej Mitrovic wrote:
 Hey, so is there a reason I'm not allowed to use immutable here:
 
 immutable finalTime = Clock.currTime + dur!"seconds"(5);
 
 Error: cannot implicitly convert expression
 (currTime(cast(immutable(TimeZone))opCall()).opBinary(dur(5L))) of
 type SysTime to immutable(SysTime)

Because SysTime does not currently work with immutable. There are multiple compiler bugs which prevent it (including the fact that you can't use postblit with non-mutable objects). It _should_ work, but it doesn't. Once the compiler does a better job dealing with const and immutable (which I believe is on the todo list after fixing destruction for temporaries), then it should work, but for now, it doesn't. - Jonathan M Davis
Jun 04 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 03 Jun 2011 17:37:40 -0400, Andrej Mitrovic  
<andrej.mitrovich gmail.com> wrote:

 On 6/3/11, Jonathan M Davis <jmdavisProg gmx.com> wrote:
 Generally, you'd just put it to sleep for the period of time that you  
 want
 to
 wait for. The only reason that I see to keep waking it up is if it  
 could be
 interrupted and effectively told to wake up - in which case you would be
 sleeping and waking up over and over again, checking to see if enough  
 time
 had
 passed or if you had been signaled to stop waiting.

Yeah, I should have made a better example. Basically I have a loop that ends either after a specific time period, or it could end by receiving a signal. I just use a shared book for that, this would be it: while (engineActive) // shared bool { if (Clock.currTime > finalTime) break; Thread.sleep(dur!("seconds")(1)); }

I'm going to put on my old-school concurrency hat here :) How I would write this is with a mutex and a condition. Then instead of sleeping, I'd wait for the conditional. I.e.: while(engineActive) // shared bool { auto curtime = Clock.currTime; if(curtime > finalTime) break; synchronized(mutex) cond.wait(finalTime - curTime); } Then, you have a function that sets the bool and signals the condition: void endProgram() { synchronized(mutex) { if(engineActive) { engineActive = false; cond.notifyAll(); } } } Taking my hat off, I think the better way to do this is with std.concurrency. But I have zero experience with it. I'd guess there are ways to wait on your message queue, and some way to broadcast a message to all threads. In any case, sleeping for a set period of time, and then checking a boolean works, but is both inefficient and less responsive than waiting for the exact condition you are looking for. Trouble is, in some cases, a thread is waiting for *multiple* conditions, like input from a stream or exiting the program. On some oses, it's very difficult to wait for both of those. There are ways to solve this with helper threads, but this can also be wasteful. Without further details on what your thread is doing, I can't say whether this would be a problem for you. What I've done in the past is wait for the condition that needs to be more responsive (i.e. handling I/O) and use a timeout that allows a reasonable response to the other condition. -Steve
Jun 06 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/6/11, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 Then, you have a function that sets the bool and signals the condition:

 void endProgram()
 {
     synchronized(mutex)
     {
        if(engineActive)
        {
            engineActive = false;
            cond.notifyAll();
        }
     }
 }

Interesting, thanks. There's some WinAPI functions like WaitForMultipleObjects, I've read about them too. Other than that, the while loop will go away and be replaced with some kind of front-end for the user (e.g. GUI), while the background thread crunches some numbers. The background thread should be able to signal if something went wrong. Throwing exceptions from the work thread is off limits, because they don't propagate to the foreground thread, so I'm left with either using some kind of global boolean or I'd use std.concurrency.send() to signal that something went wrong.
Jun 06 2011
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On 2011-06-03 15:02, Jonathan M Davis wrote:
 Incidentally, this use case shows that I should probably add overloads for
 some of the basic math functions for Duration...

I'm an idiot. I was thinking that min and max were in std.math and specific to built-in types, but we were smarter than that. They're defined as templated functions in std.algorithm and already work with core.time.Duration. - Jonathan M Davis
Jun 07 2011