Class: Unicorn::Worker
- Inherits:
-
Object
- Object
- Unicorn::Worker
- 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
-
#nr ⇒ Object
:stopdoc:.
-
#switched ⇒ Object
:stopdoc:.
-
#to_io ⇒ Object
readonly
IO.select-compatible.
Instance Method Summary collapse
-
#==(other_nr) ⇒ Object
worker objects may be compared to just plain Integers.
-
#atfork_child ⇒ Object
:nodoc:.
-
#atfork_parent ⇒ Object
parent does not read.
-
#close ⇒ Object
called in both the master (reaping worker) and worker (SIGQUIT handler).
-
#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.
-
#initialize(nr) ⇒ Worker
constructor
A new instance of Worker.
-
#kgio_tryaccept ⇒ Object
this only runs when the Rack app.call is not running act like a listener.
-
#quit ⇒ Object
master fakes SIGQUIT using this.
-
#soft_kill(sig) ⇒ Object
master sends fake signals to children.
-
#tick ⇒ Object
called in the master process.
-
#tick=(value) ⇒ Object
called in the worker process.
-
#user(user, group = nil) ⇒ Object
In most cases, you should be using the Unicorn::Configurator#user directive instead.
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
#nr ⇒ Object
:stopdoc:
13 14 15 |
# File 'lib/unicorn/worker.rb', line 13 def nr @nr end |
#switched ⇒ Object
:stopdoc:
13 14 15 |
# File 'lib/unicorn/worker.rb', line 13 def switched @switched end |
#to_io ⇒ Object (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_child ⇒ Object
: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_parent ⇒ Object
parent does not read
40 41 42 |
# File 'lib/unicorn/worker.rb', line 40 def atfork_parent # :nodoc: @to_io = @to_io.close end |
#close ⇒ Object
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_tryaccept ⇒ Object
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 |
#quit ⇒ Object
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 |
#tick ⇒ Object
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 |