Module: Pitchfork

Included in:
Configurator
Defined in:
lib/pitchfork.rb,
lib/pitchfork/info.rb,
lib/pitchfork/const.rb,
lib/pitchfork/flock.rb,
lib/pitchfork/tmpio.rb,
lib/pitchfork/worker.rb,
lib/pitchfork/chunked.rb,
lib/pitchfork/message.rb,
lib/pitchfork/version.rb,
lib/pitchfork/children.rb,
lib/pitchfork/mem_info.rb,
lib/pitchfork/tee_input.rb,
lib/pitchfork/http_parser.rb,
lib/pitchfork/http_server.rb,
lib/pitchfork/configurator.rb,
lib/pitchfork/soft_timeout.rb,
lib/pitchfork/stream_input.rb,
lib/pitchfork/http_response.rb,
lib/pitchfork/select_waiter.rb,
lib/pitchfork/shared_memory.rb,
lib/pitchfork/socket_helper.rb,
lib/pitchfork/refork_condition.rb,
ext/pitchfork_http/httpdate.c,
ext/pitchfork_http/pitchfork_http.c

Overview

:enddoc:

Defined Under Namespace

Modules: Const, HttpResponse, Info, ReforkCondition, SharedMemory, SocketHelper, SoftTimeout Classes: Children, Chunked, Configurator, Flock, HttpParser, HttpParserError, HttpServer, MemInfo, Message, MessageSocket, RequestEntityTooLargeError, RequestURITooLongError, SelectWaiter, StreamInput, TeeInput, TmpIO, Worker

Constant Summary collapse

ClientShutdown =

Raised inside TeeInput when a client closes the socket inside the application dispatch. This is always raised with an empty backtrace since there is nothing in the application stack that is responsible for client shutdowns/disconnects. This exception is visible to Rack applications. This is a subclass of the standard EOFError class and applications should not rescue it explicitly, but rescue EOFError instead. Such an error is likely an indication that the reverse proxy in front of Pitchfork isn’t properly buffering requests.

Class.new(EOFError)
BootFailure =
Class.new(StandardError)
ForkFailure =
Class.new(StandardError)
FORK_LOCK =

:stopdoc:

Monitor.new
F_SETPIPE_SZ =
1031
REFORKING_AVAILABLE =
Pitchfork::CHILD_SUBREAPER_AVAILABLE || Process.pid == 1
VERSION =
"0.11.0"

Class Method Summary collapse

Class Method Details

.builder(ru, op) ⇒ Object

This returns a lambda to pass in as the app, this does not “build” the app The returned lambda will be called when it is time to build the app.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/pitchfork.rb', line 77

def builder(ru, op)
  # allow Configurator to parse cli switches embedded in the ru file
  op = Pitchfork::Configurator::RACKUP.merge!(:file => ru, :optparse => op)
  if ru =~ /\.ru$/ && !defined?(Rack::Builder)
    abort "rack and Rack::Builder must be available for processing #{ru}"
  end

  # always called after config file parsing, may be called after forking
  lambda do |_, server|
    inner_app = case ru
    when /\.ru$/
      raw = File.read(ru)
      raw.sub!(/^__END__\n.*/, '')
      eval("Rack::Builder.new {(\n#{raw}\n)}.to_app", TOPLEVEL_BINDING, ru)
    else
      require ru
      Object.const_get(File.basename(ru, '.rb').capitalize)
    end

    Rack::Builder.new do
      use(Rack::ContentLength)
      use(Pitchfork::Chunked)
      use(Rack::Lint) if ENV["RACK_ENV"] == "development"
      use(Rack::TempfileReaper)
      run inner_app
    end.to_app
  end
end

.clean_fork(setpgid: true, &block) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/pitchfork.rb', line 158

def clean_fork(setpgid: true, &block)
  if pid = FORK_LOCK.synchronize { Process.fork }
    if setpgid
      Process.setpgid(pid, pid) # Make into a group leader
    end
    return pid
  end

  begin
    # Pitchfork recursively refork the worker processes.
    # Because of this we need to unwind the stack before resuming execution
    # in the child, otherwise on each generation the available stack space would
    # get smaller and smaller until it's basically 0.
    #
    # The very first version of this method used to call fork from a new
    # thread, however this can cause issues with some native gems that rely on
    # pthread_atfork(3) or pthread_mutex_lock(3), as the new main thread would
    # now be different.
    #
    # A second version used to fork from a new fiber, but fibers have a much smaller
    # stack space (https://bugs.ruby-lang.org/issues/3187), so it would break large applications.
    #
    # The latest version now use `throw` to unwind the stack after the fork, it however
    # restrict it to be called only inside `handle_clean_fork`.
    if Thread.current[:pitchfork_handle_clean_fork]
      throw self, block
    else
      while block
        block = catch(self) do
          Thread.current[:pitchfork_handle_clean_fork] = true
          block.call
          nil
        end
      end
    end
  rescue
    abort
  else
    exit
  end
end

.listener_namesObject

returns an array of strings representing TCP listen socket addresses and Unix domain socket paths. This is useful for use with Raindrops::Middleware under Linux: yhbt.net/raindrops/



109
110
111
112
113
# File 'lib/pitchfork.rb', line 109

def listener_names
  Pitchfork::HttpServer::LISTENERS.map do |io|
    Pitchfork::SocketHelper.sock_name(io)
  end
end

.log_error(logger, prefix, exc) ⇒ Object



115
116
117
118
119
120
# File 'lib/pitchfork.rb', line 115

def log_error(logger, prefix, exc)
  message = exc.message
  message = message.dump if /[[:cntrl:]]/ =~ message
  logger.error "#{prefix}: #{message} (#{exc.class})"
  exc.backtrace.each { |line| logger.error(line) }
end

.pipeObject

:nodoc:



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

def pipe # :nodoc:
  IO.pipe.each do |io|
    # shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
    # limits.
    if defined?(F_SETPIPE_SZ)
      begin
        io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
      rescue Errno::EINVAL
        # old kernel
      rescue Errno::EPERM
        # resizes fail if Linux is close to the pipe limit for the user
        # or if the user does not have permissions to resize
      end
    end
  end
end

.prevent_fork(&block) ⇒ Object

Prevent Pitchfork from forking new children for the duration of the block.

If you have background threads calling code that synchronize native locks, while the GVL is released, forking while they are held could leak to corrupted children.

One example of this is ‘getaddrinfo(3)`, so opening a connection from a background thread has a chance to produce stuck children.

To avoid this you can wrap such code in ‘Pitchfork.prevent_fork`:

def heartbeat_thread

@heartbeat_thread ||= Thread.new do
  loop do
    Pitchfork.prevent_fork do
      heartbeat
    end
    sleep 10
  end
end

end



68
69
70
# File 'lib/pitchfork.rb', line 68

def prevent_fork(&block)
  FORK_LOCK.synchronize(&block)
end

.socketpairObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/pitchfork.rb', line 141

def socketpair
  pair = UNIXSocket.socketpair(@socket_type).map { |s| MessageSocket.new(s) }
  pair[0].close_write
  pair[1].close_read
  pair
rescue Errno::EPROTONOSUPPORT
  if @socket_type == :SOCK_SEQPACKET
    # macOS and very old linuxes don't support SOCK_SEQPACKET (SCTP).
    # In such case we can fallback to SOCK_STREAM (TCP)
    warn("SEQPACKET (SCTP) isn't supported, falling back to STREAM")
    @socket_type = :SOCK_STREAM
    retry
  else
    raise
  end
end

.time_now(int = false) ⇒ Object



200
201
202
# File 'lib/pitchfork.rb', line 200

def time_now(int = false)
  Process.clock_gettime(Process::CLOCK_MONOTONIC, int ? :second : :float_second)
end