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/listeners.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, Listeners, MemInfo, MemoryPage, Message, MessageSocket, RequestEntityTooLargeError, RequestURITooLongError, SelectWaiter, Service, 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
- REFORKING_AVAILABLE =
Pitchfork::CHILD_SUBREAPER_AVAILABLE || Process.pid == 1
- VERSION =
"0.16.0"
Class Method Summary collapse
-
.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.
- .clean_fork(setpgid: true, &block) ⇒ Object
-
.listener_names ⇒ Object
returns an array of strings representing TCP listen socket addresses and Unix domain socket paths.
- .log_error(logger, prefix, exc) ⇒ Object
-
.prevent_fork(&block) ⇒ Object
Prevent Pitchfork from forking new children for the duration of the block.
- .socketpair ⇒ Object
- .time_now(int = false) ⇒ Object
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
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/pitchfork.rb', line 139 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_names ⇒ Object
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) = exc. = .dump if /[[:cntrl:]]/ =~ logger.error "#{prefix}: #{} (#{exc.class})" exc.backtrace.each { |line| logger.error(line) } 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 |
.socketpair ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/pitchfork.rb', line 122 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
181 182 183 |
# File 'lib/pitchfork.rb', line 181 def time_now(int = false) Process.clock_gettime(Process::CLOCK_MONOTONIC, int ? :second : :float_second) end |