Module: Pigeon::Support

Extended by:
Support
Included in:
Support
Defined in:
lib/pigeon/support.rb

Instance Method Summary collapse

Instance Method Details

#daemonize(logger = nil) ⇒ Object

Uses the double-fork method to create a fully detached background process. Returns the process ID of the created process. May throw an exception if these processes could not be created.



7
8
9
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/pigeon/support.rb', line 7

def daemonize(logger = nil)
  delay = 10
  rfd, wfd = IO.pipe
  
  forked_pid = fork do
    rfd.close

    supervisor_pid = fork do
      relaunch = true
      
      while (relaunch)
        daemon_pid = fork do
          begin
            yield
          rescue SystemExit
            # Forced exit from supervisor process
          rescue Object => e
            if (logger)
              logger.error("Terminated with Exception: [#{e.class}] #{e}")
              logger.error(e.backtrace.join("\n"))

              Thread.list.each do |thread|
                logger.error("Stack trace of current threads")
                logger.error(thread.inspect)
                
                if (thread.backtrace)
                  logger.error("\t" + thread.backtrace.join("\n\t"))
                end
              end
            end

            exit(-1)
          end
        end

        begin
          interrupted = false

          Signal.trap('INT') do
            interrupted = true
            Process.kill('INT', daemon_pid)

            relaunch = false
          end

          _, status = Process.wait2(daemon_pid)

          if (interrupted)
            logger.info("Supervisor #{Process.pid} received termination signal, shut down child #{daemon_pid}.")
          end

          # A non-zero exit status indicates some sort of error, so the
          # process will be relaunched after a short delay.
          relaunch = (status != 0)

        ensure
          # Reset Signal handler before forking again
          Signal.trap('INT') do
          end
        end
        
        if (relaunch)
          begin
            logger.info("Supervisor #{Process.pid} will relaunch in %d seconds" % delay)
            sleep(delay)

          rescue Interrupt
            logger.info("Supervisor #{Process.pid} abandoing restart because of termination")

            relaunch = false
          end
        else
          logger.info("Terminated normally.")
        end
      end
    end

    wfd.puts(supervisor_pid)
    wfd.flush
    wfd.close
  end

  Process.wait2(forked_pid)

  daemon_pid = rfd.readline
  rfd.close
  
  daemon_pid.to_i
end

#find_writable_directory(*list) ⇒ Object

Finds the first directory in the given list that exists and is writable. Returns nil if none match.



103
104
105
106
107
# File 'lib/pigeon/support.rb', line 103

def find_writable_directory(*list)
  list.flatten.compact.find do |dir|
    File.exist?(dir) and File.writable?(dir)
  end
end

#nap(time) ⇒ Object



97
98
99
# File 'lib/pigeon/support.rb', line 97

def nap(time)
  select(nil, nil, nil, time.to_f)
end

#unique_idObject

Returns a unique 160-bit identifier for this engine expressed as a 40 character hexadecimal string. The first 32-bit sequence is a timestamp so these numbers increase over time and can be used to identify when a particular instance was launched. For informational purposes, the name of the host is appended to help identify the origin of the ident.



114
115
116
117
118
119
120
121
122
# File 'lib/pigeon/support.rb', line 114

def unique_id
  '%8x%s@%s' % [
    Time.now.to_i,
    Digest::SHA1.hexdigest(
      '%.8f%8x' % [ Time.now.to_f, rand(1 << 32) ]
    )[0, 32],
    Socket.gethostname
  ]
end