Fiber is danger or not on Crystal?

In Ruby, nothing is thread save because of the GIL. Having thread save mutexes doesn’t help anything. At least common data types like Array need to be thread save as well.

All of the ruby programs I’ve worked with used Mutexes around shared data structures to avoid race conditions. Modifying an Array inside a Mutex is safe correct?

Yes, but then you need to guard every operation on a shared data structure with a mutex. Even something simple as arrray[0]. Having to implement that in user code would be silly and error prone. That’s why you use thread-safe data structures for this.

1 Like

With the GIL

  • Race conditions possibly logically corrupt data which may or may not raise an exception.

Without the GIL:

  • Race conditions tend to corrupt more and crash the program faster.

Either way ruby programs need either concurrent data structures such as Queue or Mutexes. Existing threaded programs already use them.

I’m curious about the number of existing threaded ruby programs and libraries that would safely run without the GIL and without modification because of their use of Queue, Mutex and testing on jruby.

Many popular ruby programs and libraries have already done the work of wrapping shared data in Mutexes or using concurrent safe data structures for jruby compatibility. If they aren’t safe how do they work on jruby or other fully threaded ruby vm’s without a GIL?

Mutex and Fiber is completely different thing. On current Ruby, Fiber is (semi-)coroutine and user can (must) switch fibers by programmer. In other words, programmer must schedule fibers completely. Programmer must not switch fibers during atomic operations, like above example (or programmers need to introduce Mutex-like synchronization mechanism by themselves).

Thread-safe do you want to say?

(1) On MRI (ruby command), it has GIL and operations written in C functions are thread-safe in many cases (*1). In other words, programs written in Ruby is not thread-safe.

a = [1, 2] # assumption: a[1] should be doubled value of a[0]
Thread.new{
  p(a[0] = 10) # debug output
  a[1] = 20
}
Thread.new{
  p a # observe an array.
      # Programmer expects "a[1] should be doubled value of a[0]", but ...
}
a[0] = 100
a[1] = 200

This program is not thread-safe and can violate the assumption “a[1] should be doubled value of a[0]”.

(*1) If a method written in C calls Ruby methods, it can switch threads, so that it is not thread-safe.

(2) JRuby and other implementation don’t have GIL and basic operations can not be thread-safe (I don’t know details).

puma web-application server supports thread-execution and I heard recent Ruby on Rails framework support multi-thread execution by many efforts. I think many code are already thread-safe.

I respect these efforts, but I doubt there are more thread-safety bugs :stuck_out_tongue: because it is too tough to expose all this kind of bugs.

@ko1

In 2010 I used rails with puma and rainbows on a large scale web site. Google reported around 400-600m page views per month but my memory is a little foggy since I was mostly concerned with request latency. Several times that number of ajax queries were handled by unicorn/puma or later rainbows in thread pool mode. The distinction between using unicorn or rainbows came down to CPU use. The load balancer sent CPU bound request such as rails page rendering to unicorn clusters. Puma or later rainbows handled workloads with little rendering such as ajax queries that spit out json from the db’s.

Every page was thread safe and callable from either cluster and we often used the load balancers to switch requests between them based on various metrics.

We also used a custom queueing system (pre sidekiq) that ran each job in it’s own thread. Lots of ruby libraries were used. All of them were thread safe.

Cross testing was done on jruby and rubinius with real threads but for our workload most requests were faster on MRI.

Lots of other people working on jruby, rubinius and even MRI had to add thread safety to their code a long time ago. Threads on MRI still break applications without locks or queues even with the GIL.

To me this issue was solved ~10 years ago with ruby libraries. MRI only needs to remove the GIL to keep pace with other ruby interpreters.

@ko1 Why not copy what jruby did with the runtime guarantees and allow existing thread safe ruby programs to run without the GIL?

More information here.

@ko1 the blog post is finally out Blog: Parallelism in Crystal

1 Like
person = ["Alice", 10] # name and age

# I/O simulation method
def search_age_in_internet(name)
  pp "Searching for.. #{name}"
  case name
  when "Bob"
    20
  when "Carol"
    30
  else
    0
  end
end


spawn do
  person[0] = "Bob"
  person[1] = search_age_in_internet("Bob") #=> 20
end	

spawn do
  person[0] = "Carol"
  person[1] = search_age_in_internet("Carol") #=> 30
end

Fiber.yield
p person

This outputs

[“Carol”, 30]

I removed the Fiber.yield in search_age_in_internet.

Is this a solution?