Runnin code at a certain time?

T

Tom Ricks

Hello all,
I am new to ruby and I am attempting to run some code at a certain
time. The Ruby Time class is very interesting, you can add time, and do
all of these amazing things but i cannot find a way to run code at a
certain time. I feel like I'm missing something. So far all I have is:

sleep(1) while Time.now < ("Time")

How would I link some code to be run to this? Or is there an easier way?
 
C

Caleb Clausen

Hello all,
I am new to ruby and I am attempting to run some code at a certain
time. The Ruby Time class is very interesting, you can add time, and do
all of these amazing things but i cannot find a way to run code at a
certain time. I feel like I'm missing something. So far all I have is:

sleep(1) while Time.now < ("Time")

How would I link some code to be run to this? Or is there an easier way?

The proper answer depends on the context where this code will be used.
Do you want your whole program to stop until some target time? In that
case, something as simple as

sleep(target_time-Time.now)
code_to_be_delayed

will do the trick nicely. But if this code runs within the context of
some larger system, which cannot afford to have the whole program be
unable to respond to events for many seconds in a row (rails, for
example), then sleep is a bad idea. In some cases, you might be able
to put your sleep and code_to_be_delayed in another thread, but then
you run into the problem of thread synchronization. If neither of my
simple suggestions does the trick, you should post more details abut
your application. (Maybe then someone will have enough information to
make a specific suggestion... I doubt I will.)
 
M

MenTaLguY

You might want to try my Scheduler gem.

Scheduler.at_time(time_in_seconds_from_epoch) do
# ... stuff to do ...
end

-mental
 
T

Tom Ricks

Caleb said:
The proper answer depends on the context where this code will be used.
Do you want your whole program to stop until some target time?
But if this code runs within the context of
some larger system, which cannot afford to have the whole program be
unable to respond to events for many seconds in a row (rails, for
example), then sleep is a bad idea.

Well, I'm not really sure. The intent is to have a repetitive code ran
at quite a few different times with changing variables. The sleep option
doesn't really seem reliable and realistic, although its the only thing
so far. The code I can come up with is this:

class TestClass
def testtime(timetorun)
timetorun = @timetorun
sleep(1) while Time.now.strftime("%I:%M:%S %p") < ("#{@timetorun}")
#Somewhere code to run when Time.now = timetorun
end
end
test = TestClass.new
test.testtime("11:04:40 AM")

The problems are #1. I don't know how to link the time code to the code
to be run when the time code is fulfilled. #2. If I have a list of times
to be run, and one time is out of order, it will just sleep past the out
of order time. #3. While the code is waiting for a certain time, with
sleep, it cannot do anything else. Is there a way that I can run code
that will watch for a time to be fulfilled on many levels instead of
just waiting until a time, moving on and waiting for the next? Is it
possible to do something like this:

when test.testtime is true/finished?
run some code
end
In some cases, you might be able
to put your sleep and code_to_be_delayed in another thread, but then
you run into the problem of thread synchronization.

How would I do this?

Your example of

sleep(target_time-Time.now)
code_to_be_delayed

seems to work better, although it still shuts the whole code down for a
while.
 
C

Caleb Clausen

Well, I'm not really sure. The intent is to have a repetitive code ran
at quite a few different times with changing variables. The sleep option
doesn't really seem reliable and realistic, although its the only thing
so far. The code I can come up with is this:

class TestClass
def testtime(timetorun)
timetorun = @timetorun
sleep(1) while Time.now.strftime("%I:%M:%S %p") < ("#{@timetorun}")
#Somewhere code to run when Time.now = timetorun
end
end
test = TestClass.new
test.testtime("11:04:40 AM")

The problems are #1. I don't know how to link the time code to the code
to be run when the time code is fulfilled.

I think procs are the feature you want to know about here.
#2. If I have a list of times
to be run, and one time is out of order, it will just sleep past the out
of order time.

so, sort 'em.
#3. While the code is waiting for a certain time, with
sleep, it cannot do anything else.

Select, or threads, or some kind of event driven system like
EventMachine are the usual solutions to this problem. I forgot to
mention select before.
Is there a way that I can run code
that will watch for a time to be fulfilled on many levels instead of
just waiting until a time, moving on and waiting for the next? Is it
possible to do something like this:

when test.testtime is true/finished?
run some code
end

Well, in general what you want here is a timer. It's very easy to
write your own; it sounds like you're close now, you just need to
learn about sort.
How would I do this?

Hmm, you have some learning to do. Including how to find information
about the standard library. I suggest you make friends with ri, which
is the command-line tool that displays stdlib documentation. Google
can also be very helpful. You also need some general background on
ruby and programming concepts; I'd suggest the book 'programming
ruby'. There's a free version of an old edition online.
 
A

Anonymous

...

Well, I'm not really sure. The intent is to have a repetitive code ran
at quite a few different times with changing variables. The sleep option
doesn't really seem reliable and realistic, although its the only thing
so far. The code I can come up with is this:

class TestClass
  def testtime(timetorun)
    timetorun = @timetorun
    sleep(1) while Time.now.strftime("%I:%M:%S %p") < ("#{@timetorun}")
    #Somewhere code to run when Time.now = timetorun
  end
end
test = TestClass.new
test.testtime("11:04:40 AM")

The problems are #1. I don't know how to link the time code to the code
to be run when the time code is fulfilled. #2. If I have a list of times
to be run, and one time is out of order, it will just sleep past the out
of order time. #3. While the code is waiting for a certain time, with
sleep, it cannot do anything else. Is there a way that I can run code
that will watch for a time to be fulfilled on many levels instead of
just waiting until a time, moving on and waiting for the next? Is it
possible to do something like this:

when test.testtime is true/finished?
 run some code
end


How would I do this?

Your example of

sleep(target_time-Time.now)
code_to_be_delayed

seems to work better, although it still shuts the whole code down for a
while.

I'm totally new to Ruby so I'm sorry for the piggyback question but,
in Perl you could
set an alarm clock that'd leave the program unfettered:


{ local $SIG{ALRM} = sub { die "timeout" };
alarm( "target_time" - "Time.now" );
# do other things
alarm( 0 );
};
if ( $@ =~ /^timeout/ ) {# time's up... }
elsif ( $@ ) {# other error... }


How would Ruby do this?

Thanks,
 
C

Caleb Clausen

I'm totally new to Ruby so I'm sorry for the piggyback question but,
in Perl you could
set an alarm clock that'd leave the program unfettered:


{ local $SIG{ALRM} = sub { die "timeout" };
alarm( "target_time" - "Time.now" );
# do other things
alarm( 0 );
};
if ( $@ =~ /^timeout/ ) {# time's up... }
elsif ( $@ ) {# other error... }


How would Ruby do this?

I went and looked again, and there is a Timeout module in ruby's
stdlib, which I had forgotten about:
http://www.ruby-doc.org/stdlib/libdoc/timeout/rdoc/index.html

So the equivalent of the perl code you posted should be:

#UNTESTED!!!
require 'timeout'
begin
Timeout.timeout(target_time-Time.now){
#do other things
}
rescue Timeout::Error
#time's up
rescue Exception
#other error
end

Timeout uses Thread#raise, which I think is not reliable on JRuby or
other implementations that use native threads.

PS: does anyone know why Timeout::Error < Interrupt ? Interrupt is for
^C... why would you want timeouts to be handled like ^C?
 
R

Reid Thompson

Caleb said:
I went and looked again, and there is a Timeout module in ruby's
stdlib, which I had forgotten about:
http://www.ruby-doc.org/stdlib/libdoc/timeout/rdoc/index.html

So the equivalent of the perl code you posted should be:

#UNTESTED!!!
require 'timeout'
begin
Timeout.timeout(target_time-Time.now){
#do other things
}
rescue Timeout::Error
#time's up
rescue Exception
#other error
end

Timeout uses Thread#raise, which I think is not reliable on JRuby or
other implementations that use native threads.

PS: does anyone know why Timeout::Error < Interrupt ? Interrupt is for
^C... why would you want timeouts to be handled like ^C?
may be of interest
http://rubyforge.org/projects/rubycron/
 
R

Reid Thompson

Caleb said:
I went and looked again, and there is a Timeout module in ruby's
stdlib, which I had forgotten about:
http://www.ruby-doc.org/stdlib/libdoc/timeout/rdoc/index.html

So the equivalent of the perl code you posted should be:

#UNTESTED!!!
require 'timeout'
begin
Timeout.timeout(target_time-Time.now){
#do other things
}
rescue Timeout::Error
#time's up
rescue Exception
#other error
end

Timeout uses Thread#raise, which I think is not reliable on JRuby or
other implementations that use native threads.

PS: does anyone know why Timeout::Error < Interrupt ? Interrupt is for
^C... why would you want timeouts to be handled like ^C?
also
http://www.notwork.org/~gotoken/ruby/p/crontab/crontab/crontab.rb
 
C

Charles Oliver Nutter

Caleb said:
Timeout uses Thread#raise, which I think is not reliable on JRuby or
other implementations that use native threads.

It should be reliable, and in fact we reimplemented Timeout in Java to
reduce the cost almost down to zero. I don't like that things like raise
and kill and Timeout are used, but we've made sure they work well when
they are.
PS: does anyone know why Timeout::Error < Interrupt ? Interrupt is for
^C... why would you want timeouts to be handled like ^C?

I do not.

- Charlie
 
C

Caleb Clausen

It should be reliable, and in fact we reimplemented Timeout in Java to
reduce the cost almost down to zero. I don't like that things like raise
and kill and Timeout are used, but we've made sure they work well when
they are.

I thought you'd made some long blog post proving how it's impossible
to Thread#raise perfectly reliably in JRuby. I may be confused. I'm
glad to hear that it does work and I apologize for spreading
misinformation.
 
E

Eric Hodel

The proper answer depends on the context where this code will be used.
Do you want your whole program to stop until some target time? In that
case, something as simple as

sleep(target_time-Time.now)
code_to_be_delayed

will do the trick nicely.

Note that #sleep won't necessarily sleep as long as you like (second
sentence):

$ ri Kernel#sleep
----------------------------------------------------------- Kernel#sleep
sleep([duration]) => fixnum

From Ruby 1.8
------------------------------------------------------------------------
Suspends the current thread for duration seconds (which may be any
number, including a Float with fractional seconds). Returns the
actual number of seconds slept (rounded), which may be less than
that asked for if another thread calls Thread#run. Zero arguments
causes sleep to sleep forever.
[...]
 
C

Caleb Clausen

Do you want your whole program to stop until some target time? In that
case, something as simple as

sleep(target_time-Time.now)
code_to_be_delayed

will do the trick nicely.

Note that #sleep won't necessarily sleep as long as you like (second
sentence):

$ ri Kernel#sleep
----------------------------------------------------------- Kernel#sleep
sleep([duration]) => fixnum

From Ruby 1.8
------------------------------------------------------------------------
Suspends the current thread for duration seconds (which may be any
number, including a Float with fractional seconds). Returns the
actual number of seconds slept (rounded), which may be less than
that asked for if another thread calls Thread#run. Zero arguments
causes sleep to sleep forever.
[...]

Is there some particular danger that Thread#run will be called on my
threads without my asking?
 
E

Eric Hodel

Do you want your whole program to stop until some target time? In
that
case, something as simple as

sleep(target_time-Time.now)
code_to_be_delayed

will do the trick nicely.

Note that #sleep won't necessarily sleep as long as you like (second
sentence):

$ ri Kernel#sleep
-----------------------------------------------------------
Kernel#sleep
sleep([duration]) => fixnum

From Ruby 1.8
------------------------------------------------------------------------
Suspends the current thread for duration seconds (which may be
any
number, including a Float with fractional seconds). Returns the
actual number of seconds slept (rounded), which may be less than
that asked for if another thread calls Thread#run. Zero
arguments
causes sleep to sleep forever.
[...]

Is there some particular danger that Thread#run will be called on my
threads without my asking?

If it's library code, yes. If it's application code, probably not.
 
C

Caleb Clausen

If it's library code, yes. If it's application code, probably not.

Can you be more specific? I'm curious as to how exactly this could
happen. I suspect you speak from experience, but I can't see how if an
application fiddles with a libraries internals in this way, that's not
considered a bug in the application.
 
E

Eric Hodel

Can you be more specific? I'm curious as to how exactly this could
happen. I suspect you speak from experience, but I can't see how if an
application fiddles with a libraries internals in this way, that's not
considered a bug in the application.

Since threads are involved, it doesn't need to muck with any
internals, it just has to wake up the thread your sleep was running in:

class YourLib
def your_sleep
sleep 1_000_000
end
end

class MyApp
# ...
def do_stuff
@threads << Thread.start do
# ...
YourLib.new.your_sleep
# ...
end
end

def wakeup
@threads.each do |thread|
thread.run
end
end
end
 
C

Caleb Clausen

Since threads are involved, it doesn't need to muck with any
internals, it just has to wake up the thread your sleep was running in:

The only purpose of run is to break a call to sleep (or select, or
join. Anything else?) If code external to my library decides to call
run on me, it ought to know what it's doing. If something bad happens,
it seems to me that that's a bug in the external code.
class YourLib
def your_sleep
sleep 1_000_000
end
end

class MyApp
# ...
def do_stuff
@threads << Thread.start do
# ...
YourLib.new.your_sleep
# ...
end
end

def wakeup
@threads.each do |thread|
thread.run
end
end
end

What application actually does this?
 
E

Eleanor McHugh

The only purpose of run is to break a call to sleep (or select, or
join. Anything else?) If code external to my library decides to call
run on me, it ought to know what it's doing. If something bad happens,
it seems to me that that's a bug in the external code.

In general if you're writing a library that makes use of threads it's
your responsibility to make them behave correctly when 'interrupted'
by application code. Otherwise you have a fragile library that will
greatly complicate debugging for its users.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,787
Messages
2,569,631
Members
45,338
Latest member
41Pearline46

Latest Threads

Top