Boost logo

Boost Users :

Subject: [Boost-users] Understanding fibers
From: Stephan Menzel (stephan.menzel_at_[hidden])
Date: 2018-12-13 14:19:26


Hello @ll,

I am in the process of transforming a sync server application to an async
approach. The framework in use (not asio) instantiates and calls an object
representing the call when a request comes in, allows me to do things, fill
a response object and then expects me to call a finish() method which will
send the response and start the next request. This is not what this
question is about but more like my circumstances. All code is pseudocode
please...

void rpc_work() {
   const value = do_stuff(request);
   response.fill_value(value)
   parent->finish();
}

Now some of the calls will imply calling methods, which block on some
operations, waiting for external resources to respond. Obviously in an
async server with only one thread, this will block everything. So I'm
looking for a clever way to structure this.

My approach so far (in the prototype) is to go boost::async() for those
operations. Like this:

void rpc_work() {
   // prepare work
   boost::async(launch::any, [request, response, parent]() {
         const value = do_blocking_stuff(request);
         response.fill_value(value)
         parent->finish();
   });
}

The way I understand boost::async or indeed std::async is, that normally it
will just spawn a thread for the operation. The best I can hope for is a
thread pool but this is not guaranteed either (on Linux x64 gcc 7.3).

So I started looking into fibers but I'm having a hard time understanding
them, which is why I post here. From what I gather, what I have here is a
prime use case as it matches many things the docs talk about. And yet they
also suggest that the fibers are very intrusive and everything underneath
do_blocking_stuff() would have to be aware of being in a fiber and yield()
when appropriate. Is this correct? What would this mean?

Most if not all of the blocking operations are using boost::thread::futures
in their blocking things. They would do things like...

value_t do_blocking_stuff(request) {
   boost::future<value_t> fv = retrieve_value_from_somewhere();
   fv.wait_for(timeout);
   return fv.get()
}

Now, considering I would use fibers in the server, would I have to use
'fiber futures' here to make this work? To make the blocking functions
automatically 'fiber aware' and yield() when they would block? This would
imply replacing the thread::futures?
And if so, how will I know when the value is there and they are ready to
continue? And how will I cause this continuation? Do I have to keep track
of them and somehow poll() them in a loop or something?

I am aware of the broad nature of my question but perhaps someone can chime
in and give some hints that allow me to understand them better. They look
like they would fit my use case perfectly but I am a bit reluctant of
dragging the concept all throughout the code until I realize it won't work.

Perhaps someone can eli5 this to me ;-)

Cheers,

Stephan



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net