wiki:FiberLoop

Using Ruby 1.9 Fibers to drive your apps (uWSGI >=0.9.7-dev)

In the uWSGI Rack plugin a LoopEngine for ruby 1.9 fibers is available.

Fibers are a form of coroutine, useful for implementing cooperative multitasking in your apps.

Quickstart

First of all we have to compile the uWSGI server core

python uwsgiconfig --build core

Then we will build the rack plugin

python uwsgiconfig --plugin plugins/rack core

(do not remove the latest 'core' arg, otherwise the world will be destroyed)

If your ruby 1.9 installation is not on the system path you can force a specific ruby interpreter with:

UWSGICONFIG_RUBYPATH=/opt/ruby/bin/ruby python uwsgiconfig.py --plugin plugins/rack core

Finally compile the fiber plugin

python uwsgiconfig --plugin plugins/fiber core

In debian sid you can use something like this:

python uwsgiconfig.py --build core
UWSGICONFIG_RUBYPATH="ruby1.9.1" python uwsgiconfig.py --plugin plugins/rack core
UWSGICONFIG_RUBYPATH="ruby1.9.1" python uwsgiconfig.py --plugin plugins/fiber core

Now we write a rack app (call it fibers.ru) that will print the first 10 numbers but suspends its execution after every iteration to allow other requests to be accepted

require 'fiber'
class SuspendingBody

        def each
                for i in 1..10
                        yield "number: #{i}\n"
                        Fiber.yield
                end
        end

end

class RackFoo

        def call(env)
                [200, { 'Content-Type' => 'text/plain'}, SuspendingBody.new]
        end

end

run RackFoo.new

Now run uWSGI with 20 async cores (or use a higher value).

This time we will a use a yaml configuration file (call it fibers.yml)

uwsgi:
  plugins:              rack, fiber
  socket:               127.0.0.1:3031
  rack:                 fibers.ru
  post-buffering:       4096
  async:                20
  loop:                 fiber

Now run uWSGI

./uwsgi fibers.yml

Sleeping and non-blocking-io

You can use the uWSGI api to combien fibers with non-blocking io and sleeping

For sleep without blocking you use UWSGI.async_sleep(secs) function

require 'fiber'
class SuspendingBody

        def each
                for i in 1..10
                        yield "number: #{i}\n"
                        UWSGI.async_sleep(3)
                        Fiber.yield
                end
        end

end

class RackFoo

        def call(env)
                [200, { 'Content-Type' => 'text/plain'}, SuspendingBody.new]
        end

end

run RackFoo.new

For non-blocking-io you can use the async_connect, wait_fd_read, wait_fd_write

The following example will async sleep 3 times then will connect to a http server and returns the output. (it is a bit complex, but should cover 99% of the uses)

require 'fiber'
class SuspendingBody

        def each
                for i in 1..3
                        yield "number: #{i}\n"
                        UWSGI.async_sleep(1)
                        puts "sleep..."
                        Fiber.yield
                end

                fd = UWSGI.async_connect("81.174.68.52:80")
                UWSGI.wait_fd_write(fd, 3)
                Fiber.yield
                io = IO.new(fd)
                puts "connected"
                io.syswrite("GET /uwsgi/export/1081%3A362d695b2f25/plugins/fiber/fiber.c HTTP/1.0\r\n")
                io.syswrite("Host: projects.unbit.it\r\n")
                io.syswrite("\r\n")

                UWSGI.wait_fd_read(fd, 3)
                Fiber.yield

                puts "data available"

                begin
                while body = io.sysread(fd)
                        yield body
                        Fiber.yield
                end
                rescue
                end

                io.close
        end

end

class RackFoo

        def call(env)
                [200, { 'Content-Type' => 'text/plain'}, SuspendingBody.new]
        end

end

run RackFoo.new

Framework support

Sinatra has been tested with fibers. You can stream response in this way (simply return an object that respond to "each" method):

require 'fiber'
require 'sinatra'

get '/hi' do

   class Response
        def each
                for i in 1..10
                        yield "ciao<br/>"
                        Fiber.yield
                end
        end
   end

   Response.new
end

run Sinatra::Application

REMEMBER !!!

Coroutines are for increasing "concurrency" of your app, they do not allow you to execute code faster !!!