Ruby 3 MRI and GIL

franzejr
franzejr
Sep 26, 2015 · 3 min read

Probably you have heard from someone that Ruby doesn't scale, they are probably referring to the thread-safety of MRI. At the beginning, Ruby hasn't support native threads, Ruby 1.8.7 MRI version supports green threads— lightweight threads that run in user space. So, if it supports green threads it could be good, but not exactly. Green threads still run on a single native thread, which means you are not really running things in parallel. At Ruby 1.9 + it starts supporting native threads.

When MRI version was released supporting native threads… It was awesome! Until everyone know this version implemented over what's known as a Global Interpreter Lock(GIL). Its purpose is to ensure that just one thread can run for one process. Basically, it keeps a “global lock”. We have some approaches to prevent race conditions, deadlocks and some concurrent problems. One coarse-grained approach for solving those problems is GIL.

It’s important to mention that on Ruby MRI, GIL only applies to Ruby Operations, it makes sense because it exists to protect the integrity of the Ruby Virtual Machine.

Why we need to use GIL?

  • It makes developer’s lives easier(it’s harder to corrupt data)
  • it avoids race conditions within C extensions (it improves compatibility with C libraries that aren't thread-safe)
  • Easy of implementation

In the last few years we are seeing lots of new languages that have concurrency abstractions built-in at the core of the language. It seems, now on Ruby 3 it should come up with some related features. As we can see above:

As I mentioned, the approaches vary related to the granularity. If you use a fine-grained locking, every separated structure has a lock. GIL is a coarse-grained locking where one lock protects everything.

There are pros and cons related to each of these approaches. Fine-grained locking is good for parallelism(for example, two threads can run at the same time when they don’t share any resource), however there is a much bigger administrative overhead. Corse grained approach there is no much overhead, but two threads cannot run at the same time(even they don’t share any resource).

Let’s see some approaches and see how it may be the Ruby 3.0 approach.

Actors

One implementation: https://celluloid.io/ here you can see a basic usage.

Ownership model

You don’t have to worry about how to share the memory. Just the own object can destroy itself. Some examples of Ownership model in Rust.

Stream / Pipeline

Stream model is pretty simple model, however it doesn’t cover 100% of concurrency(According to Matz).

According to the Ruby language Creator, he will remove GIL and it looks like Ruby 3 will have the Stream/Pipeline approach with the introduction of a new operator: |>. It’s not sure.

The streem language is a proof of concept from the Stream approach. You should take a look on this:

References:

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store