JRubyConf and Clojure STM

I recently attended JRubyConf in San Francisco. The conference definitely had a strong web presence, but it wasn’t without a few “web neutral” presentations. The JRuby State of the Union, The JRuby Testing Story, and some of the lightning talks were interesting and fun.

Phil Hagelberg gave a brief presentation on Clojure’s Software Transactional Memory and how it can be used within JRuby. He wrote a small library that exposes and wraps the necessary STM components in the Clojure Java implementation. I briefly experimented with library, and it appears to work quite well and could provide immediate value to multi-threaded applications.

STM in JRuby Example

JRuby Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class STMCar
  def initialize
    @vin = Clojure::Ref.new(nil)    
  end
  
  def vin
    return nil if @vin.nil?
    @vin.deref
  end
  
  def vin=(vin)
    @vin.commute { vin }
  rescue
    raise "VIN can only be set within a transaction"
  end
end

class UnsafeCar
  attr_accessor :vin
end

module ThreadSpawner
  def in_threads(number, &block)
    threads = []
    number.times do |x|
      threads << Thread.new do
        block.call(x)
      end
    end
    threads.each { |t| t.join }
  end
  
  extend self
end

Test Run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
stm_car = STMCar.new
unsafe_car = UnsafeCar.new

ThreadSpawner.in_threads(10) do |number|
  Clojure::Ref.dosync do
    stm_car.vin = number + 1
    sleep(rand(10) / 10.0)
    puts "#{stm_car.vin} -- #{number + 1}"
  end
end

puts

ThreadSpawner.in_threads(10) do |number|
  unsafe_car.vin = number + 1
  sleep(rand(10) / 10.0)
  puts "#{unsafe_car.vin} -- #{number + 1}"
end

Results

STM Car

Got VIN Assigned VIN
5 5
10 10
7 7
3 3
6 6
8 8
4 4
2 2
1 1
9 9

Unsafe Car

Got VIN Assigned VIN
8 8
10 10
10 1
10 5
10 7
10 6
10 9
10 2
10 3
10 4

Example Clojure w/ STM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(require 'clojure.contrib.math)
(refer 'clojure.contrib.math)

(defstruct car :vin)
(def stm_car (ref (struct car 3)))

(defn vin []
  (let [chars (map char (range 65 91))]
    (loop [shuffled "" x (count chars)]
      (if (zero? x)
        shuffled
        (recur 
          (str shuffled (nth chars (rand-int (count chars)))) (dec x))))))

(let [
      update #(dosync 
                (commute stm_car assoc :vin %1)
                (Thread/sleep  (* (rand) 500))
                (println (str (:vin @stm_car) " -- " %1)))
      ]
  
  (dorun 
    (apply pcalls 
      (repeat 10 #(update (vin)))))    
)

(println (:vin @stm_car))


Leave a Reply

  

  

  

Stay Connected