class ActionDispatch::DebugLocks

control. It should only be enabled when in use, and removed thereafter.
This middleware exposes operational details of the server, with no access
human consumption, and should be expected to change between releases.
NOTE: The formatting and content of this middleware’s output is intended for
identified, but should be visible in the displayed backtraces.
other external lock or blocking I/O call. These cannot be automatically
Generally a deadlock will be caused by the interlock conflicting with some
backtrace.
interlock, which lock level they are holding or awaiting, and their current
route ‘/rails/locks` will show a summary of all threads currently known to the
After restarting the application and re-triggering the deadlock condition, the
config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
`config/application.rb`:
To use it, insert it near the top of the middleware stack, using
This middleware can be used to diagnose deadlocks in the autoload interlock.
# Action Dispatch DebugLocks

def blocked_by?(victim, blocker, all_threads)

def blocked_by?(victim, blocker, all_threads)
  return false if victim.equal?(blocker)
  case victim[:sleeper]
  when :start_sharing
    blocker[:exclusive] ||
      (!victim[:waiting] && blocker[:compatible] && !blocker[:compatible].include?(false))
  when :start_exclusive
    blocker[:sharing] > 0 ||
      blocker[:exclusive] ||
      (blocker[:compatible] && !blocker[:compatible].include?(victim[:purpose]))
  when :yield_shares
    blocker[:exclusive]
  when :stop_exclusive
    blocker[:exclusive] ||
      victim[:compatible] &&
      victim[:compatible].include?(blocker[:purpose]) &&
      all_threads.all? { |other| !other[:compatible] || blocker.equal?(other) || other[:compatible].include?(blocker[:purpose]) }
  end
end

def call(env)

def call(env)
  req = ActionDispatch::Request.new env
  if req.get?
    path = req.path_info.chomp("/")
    if path == @path
      return render_details(req)
    end
  end
  @app.call(env)
end

def initialize(app, path = "/rails/locks")

def initialize(app, path = "/rails/locks")
  @app = app
  @path = path
end

def render_details(req)

def render_details(req)
  threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
    # The Interlock itself comes to a complete halt as long as this block is
    # executing. That gives us a more consistent picture of everything, but creates
    # a pretty strong Observer Effect.
    #
    # Most directly, that means we need to do as little as possible in this block.
    # More widely, it means this middleware should remain a strictly diagnostic tool
    # (to be used when something has gone wrong), and not for any sort of general
    # monitoring.
    raw_threads.each.with_index do |(thread, info), idx|
      info[:index] = idx
      info[:backtrace] = thread.backtrace
    end
    raw_threads
  end
  str = threads.map do |thread, info|
    if info[:exclusive]
      lock_state = +"Exclusive"
    elsif info[:sharing] > 0
      lock_state = +"Sharing"
      lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
    else
      lock_state = +"No lock"
    end
    if info[:waiting]
      lock_state << " (yielded share)"
    end
    msg = +"Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}]  #{lock_state}\n"
    if info[:sleeper]
      msg << "  Waiting in #{info[:sleeper]}"
      msg << " to #{info[:purpose].to_s.inspect}" unless info[:purpose].nil?
      msg << "\n"
      if info[:compatible]
        compat = info[:compatible].map { |c| c == false ? "share" : c.to_s.inspect }
        msg << "  may be pre-empted for: #{compat.join(', ')}\n"
      end
      blockers = threads.values.select { |binfo| blocked_by?(info, binfo, threads.values) }
      msg << "  blocked by: #{blockers.map { |i| i[:index] }.join(', ')}\n" if blockers.any?
    end
    blockees = threads.values.select { |binfo| blocked_by?(binfo, info, threads.values) }
    msg << "  blocking: #{blockees.map { |i| i[:index] }.join(', ')}\n" if blockees.any?
    msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
  end.join("\n\n---\n\n\n")
  [200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
          Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
end