Home > Enterprise >  Launching a boost fiber in thread 1 from thread 2
Launching a boost fiber in thread 1 from thread 2

Time:06-28

I have a main linux thread (th1) that runs a number of boost fibers that are scheduled using the boost priority scheduler.

Every so often, I would like to launch a fiber from another thread (th2) that will run in th1 and be scheduled along with the other th1 fibers. The code I use to launch fibers in th1 looks like:

void launchFiber()
{
boost::fibers::use_scheduling_algorithm< priority_scheduler >()
boost::fibers::fiber *fib = new boost::fibers::fiber(fb_fiberFunction);
priority_props & props( fib->properties< priority_props >() );
props.set_priority(FiberPriorityValue);
props.name = "Fiber Name";
fib->detach();
}

The launch code works fine when I call the launchFiber function from th1 but it does not work when I call it from th2--it looks like the fiber is not added to the th1 fiber queue. I have added a mutex to the th1 priority_scheduler routine to protect the fiber queue but this doesn't seem to help.

It seems to me that I don't really understand how the fiber system is working when there is more than one thread involved. I have tried to look at the library source code but it is not really clear to me.

My guess is that this would be simple if I understood it correctly. Could someone provide an example of how I might do this.

CodePudding user response:

Contrary to system threads, fibers are based on cooperative scheduling. This means that you should manually tell to the scheduler when another fiber can be scheduled. The scheduler can choose the best fiber to schedule during this user-defined scheduling point. Here, the scheduler will choose the one with the highest priority. If there are no fibers with higher priority, then the same fiber can resume its execution back. The documentation states:

Each fiber has its own stack. A fiber can save the current execution state, including all registers and CPU flags, the instruction pointer, and the stack pointer and later restore this state. The idea is to have multiple execution paths running on a single thread using cooperative scheduling (versus threads, which are preemptively scheduled). The running fiber decides explicitly when it should yield to allow another fiber to run (context switching).
Control is cooperatively passed between fibers launched on a given thread. At a given moment, on a given thread, at most one fiber is running. Spawning additional fibers on a given thread does not distribute your program across more hardware cores, though it can make more effective use of the core on which it's running.

this_fiber::yield() is meant to perform the actual yields operations on the current fiber.

Note that fibers are not safely compatible with thread-local storage if they are moved between threads (not the case by default) and using basic basic mutex/condition variables is not safe either, particularly if a yield can appear in a middle of a protected code (critical section) as it can cause deadlocks. It can also be sub-optimal because mutexes can cause the current thread to be pre-empted or passively waiting while another fiber could do computations. Boost provide alternative synchronisation mechanisms for fibers that are safer/more-efficient though one still need to care about that. This is why fibers cannot be used to execute any arbitrary code blindly.

For more information, you can give a look to the examples starting from the simplest one.

CodePudding user response:

I did spend some time looking into this problem. It turns out that executing the command: boost::fibers::use_scheduling_algorithm< priority_scheduler >() creates a new priority_scheduler object with its own fiber queue. And this scheduler is associated with a context that is specific to the thread it is running in. So, in my circumstance, when I created a new fiber it ended up in the queue specific to the calling thread (th2, which wasn't running fibers) instead of the thread that was running all my fibers, th1.

So, I abandoned my idea of creating a fiber to run in th1 by a call from th2. I now using a queue that queues fiber launch requests from external threads. The fiber thread (th1) will check this queue when it executes the scheduler pick_next() function and if requests exist, fibers are created and added to th1's scheduler queue. It works fine--though I have an intermediate queue which I would prefer not to have (for esthetic reasons only).

  • Related