Class: DeepTest::Warlock

Inherits:
Object
  • Object
show all
Defined in:
lib/deep_test/warlock.rb

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Warlock

Returns a new instance of Warlock.



3
4
5
6
7
8
# File 'lib/deep_test/warlock.rb', line 3

def initialize(options)
  @options = options
  @demons_semaphore = Mutex.new
  @demons = []
  @reapers = []
end

Instance Method Details

#any_running?Boolean

Returns:

  • (Boolean)


98
99
100
101
102
# File 'lib/deep_test/warlock.rb', line 98

def any_running?
  @demons_semaphore.synchronize do
    @demons.any? {|name, pid| running?(pid)}
  end
end

#close_open_network_connectionsObject



46
47
48
49
50
51
52
53
# File 'lib/deep_test/warlock.rb', line 46

def close_open_network_connections
  ObjectSpace.each_object(BasicSocket) do |sock|
    begin
      sock.close 
    rescue IOError
    end
  end
end

#demon_countObject



55
56
57
58
59
# File 'lib/deep_test/warlock.rb', line 55

def demon_count
  @demons_semaphore.synchronize do
    @demons.size
  end
end

#exit_when_none_runningObject



82
83
84
85
86
87
88
# File 'lib/deep_test/warlock.rb', line 82

def exit_when_none_running
  Thread.new do
    wait_for_all_to_finish
    DeepTest.logger.debug { "exiting #{Process.pid} with all demons finished" }
    exit(0)
  end
end

#running?(pid) ⇒ Boolean

stolen from daemons

Returns:

  • (Boolean)


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/deep_test/warlock.rb', line 105

def running?(pid)
  # Check if process is in existence
  # The simplest way to do this is to send signal '0'
  # (which is a single system call) that doesn't actually
  # send a signal
  begin
    Process.kill(0, pid)
    return true
  rescue Errno::ESRCH
    return false
  rescue ::Exception   # for example on EPERM (process exists but does not belong to us)
    return true
  #rescue Errno::EPERM
  #  return false
  end
end

#start(name, demon, *demon_args) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/deep_test/warlock.rb', line 10

def start(name, demon, *demon_args)
  # Not synchronizing for the fork seems to cause
  # random errors (Bus Error, Segfault, and GC non-object)
  # in Beachhead processes.
  #
  begin
    pid = nil
    @demons_semaphore.synchronize do 
      pid = fork do
        # Fork leaves the semaphore locked and we'll never make it
        # to end of synchronize block.
        #
        # The Ruby 1.8.6 C mutex implementation automatically treats
        # a mutex locked by a dead thread as unlocked and will raise
        # an error if we try to unlock it from this thread.
        #
        @demons_semaphore.unlock if @demons_semaphore.locked?

        close_open_network_connections
        demon.forked name, @options, demon_args

        exit
      end

      raise "fatal: fork returned nil" if pid.nil?
      add_demon name, pid
    end

    launch_reaper_thread name, pid

  rescue => e
    puts "exception starting #{name}: #{e}"
    puts "\t" + e.backtrace.join("\n\t")
  end
end

#stop_demonsObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/deep_test/warlock.rb', line 61

def stop_demons
  DeepTest.logger.debug { "stopping all demons" }
  receivers = @demons_semaphore.synchronize do
    @demons.reverse
  end

  receivers.reverse.each do |demon|
    name, pid = demon
    if running?(pid)
      DeepTest.logger.debug { "Sending SIGTERM to #{name}, #{pid}" }
      Process.kill("TERM", pid)
    end
  end
  DeepTest.logger.debug { "Warlock: Stopped all receivers" }

  DeepTest.logger.debug { "waiting for reapers" }
  @reapers.each {|r| r.join}

  DeepTest.logger.debug { "Warlock: done reaping processes" }
end

#wait_for_all_to_finishObject



90
91
92
93
94
95
96
# File 'lib/deep_test/warlock.rb', line 90

def wait_for_all_to_finish
  loop do
    Thread.pass
    return unless any_running?
    sleep(0.01)
  end
end