This might be a nice addition to the Ruby synchronisation primitives.
I welcome comments and improvements.

(shamelessly glarked and adapted from thread.rb)

---8<---
#!/usr/bin/ruby -w

class Semaphore
  def initialize max = 1, init = max
    fail ArgumentError, "maximum value must be > 0" unless max > 0
    fail ArgumentError, "initial value must be >= 0" unless init >= 0
    fail ArgumentError, "initial value must be <= maximum" unless init <= max
    @waiting = []
    @maximum = max
    @value = init
  end

  def signal
    return if @value == @maximum
    Thread.critical = true
    @value += 1
    begin
      t = @waiting.shift
      t.run if t
    rescue ThreadError
      # nothing: tried to run a dead thread
    end
    Thread.critical = false
    self
  end

  def wait
    while(Thread.critical = true; @value == 0)
      @waiting.push Thread.current
      Thread.stop # implies Thread.critical = false
    end
    @value -= 1
    Thread.critical = false
    self
  end

  def synchronize
    wait
    begin
      yield
    ensure
      signal
    end
  end
end


##########################################################################
if __FILE__ == $0

srand
def snooze
  sleep rand 0
end

class Test1
  def initialize
    puts "* N processes contending for M resources, where N > M"
    threads = []
    @sem = Semaphore.new 3      # 3 resources available

    for i in 0..9 do
      threads.push Thread.start{ client i }
    end

    threads.each{ |t| t.join }
  end

  def client i
    puts "client #{i}: wait"
    @sem.wait
    puts "client #{i}: running"
    snooze
    puts "client #{i}: signal"
    @sem.signal
  end
end

class Test2
  def initialize
    puts "* synchronous producer/consumer"
    threads = []
    @sin  = Semaphore.new 1, 0
    @sout = Semaphore.new 1, 0
    @shared = 0
    @N = 10

    threads.push Thread.start{ producer }
    threads.push Thread.start{ consumer }
    threads.each{ |t| t.join }
  end

  def producer
    for i in 1..@N do
      snooze
      @shared = i
      puts "produced #{i}"
      @sin.signal
      @sout.wait
    end
  end

  def consumer
    for i in 1..@N do
      snooze
      @sin.wait
      puts "consumed #{@shared}"
      @sout.signal
    end
  end
end

class Test3
  def initialize
    puts "* asynchronous producer/consumer with finite (circular) buffer"
    threads = []
    @MAX = 3
    @buffer = Array.new @MAX, 0
    @input = 0
    @output = 0
    @elements = Semaphore.new @MAX, 0
    @spaces   = Semaphore.new @MAX
    @N = 10

    threads.push Thread.start{ producer }
    threads.push Thread.start{ consumer }
    threads.each{ |t| t.join }
  end

  def producer
    for i in 1..@N do
      snooze
      @spaces.wait
      @buffer[@input] = i
      @input = (@input + 1) % @MAX
      puts "produced #{i}"
      @elements.signal
    end
  end

  def consumer
    for i in 1..@N do
      snooze
      @elements.wait
      n = @buffer[@output]
      @output = (@output + 1) % @MAX
      puts "consumed #{n}"
      @spaces.signal
    end
  end
end

class Test4
  def initialize
    puts "* transfer of control (coroutines)"
    threads = []
    @a = Semaphore.new 1, 0
    @b = Semaphore.new 1, 0
    @c = Semaphore.new 1, 0
    threads.push Thread.new{ parent }
    threads.push Thread.new{ process_a }
    threads.push Thread.new{ process_b }
    threads.each{ |t| t.join }
  end

  def parent
    puts "P: 1"
    @a.signal
    @c.wait
    puts "P: 6"
  end

  def process_a
    @a.wait
    puts "A: 2"
    @b.signal
    @a.wait
    puts "A: 4"
    @b.signal
  end

  def process_b
    @b.wait
    puts "B: 3"
    @a.signal
    @b.wait
    puts "B: 5"
    @c.signal
  end
end

Test1.new
Test2.new
Test3.new
Test4.new

end
---8<---

regards,
Michel