Class: Loops::Worker

Inherits:
Object
  • Object
show all
Defined in:
lib/loops/worker.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, pm, engine, &blk) ⇒ Worker

Returns a new instance of Worker.

Raises:

  • (ArgumentError)


6
7
8
9
10
11
12
13
# File 'lib/loops/worker.rb', line 6

def initialize(name, pm, engine, &blk)
  raise ArgumentError, "Need a worker block!" unless block_given?

  @name = name
  @pm = pm
  @engine = engine
  @worker_block = blk
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



3
4
5
# File 'lib/loops/worker.rb', line 3

def name
  @name
end

#pidObject (readonly)

Returns the value of attribute pid.



4
5
6
# File 'lib/loops/worker.rb', line 4

def pid
  @pid
end

Instance Method Details

#loggerObject



15
16
17
# File 'lib/loops/worker.rb', line 15

def logger
  @pm.logger
end

#runObject



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
# File 'lib/loops/worker.rb', line 23

def run
  return if shutdown?
  if @engine == 'fork'
    # Enable COW-friendly garbage collector in Ruby Enterprise Edition
    # See http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow for more details
    if GC.respond_to?(:copy_on_write_friendly=)
      GC.copy_on_write_friendly = true
    end

    @pid = Kernel.fork do
      @pid = Process.pid
      normal_exit = false
      begin
        $0 = "loop worker: #{@name}\0"
        @worker_block.call
        normal_exit = true
        exit(0)
      rescue Exception => e
        message = SystemExit === e ? "exit(#{e.status})" : e.to_s
        if SystemExit === e and e.success?
          if normal_exit
            logger.info("Worker finished: normal return")
          else
            logger.info("Worker exited: #{message} at #{e.backtrace.first}")
          end
        else
          logger.fatal("Worker exited with error: #{message}\n  #{e.backtrace.join("\n  ")}")
        end
        logger.fatal("Terminating #{@name} worker: #{@pid}")
      end
    end
  elsif @engine == 'thread'
    @thread = Thread.start do
      @worker_block.call
    end
  else
    raise ArgumentError, "Invalid engine name: #{@engine}"
  end
rescue Exception => e
  logger.error("Exception from worker: #{e} at #{e.backtrace.first}")
end

#running?(verbose = false) ⇒ Boolean

Returns:

  • (Boolean)


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/loops/worker.rb', line 65

def running?(verbose = false)
  if @engine == 'fork'
    return false unless @pid
    begin
      Process.waitpid(@pid, Process::WNOHANG)
      res = Process.kill(0, @pid)
      logger.debug("KILL(#{@pid}) = #{res}") if verbose
      return true
    rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM => e
      logger.error("Exception from kill: #{e} at #{e.backtrace.first}") if verbose
      return false
    end
  elsif @engine == 'thread'
    @thread && @thread.alive?
  else
    raise ArgumentError, "Invalid engine name: #{@engine}"
  end
end

#shutdown?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/loops/worker.rb', line 19

def shutdown?
  @pm.shutdown?
end

#stop(force = false) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/loops/worker.rb', line 84

def stop(force = false)
  if @engine == 'fork'
    begin
      sig = force ? 'SIGKILL' : 'SIGTERM'
      logger.debug("Sending #{sig} to ##{@pid}")
      Process.kill(sig, @pid)
    rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM=> e
      logger.error("Exception from kill: #{e} at #{e.backtrace.first}")
    end
  elsif @engine == 'thread'
    force && !defined?(::JRuby) ? @thread.kill! : @thread.kill
  else
    raise ArgumentError, "Invalid engine name: #{@engine}"
  end
end