Posted on December 1st, 2009 by
Justin DeWind
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)) |
Stay Connected

Leave a Reply