Ruby STM: Round the Third

Previously on this blog:

  1. Ruby STM: First Round
  2. Ruby STM: Round Two
  3. STM Deadlocks

Here’s what’s I’ve implemented since the last couple posts:

  • STM::Variable, a trivial transactional container for a single value
  • a replacement API for STM#or_else
  • deadlock detection
  • blocking retries

Specifically, STM#or_else has been replaced by STM.retries?, a class method which takes a block and returns true or false depending on whether the given sub-transaction retries. I’ve also added TrueClass#then_try and FalseClass#then_try, which yield to the given block and do nothing, respectively. Given this, we can write a new Ruby version of Audrey’s sample script:

require 'stm'

a = STM::Variable.new 0
still_running = STM::Variable.new 2

t1 = Thread.new {
  puts "Thread 1 started"
  STM.atomically {
    STM.retry until a[] > 5
    a[] = -1000
    still_running[] -= 1
  }
  puts "Thread 1 finished: a is >5 and reset to -1000"
}

t2 = Thread.new {
  puts "Thread 2 started"
  STM.atomically {
    STM.retries? {
      STM.retry until a[] > 100
    }.then_try {
      STM.retry until a[] < -100
    }
    still_running[] -= 1
  }
  puts "Thread 2 finished: a is now < -100"
}

while still_running[].nonzero?
  puts STM.atomically { saved = a[] ; a += 1 ; saved }
  sleep 1
end

t1.join
t2.join

Just like its Perl 6 counterpart (sigils aside), it outputs:

Thread 1 started
Thread 2 started
0
1
2
3
4
5
Thread 1 finished: a is >5 and reset to -1000
Thread 2 finished: a is now < -100