MacRuby Drops GIL, Gains Concurrent Threads

| by Werner Schuster on Jun 30, 2009. Estimated reading time: 2 minutes |

Threading in Ruby has always been a troublesome issue. Ruby 1.8 implements userspace threads with performance problems and also don't make use of multiple cores.
Ruby 1.9 switched to mapping every Ruby thread to a kernel thread, which removes some of the inefficiencies in 1.8's threads - yet still only one Ruby thread can run at a time.

The reason is the Global Interpreter Lock (GIL), sometimes referred to as Global VM Lock (GVL). Every Ruby thread needs to acquire the GIL before it can run. Ruby shares this implementation detail with languages like Python (where it's been a divisive issue for nearly a decade).

In the past few years, alternative Ruby implementations have done away with the issue: JRuby and IronRuby both don't have a GIL.

Now MacRuby joins their ranks and works without a GIL, as Laurent Sansonetti explains:

All MacRuby threads are scheduled by the OS kernel and registered into the Objective-C garbage collector (which runs on its own threads) before doing anything.

While the MacRuby runtime still has shared state across all threads, it now synchronizes the access to these structures instead of only allowing one thread to be active. The details:

The Core object contains a lock that is used every time a shared data structure is accessed. Shared data structures are for example: LLVM caches, various stubs caches, BridgeSupport caches, etc.

Everything else was moved to a VM class, which is completely lock-free. A VM object is created at demand and only once for every thread that wants to access the runtime. The VM object contains data structures for the very specific thread execution, like for instance current blocks, bindings, exceptions, etc. The VM sometimes calls the Core (when defining a method for example) which acquires the Core lock, but most of the time it just runs concurrently.

The new threading system is available in MacRuby's experimental branch, which is destined to become the next MacRuby version. The branch also contains a sample (and simple) web server built with MacRuby.

Once the next stable MacRuby version is released with the new threading code, there'll be three Ruby implementations with concurrent threads, without any GIL, vs. Ruby 1.8 (userspace threads) and Ruby 1.9.x. Both JRuby and MacRuby support the Ruby 1.9.x language and libraries.

Ruby 1.9.x shares the GIL problem with Python, albeit the Unladen Swallow project promises to remove the GIL by 2010 (whether that happens and whether the provided patches actually get merged into the official Python is another matter - patches that remove the GIL have been floating around for a decade).

A final note: discussions about GILs in either Ruby or Python often bring up the argument that threads in these languages simply aren't the proper way to get concurrency. Another argument is that the GIL is only a problem with CPU bound code - I/O bound code is not a problem since the GIL is properly released, allowing other threads to run during I/O operations. With that in mind - how do you implement CPU bound code that can make use of multiple cores in Ruby? Do you take care of creating and managing multiple OS processes?


Rate this Article


Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Ruby Memory Model by Peter Veentjer

So when can we expect a Ruby Memory Model? Without a Memory Model it still isn't possible to write correct behaving or performing code.

Memory overhead of multi processing by Martin Probst

It might well be that multi-threading is not the correct way to get concurrency in Ruby or Python, but if you need multi-processing you might be in trouble as well. Even a tiny Rails application instance will easily consume over 50 MB of RAM, and larger applications will require significantly more. Now if you have 100 really concurrent users, you will spend 100x50 MB = 5 GB of RAM just on the application instances (not caches!). And of course this will grow significantly if your application happens to grow.

linux support by Roger Pack

more speed sweet!
linux support?

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

3 Discuss