What have you found for these years?

2012-04-04

thread-safe is not fiber-safe; fiber-safe could be even harder

Other posts:
2148. 04-04 thread-safe is not fiber-safe; fiber-safe could be even harder
2149. 04-04 thread-safe is not fiber-safe; fiber-safe could be even harder (2)
2151. 04-05 thread-safe is not fiber-safe; fiber-safe could be even harder (3)

deterministic race conditions in fibers

i = [0]
a = Fiber.new{ t = i[0]; Fiber.yield; i[0] = t + 1 }
b = Fiber.new{ t = i[0]; Fiber.yield; i[0] = t + 1 }
p i # => [0]
a.resume
b.resume
a.resume # 1
b.resume # 1
p i # => [1] # should be [2]

thread-safe code is not fiber-safe, here we have deadlock

m = Mutex.new
i = [0]
a = Fiber.new{ m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 } }
b = Fiber.new{ m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 } }
p i # => [0]
a.resume
b.resume # ThreadError: deadlock; recursive locking

reentrant mutex doesn't really lock for fibers. it still has race conditions

require 'monitor'
m = Monitor.new
i = [0]
a = Fiber.new{ m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 } }
b = Fiber.new{ m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 } }
p i # => [0]
a.resume
b.resume
a.resume # 1
b.resume # 1
p i # => [1] # should be [2]

detect deadlock and retry with threads to resume fibers again, finally fiber-safe now

require 'fiber'

def try_resume f
  case r = f.resume
  when ThreadError
    Thread.new{ sleep(0.1); try_resume(f) }
  else
    r
  end if f.alive?
end

m = Mutex.new
i = [0]
a = Fiber.new{
      begin
        m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 }
      rescue ThreadError => e
        Fiber.yield e
        retry
      end
    }
b = Fiber.new{
      begin
        m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 }
      rescue ThreadError => e
        Fiber.yield e
        retry
      end
    }
p i # => [0]
while a.alive? || b.alive?
  try_resume(a)
  try_resume(b)
end
p i # => [2]

retry with eventmachine instead, but do we really want to do this?

require 'fiber'
require 'eventmachine'

def try_resume f
  case r = f.resume
  when ThreadError
    EM.add_timer(0.1){ try_resume(f) }
  else
    r
  end if f.alive?
end

EM.run{
  m = Mutex.new
  i = [0]
  a = Fiber.new{
        begin
          m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 }
        rescue ThreadError => e
          Fiber.yield e
          retry
        end
      }
  b = Fiber.new{
        begin
          m.synchronize{ t = i[0]; Fiber.yield; i[0] = t + 1 }
        rescue ThreadError => e
          Fiber.yield e
          retry
        end
      }
  p i # => [0]
  try_resume(a)
  try_resume(b)
  EM.add_periodic_timer(0.1){
    try_resume(a)
    try_resume(b)
    if !a.alive? && !b.alive?
      p i # => [2]
      EM.stop
    end
  }
}

can STM (software transactional memory) help here?

STM for Ruby WANTED!!

or let's just use threads, shall we?

m = Mutex.new
i = [0]
a = Thread.new{ m.synchronize{ t = i[0]; sleep(0.1); i[0] = t + 1 } }
b = Thread.new{ m.synchronize{ t = i[0]; sleep(0.1); i[0] = t + 1 } }
p i # => [0]
a.join
b.join
p i # => [2]
--
concurrency with shared state is really hard!

0 retries:

Post a Comment

All texts are licensed under CC Attribution 3.0