Module | Timeout |
In: |
lib/active_ldap/timeout.rb
lib/active_ldap/timeout_stub.rb |
A forking timeout implementation that relies on signals to interrupt blocking I/O instead of passing that code to run in a separate process.
A process is fork()ed, sleeps for sec, then sends a ALRM signal to the Process.ppid process. ALRM is used to avoid conflicts with sleep()
This overwrites any signal
# File lib/active_ldap/timeout.rb, line 14 14: def Timeout.alarm(sec, exception=Timeout::Error, &block) 15: return block.call if sec == nil or sec.zero? 16: 17: 18: # Trap an alarm in case it comes before we're ready 19: orig_alrm = trap(:ALRM, 'IGNORE') 20: 21: # Setup a fallback in case of a race condition of an 22: # alarm before we set the other trap 23: trap(:ALRM) do 24: # Don't leave zombies 25: Process.wait2() 26: # Restore the original handler 27: trap('ALRM', orig_alrm) 28: # Now raise an exception! 29: raise exception, 'execution expired' 30: end 31: 32: # Spawn the sleeper 33: pid = Process.fork { 34: begin 35: # Sleep x seconds then send SIGALRM 36: sleep(sec) 37: # Send alarm! 38: Process.kill(:ALRM, Process.ppid) 39: end 40: exit! 0 41: } 42: 43: # Setup the real handler 44: trap(:ALRM) do 45: # Make sure we clean up any zombies 46: Process.waitpid(pid) 47: # Restore the original handler 48: trap(:ALRM, orig_alrm) 49: # Now raise an exception! 50: raise exception, 'execution expired' 51: end 52: 53: begin 54: # Run the code! 55: return block.call 56: ensure 57: # Restore old alarm handler since we're done 58: trap(:ALRM, orig_alrm) 59: # Make sure the process is dead 60: # This may be run twice (trap occurs during execution) so ignore ESRCH 61: Process.kill(:TERM, pid) rescue Errno::ESRCH 62: # Don't leave zombies 63: Process.waitpid(pid) rescue Errno::ECHILD 64: end 65: end