Fibers


Multi-threaded programming can be hard on many levels, and if you are not careful you can end up in some painful situations. I’ve seen programs that spawn over 60 threads on a quad-core and have each thread hit the same mutex, this isn’t performant!

After I saw Christian Gyrling’s talk on fibers I was keen to give it a go. Fibers have OS support on Windows but I couldn’t find anything on Linux of macOS. However using Boost’s Context you can achieve the same thing. Gyrling does a really good job of explaining them that I would recommend watching.

Side Note: C++20 might have coroutines which are basically the same thing!

The nice thing about fibers is that they are cheap compared with Threads, a fiber can be thought of as a logical thread, however the cool thing is when a fiber has been slept it can be awoken on a different thread, this really allows us to maximise useage of the CPU cores.

My fiber interface looks like this.


void
some_job(job_ctx *ctx, void *arg)
{
  /* do some calculation on the argument */
}


void
job(job_ctx *ctx, void *arg)
{
  int some_data[128];
  job_desc desc[128];

  for(int i = 0; i < 128; ++i)
  {
    desc.func = some_job;
    desc.arg = (void*)some_data[i];
    desc.keep_on_thread = ROA_FALSE;
  }

  unsigned batch = job_ctx_submit(ctx, desc, 128);

  /* this will sleep this fiber */
  /* and the thread will go do something else */
  job_ctx_wait(ctx, batch);

  /* after all fibers complete we can use the results */
}

The system works fairly simply. You have a thread dispatcher that is constantly checking to see if there are any pending fibers that need to be continued. If not it will check to see if there are any jobs that haven’t been started yet, if there are, it will get the job and a new fiber and begin executing it.

void
thread_dispatch(void *arg)
{
  /* setup stuff */

  for(;;)
  {
    /* if found a pending fiber */
    if(pending_fiber())
    {
      do_pending_fiber();
    }
    else if(pending_work())
    {
      do_new_fiber(
        get_available_fiber(),
        get_work());
    }
  }
}

My fiber system is loosly based on RichieSams version. It’s worth a look.

There are some dragons to be aware of. If you are going to lock a mutex on a fiber be damn sure you unlock on the same thread, bad things happen. Also you have to be super careful around 3rd party and OS interactions, a lot of code assumes it is going to be interacted with on the same thread. For instance OSX window’ing code really needs to all be done on the same thread, this is the reason I have the ‘keep_on_thread’ option in the job description.