unless defined? Thread.exclusive

def Thread.exclusive
  old = Thread.critical
  Thread.critical = true
  yield
ensure
  Thread.critical = old
end

end

module FSDB

class Mutex

def initialize
  @waiting = []
  @locked = nil
end

def locked?
  @locked
end

def try_lock
  Thread.exclusive do
    if not @locked
      @locked = Thread.current
      true
    end
  end
end

def lock
  thread = Thread.current
  Thread.exclusive do
    if @locked
      @waiting.push thread
      Thread.stop
      unless @locked == thread
        raise ThreadError, "queue was jumped"
      end
    else
      @locked = thread
    end
    self
  end
end

def unlock
  return unless @locked

  t = Thread.exclusive { wake_next_waiter }

  begin
    t.run if t
  rescue ThreadError
  end
  self
end

def synchronize
  lock
  yield
ensure
  unlock
end

def remove_dead
  Thread.exclusive do
    @waiting = @waiting.select {|t| t.alive?}
    wake_next_waiter if @locked and not @locked.alive?
  end
end

private
def wake_next_waiter
  t = @waiting.shift
  t.wakeup if t
  @locked = t
rescue ThreadError
  retry
end

module ForkSafely
  def fork
    super do
      ObjectSpace.each_object(Mutex) { |m| m.remove_dead }
      yield
    end
  end
end

end

module ForkSafely

include Mutex::ForkSafely

end include ForkSafely

end # module FSDB

if __FILE__ == $0

# Stress test is in fsdb/test/test-mutex.rb. This is just to show fork usage.

include FSDB::ForkSafely

m = FSDB::Mutex.new

Thread.new { m.synchronize { sleep 1 } }

fork do
  m.synchronize { puts "Didn't get here if you used standard mutex or fork." }
end

m.synchronize { puts "Got here." }

Process.wait

end