Skip to content

Instantly share code, notes, and snippets.

@tkareine
Created December 13, 2010 21:56
Show Gist options
  • Save tkareine/739662 to your computer and use it in GitHub Desktop.
Save tkareine/739662 to your computer and use it in GitHub Desktop.
A simple count down latch in Ruby
#!/usr/bin/env ruby
# coding: utf-8
require 'thread'
class CountDownLatch
attr_reader :count
def initialize(to)
@count = to.to_i
raise ArgumentError, "cannot count down from negative integer" unless @count >= 0
@lock = Mutex.new
@condition = ConditionVariable.new
end
def count_down
@lock.synchronize do
@count -= 1 if @count > 0
@condition.broadcast if @count == 0
end
end
def wait
@lock.synchronize do
@condition.wait(@lock) while @count > 0
end
end
end
if $0 == __FILE__
require 'test/unit'
class CountDownLatchTest < Test::Unit::TestCase
def test_requires_positive_count
assert_raise(ArgumentError) { CountDownLatch.new(-1) }
end
def test_basic_latch_usage
latch = CountDownLatch.new(1)
name = "foo"
Thread.new do
name = "bar"
latch.count_down
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_basic_latch_usage_inverted
latch = CountDownLatch.new(1)
name = "foo"
Thread.new do
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
name = "bar"
latch.count_down
end
def test_count_down_from_zero_skips_wait
latch = CountDownLatch.new(0)
latch.wait
assert_equal(0, latch.count)
end
def test_count_down_twice_with_thread
latch = CountDownLatch.new(2)
name = "foo"
Thread.new do
latch.count_down
name = "bar"
latch.count_down
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_count_down_twice_with_two_parallel_threads
latch = CountDownLatch.new(2)
name = "foo"
Thread.new { latch.count_down }
Thread.new do
name = "bar"
latch.count_down
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_count_down_twice_with_two_chained_threads
latch = CountDownLatch.new(2)
name = "foo"
Thread.new do
latch.count_down
Thread.new do
name = "bar"
latch.count_down
end
end
latch.wait
assert_equal(0, latch.count)
assert_equal("bar", name)
end
def test_count_down_with_multiple_waiters
proceed_latch = CountDownLatch.new(2)
check_latch = CountDownLatch.new(2)
results = {}
Thread.new do
proceed_latch.wait
results[:first] = 1
check_latch.count_down
end
Thread.new do
proceed_latch.wait
results[:second] = 2
check_latch.count_down
end
assert_equal({}, results)
proceed_latch.count_down
proceed_latch.count_down
check_latch.wait
assert_equal(0, proceed_latch.count)
assert_equal(0, check_latch.count)
assert_equal({:first => 1, :second => 2}, results)
end
def test_interleaved_latches
change_1_latch = CountDownLatch.new(1)
check_latch = CountDownLatch.new(1)
change_2_latch = CountDownLatch.new(1)
name = "foo"
Thread.new do
name = "bar"
change_1_latch.count_down
check_latch.wait
name = "man"
change_2_latch.count_down
end
change_1_latch.wait
assert_equal("bar", name)
check_latch.count_down
change_2_latch.wait
assert_equal("man", name)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment