Simon Iong
Simon Iong

Reputation: 191

ruby class instance variable not thread safe?

I just wondering if class instance variable is thread safe or not. Recently, I stuck around on a db lock problem which is caused by a custom ORM's model. ( No active-record, or sequel model anyway, just a simple version of ORM which is created by myself ).

To keep it simple, I just wrapped a simple version here:

I use a simple class instance variable to save a db instance ( could be a Sequel::Database object ).

class TestORM
  class << self
    attr_accessor :db 
    def self.db=( db )
      @db = db
    end 
  end 
end

and I create a test script for it to verify if this db is thread safe.

require 'test_orm.rb'
t1 = Thread.new do  
  db = 'db string 1'
  p "Thread 1: before assign, thread = #{Thread.current.object_id}, TestORM.db.object_id = #{TestORM.db.object_id}"
  TestORM.db = db
  p "Thread 1: after assign, thread = #{Thread.current.object_id}, TestORM.db.object_id = #{TestORM.db.object_id}"
  sleep( 0.5 )
  p "Thread 1: TestORM.db.object_id =  #{TestORM.db.object_id}"
end 
t2 = Thread.new do  
  db = 'db string 2'      
  p "Thread 2: before assign, thread = #{Thread.current.object_id}, TestORM.db.object_id = #{TestORM.db.object_id}"
  TestORM.db = db
  p "Thread 2: after assign, thread = #{Thread.current.object_id}, TestORM.db.object_id = #{TestORM.db.object_id}"
  sleep( 0.5 )
  p "Thread 2: TestORM.db.object_id =  #{TestORM.db.object_id}"
end

t1.join
t2.join

run the code above produce following result:

"Thread 1: before assign, thread = 70332471940100, TestORM.db.object_id = 4"
"Thread 2: before assign, thread = 70332471939980, TestORM.db.object_id = 4"
"Thread 1: after assign, thread = 70332471940100, TestORM.db.object_id = 70332471939840"
"Thread 2: after assign, thread = 70332471939980, TestORM.db.object_id = 70332471939520"
"Thread 1: TestORM.db.object_id =  70332471939520"
"Thread 2: TestORM.db.object_id =  70332471939520"

From the result above, It seems class instance variable is not thread safe, since Thread 2 override Thread 1's TestORM.db instance.

Any hints or any solution on this kind of wrapping?

ruby version: ruby 1.9.2p320

Upvotes: 0

Views: 1660

Answers (1)

Sony Santos
Sony Santos

Reputation: 5545

TestORM is a constant shared among threads. That is, you are changing the same object.

If you were using instances, you could use different instances, one per thread. Since you created that ORM, I suggest you to use instances for different DBs; so your code would look like this:

require 'test_orm.rb'
t1 = Thread.new do  
  db = 'db string 1'
  orm = TestORM.new   
  p "Thread 1: before assign, thread = #{Thread.current.object_id}, orm.db.object_id = #{orm.db.object_id}"
  orm.db = db
  p "Thread 1: after assign, thread = #{Thread.current.object_id}, orm.db.object_id = #{orm.db.object_id}"
  sleep( 0.5 )
  p "Thread 1: orm.db.object_id =  #{orm.db.object_id}"
end 
t2 = Thread.new do  
  db = 'db string 2'
  orm = TestORM.new   
  p "Thread 2: before assign, thread = #{Thread.current.object_id}, orm.db.object_id = #{orm.db.object_id}"
  orm.db = db
  p "Thread 2: after assign, thread = #{Thread.current.object_id}, orm.db.object_id = #{orm.db.object_id}"
  sleep( 0.5 )
  p "Thread 2: orm.db.object_id =  #{orm.db.object_id}"
end

You may want to define your DB in initialize:

orm = TestORM.new(db)

Upvotes: 2

Related Questions