Class: Unicorn::Worker

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

Overview

This class and its members can be considered a stable interface and will not change in a backwards-incompatible fashion between releases of unicorn. Knowledge of this class is generally not not needed for most users of unicorn.

Some users may want to access it in the before_fork/after_fork hooks. See the Unicorn::Configurator RDoc for examples.

Constant Summary collapse

PER_DROP =
Raindrops::PAGE_SIZE / Raindrops::SIZE
DROPS =
[]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nr) ⇒ Worker

Returns a new instance of Worker.


19
20
21
22
23
24
25
26
27
# File 'lib/unicorn/worker.rb', line 19

def initialize(nr)
  drop_index = nr / PER_DROP
  @raindrop = DROPS[drop_index] ||= Raindrops.new(PER_DROP)
  @offset = nr % PER_DROP
  @raindrop[@offset] = 0
  @nr = nr
  @switched = false
  @to_io, @master = Unicorn.pipe
end

Instance Attribute Details

#nrObject

:stopdoc:


13
14
15
# File 'lib/unicorn/worker.rb', line 13

def nr
  @nr
end

#switchedObject

:stopdoc:


13
14
15
# File 'lib/unicorn/worker.rb', line 13

def switched
  @switched
end

#to_ioObject (readonly)

IO.select-compatible


14
15
16
# File 'lib/unicorn/worker.rb', line 14

def to_io
  @to_io
end

Instance Method Details

#==(other_nr) ⇒ Object

worker objects may be compared to just plain Integers


89
90
91
# File 'lib/unicorn/worker.rb', line 89

def ==(other_nr) # :nodoc:
  @nr == other_nr
end

#atfork_childObject

:nodoc:


29
30
31
32
# File 'lib/unicorn/worker.rb', line 29

def atfork_child # :nodoc:
  # we _must_ close in child, parent just holds this open to signal
  @master = @master.close
end

#atfork_parentObject

parent does not read


40
41
42
# File 'lib/unicorn/worker.rb', line 40

def atfork_parent # :nodoc:
  @to_io = @to_io.close
end

#closeObject

called in both the master (reaping worker) and worker (SIGQUIT handler)


104
105
106
107
# File 'lib/unicorn/worker.rb', line 104

def close # :nodoc:
  @master.close if @master
  @to_io.close if @to_io
end

#fake_sig(sig) ⇒ Object

call a signal handler immediately without triggering EINTR We do not use the more obvious Process.kill(sig, $$) here since that signal delivery may be deferred. We want to avoid signal delivery while the Rack app.call is running because some database drivers (e.g. ruby-pg) may cancel pending requests.


49
50
51
52
53
54
# File 'lib/unicorn/worker.rb', line 49

def fake_sig(sig) # :nodoc:
  old_cb = trap(sig, "IGNORE")
  old_cb.call
ensure
  trap(sig, old_cb)
end

#kgio_tryacceptObject

this only runs when the Rack app.call is not running act like a listener


74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/unicorn/worker.rb', line 74

def kgio_tryaccept # :nodoc:
  case buf = @to_io.kgio_tryread(4)
  when String
    # unpack the buffer and trigger the signal handler
    signum = buf.unpack('l')
    fake_sig(signum[0])
    # keep looping, more signals may be queued
  when nil # EOF: master died, but we are at a safe place to exit
    fake_sig(:QUIT)
  when :wait_readable # keep waiting
    return false
  end while true # loop, as multiple signals may be sent
end

#quitObject

master fakes SIGQUIT using this


35
36
37
# File 'lib/unicorn/worker.rb', line 35

def quit # :nodoc:
  @master = @master.close if @master
end

#soft_kill(sig) ⇒ Object

master sends fake signals to children


57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/unicorn/worker.rb', line 57

def soft_kill(sig) # :nodoc:
  case sig
  when Integer
    signum = sig
  else
    signum = Signal.list[sig.to_s] or
        raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
  end
  # writing and reading 4 bytes on a pipe is atomic on all POSIX platforms
  # Do not care in the odd case the buffer is full, here.
  @master.kgio_trywrite([signum].pack('l'))
rescue Errno::EPIPE
  # worker will be reaped soon
end

#tickObject

called in the master process


99
100
101
# File 'lib/unicorn/worker.rb', line 99

def tick # :nodoc:
  @raindrop[@offset]
end

#tick=(value) ⇒ Object

called in the worker process


94
95
96
# File 'lib/unicorn/worker.rb', line 94

def tick=(value) # :nodoc:
  @raindrop[@offset] = value
end

#user(user, group = nil) ⇒ Object

In most cases, you should be using the Unicorn::Configurator#user directive instead. This method should only be used if you need fine-grained control of exactly when you want to change permissions in your after_fork hooks.

Changes the worker process to the specified user and group This is only intended to be called from within the worker process from the after_fork hook. This should be called in the after_fork hook after any privileged functions need to be run (e.g. to set per-worker CPU affinity, niceness, etc)

Any and all errors raised within this method will be propagated directly back to the caller (usually the after_fork hook. These errors commonly include ArgumentError for specifying an invalid user/group and Errno::EPERM for insufficient privileges


126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/unicorn/worker.rb', line 126

def user(user, group = nil)
  # we do not protect the caller, checking Process.euid == 0 is
  # insufficient because modern systems have fine-grained
  # capabilities.  Let the caller handle any and all errors.
  uid = Etc.getpwnam(user).uid
  gid = Etc.getgrnam(group).gid if group
  Unicorn::Util.chown_logs(uid, gid)
  if gid && Process.egid != gid
    Process.initgroups(user, gid)
    Process::GID.change_privilege(gid)
  end
  Process.euid != uid and Process::UID.change_privilege(uid)
  @switched = true
end