Ruby Fibers may confuse

Ruby Fibers may confuse

Like many others, we use Ruby EventMachine to make most use of CPU time in the cloud. Instead of blocking and waiting for responses from remote systems, we just memorize what is to be done and return to the main loop, taking care other things that are currently going on. The asynchronous programming model is callback-based and makes otherwise pretty Ruby code look a bit unwieldy:

server.get(key) do |content|
  content.modify!
  server.set(key, content) do
    proceed_doing_stuff
  end
end

Call with Current Continuation (callcc) and the advent of Fibers in Ruby 1.9 to the rescue. They can help us make the code look pretty again:

content = server.get(key)
content.modify!
server.set(key, content)
proceed_doing_stuff

This works because server.get and server.set can obtain the remaining code as a reference (like the callback block before): the Current Continuation. The semantics are only subtly different, we can regard these simply as two ways to express code.

This article is not a praise on the pretty syntax and features of Ruby. Remember what we gain with the asynchronous programming model: not only raw performance by avoiding threads and locking, but also the convenience of always running in a single thread and not having the current state made inconsistent by getting preempted by other code.

Now what are Fibers introducing? We still run only one high-performing Ruby thread, but we gain cooperative multi-tasking. The situations a coder may find him/herself in are quite controllable through these “well-definable” preemption points. Still, to illustrate take a look at this stupid but trivial example:

received, sent = get_stats
content = server.get(key)
received += content.size
content.modify!
server.set(key, content)
sent += content.size
set_stats received, sent
proceed_doing_stuff

This is the code from above, extended with very simple statistics collection. The statistics are global state, but we’re safe because we don’t use threads. Are we?


No. All the issues come back to life that are difficult with writing threaded code. During server.get and server.set the Fiber has been yielded and resumed, and another Fiber may have modified the stats in the meanwhile. This could have been caused by the very same code, just handling another server from your connection pool.

By using callback blocks instead of Fibers the problem is still there but the code looks explicitly asynchronous. They tell the reader: “I am running somewhat later, please pay attention to any shared state!”

Liked this post? Read the archive or

Previously, on the Superfeedr blog: A Social Network?.