views:

425

answers:

5

I'm writing a job-scheduling app in Ruby for my work (primarily to move files using various protocol at a given frequency)

My main loop looks like this :

while true do
  # some code to launch the proper job
  sleep CONFIG["interval"]
end

It's working like a charm, but I'm not really sure if it is safe enough as the application might run on a server with cpu-intensive software running.

Is there another way to do the same thing, or is sleep() safe enough in my case ?

+1  A: 

If you don't need an exact interval, then it makes sense to me. If you need to be awoken at regular times without drift, you probably want to use some kind of external timer. But when you're asleep, you're not using CPU resources. It's the task switch that's expensive.

Scott Whitlock
+3  A: 

Any time I feel the need to block, I use an event loop; usually libev. Here is a Ruby binding:

http://rev.rubyforge.org/rdoc/

Basically, sleep is perfectly fine if you want your process to go to sleep without having anything else going on in the background. If you ever want to do other things, though, like sleep and also wait for TCP connections or a filehandle to become readable, then you're going to have to use an event loop. So, why not just use one at the beginning?

The flow of your application will be:

main {
   Timer->new( after => 0, every => 60 seconds, run => { <do your work> } )
   loop();
}

When you want to do other stuff, you just create the watcher, and it happens for you. (The jobs that you are running can also create watchers.)

jrockway
A: 

It wont use CPU while it is sleeping but if you are sleeping for a long time I would be more concerned of the running ruby interpreter holding up memory while it wasn't doing anything. This is not that big of a deal tho.

Corban Brook
+2  A: 

Using sleep is likely OK for quick and dirty things. But for things that need a bit more robustness or reliability I suggest that sleep is evil :) The problem with sleeping is that the thread is (I'm assuming Windows here...) is truly asleep - the scheduler will not run the thread until some time after sleep interval has passed.

During this time, the thread will not wake up for anything. This means it cannot be canceled, or wake up to process some kind of event. Of course, the process can be killed, but that doesn't give the sleeping thread an opportunity to wake up and clean anything up.

I'm not familiar with Ruby, but I assume it has some kind of facility for waiting on multiple things. If you can, I suggest that instead of using sleep, you waint on two things\

  1. A timer that wakes the thread periodically to do its work.
  2. An event that is set when he process needs to cancel or quite (trapping control-C for example).

It would be even better if there is some kind of event that can be used to signal the need to do work. This would avoid polling on a timer. This generally leads to lower resource utilization and a more responsive system.

Foredecker
+1  A: 

While sleep(timeout) is perfectly appropriate for some designs, there's one important caveat to bear in mind.

Ruby installs signal handlers with SA_RESTART (see here), meaning that your sleep (or equivalent select(nil, nil, nil, timeout)) cannot easily be interrupted. Your signal handler will fire, but the program will go right back to sleep. This may be inconvenient if you wished to react timely to, say, a SIGTERM.

Consider that ...

#! /usr/bin/ruby
Signal.trap("USR1") { puts "Hey, wake up!" }
Process.fork() { sleep 2 and Process.kill("USR1", Process.ppid) }
sleep 30
puts "Zzz.  I enjoyed my nap."

... will take about 30 seconds to execute, rather than 2.

As a workaround, you might instead throw an exception in your signal handler, which would interrupt the sleep (or anything else!) above. You might also switch to a select-based loop and use a variant of the self-pipe trick to wake up "early" upon receipt of a signal. As others have pointed out, fully-featured event libraries are available, too.

pilcrow