0

I am trying to limit the execution time of a ruby process using the following code:

trap("XCPU") do
  abort "Max Time exceeded"
end

Process.setrlimit(:CPU, 5)

loop do

end

The process does end but the trap code does not run (I just get 'killed' on the command line). However when I set the hard limit to a value greater than 5 the trap code runs

trap("XCPU") do
  abort "Max Time exceeded"
end

Process.setrlimit(:CPU, 5, 6)

loop do

end

Why is the first code not working?

Omar Khan
  • 412
  • 2
  • 12

1 Answers1

2

The XCPU signal (SIGXCPU) is only sent upon a soft limit. When the hard limit is reached, a KILL signal (SIGKILL) is sent instead. KILL signals cause the program to terminate immediately, and cannot be caught.

Taken from here:

The XCPU signal is sent to a process when it has used up the CPU for a duration that exceeds a certain predetermined user-settable value. The arrival of an XCPU signal provides the receiving process a chance to quickly save any intermediate results and to exit gracefully, before it is terminated by the operating system using the SIGKILL signal.

The KILL signal is sent to a process to cause it to terminate immediately. In contrast to SIGTERM and SIGINT, this signal cannot be caught or ignored, and the receiving process cannot perform any clean-up upon receiving this signal.

By calling Process.setrlimit without a second parameter, the hard limit defaults to being equal to the soft limit. As such, at least on your operating system, it seems the SIGKILL is being sent before a SIGXCPU can be handled by your trap block.

Here is a quick demo to show why the second approach always works:

t = Time.now
trap("XCPU") do
  abort "Max Time exceeded. Total running time: #{(Time.now - t).round} seconds"
end

Process.setrlimit(:CPU, 2, 5)

loop do
end

# => "Max Time exceeded. Total running time: 2 seconds"

The hard limit is not reached before the trap block executes, so the code runs as you expect.

Tom Lord
  • 27,404
  • 4
  • 50
  • 77