Good day,
Well, I know there are already some discussions about comparison on Boost.Fiber and Goroutines. But as a new developer in the concurrency space, I am quite confused on the difference and usability.
So currently, I am doing some Go to C++ migration, and some of my roadblocks are the use of Channels and Goroutines, so I did some minor reading on concurrency, Boost.Fiber, and Goroutines and made a sample implementation based on what I understand about the topic.
package main
import (
"fmt"
"time"
)
const (
timeout = 0
interrupt = 1
serverDone = 2
)
func runDurationTimer(d time.Duration, toStop chan int) {
go func() {
dSeconds := uint64(d.Seconds())
if dSeconds == 0 {
return
}
time.Sleep(d)
toStop <- timeout
}()
}
func clientWatchControlChannel(d time.Duration, toStop chan int) {
go func() {
time.Sleep(d)
toStop <- serverDone
}()
}
func main() {
toStop := make(chan int, 1)
d := 5 * time.Second
runDurationTimer(d, toStop)
clientWatchControlChannel(1 * time.Second, toStop)
fmt.Println("Hello World \n")
reason := <-toStop
switch reason {
case timeout:
fmt.Println("Process done, duration: " + d.String() +".")
case interrupt:
fmt.Println("Process done, received interrupt signal.")
case serverDone:
fmt.Println("Process done, server terminated the session.")
}
}
So the code above is a basic success and timeout scheme, where when a function fails to return a status on within a certain duration it times out.
For clientWatchControlChannel
in real scenario, the Sleep
would be some other process that needs to return something and finish the procedure. Basinf on the hard coded values, the whole program returns "Process done, server terminated the session."
, because the duration of running clientWatchControlChannel
is < the timeout value in runDurationTimer
.
So when I debugged the code, it runs the 2 procedures, so I placed a breakpoint at toStop <- timeout
and toStop <- serverDone
, so runDurationTimer
runs then sleeps then while waiting to finish it runs clientWatchControlChannel
then finishes first since it has lesser duration than runDurationTimer
, after this, it doesn't go back to runDurationTimer
and wait, it just terminates and assign the value for output.
In my understanding in C++, I have coded the following.
#include <boost/fiber/all.hpp>
#include <chrono>
#include <iostream>
typedef boost::fibers::unbuffered_channel<int> unbuffered_channel_t;
enum stop { timeout = 0, interrupt, serverDone };
void runDurationTimer(
const std::chrono::duration<std::uint32_t, std::ratio<1, 1>> &d,
unbuffered_channel_t &toStop) {
boost::fibers::async([&]() {
if (d.count() == 0) {
return;
}
boost::this_fiber::sleep_for(d);
toStop.push(timeout);
});
}
void clientWatchChannel(
const std::chrono::duration<std::uint32_t, std::ratio<1, 1>> &d,
unbuffered_channel_t &toStop) {
boost::fibers::async([&]() {
boost::this_fiber::sleep_for(d);
toStop.push(serverDone);
});
}
int main() {
unbuffered_channel_t chan;
auto dTime = std::chrono::duration<std::uint32_t, std::ratio<1, 1>>(3);
auto dWatch = std::chrono::duration<std::uint32_t, std::ratio<1, 1>>(1);
runDurationTimer(dTime, chan);
clientWatchChannel(dWatch, chan);
std::cout << "Hello, World!\n";
int i = 0;
chan.pop(i);
switch (i) {
case timeout:
std::cout << "Process done, duration: " << std::to_string(dTime.count())
<< ".";
break;
case interrupt:
std::cout << "Process done, received interrupt signal.";
break;
case serverDone:
std::cout << "Process done, server terminated the session.";
break;
default:
break;
}
return 0;
}
So to cut the long story short, everything was nearly the same in behaviour. Again, I debugged the code, placed breakpoints in toStop.push(serverDone);
and toStop.push(timeout);
, so the same with Go, it ran runDurationTimer
first then while waiting for it to finish clientWatchControlChannel
kicks in and ran toStop.push(serverDone);
, but the difference is instead of terminating, it went back to runDurationTimer
and ran toStop.push(timeout);
. So I think this is the thing that I am really confused. Then chan.pop(i)
returned serverDone
.
So, I really need some help on my understanding if its correct or not, or should I continue using Boost.Fiber for this Go migration or use Boost.Coroutines2. Any suggestions, reactions, and corrections are appreciated, I am willing to learn from this.
My apologies on my C++ as well, been along time stagnating. :)
Thanks.
you have to close the channel
#include <boost/fiber/all.hpp>
#include <chrono>
#include <iostream>
typedef boost::fibers::unbuffered_channel<int> unbuffered_channel_t;
enum stop { timeout = 0, interrupt, serverDone };
void runDurationTimer(
const std::chrono::duration<std::uint32_t, std::ratio<1, 1>> &d,
unbuffered_channel_t &toStop) {
boost::fibers::async([&]() {
if (d.count() == 0) {
return;
}
boost::this_fiber::sleep_for(d);
// return value would indicate that the channel was closed
// `timeout` will not be inserted into the channel
toStop.push(timeout);
});
}
void clientWatchChannel(
const std::chrono::duration<std::uint32_t, std::ratio<1, 1>> &d,
unbuffered_channel_t &toStop) {
boost::fibers::async([&]() {
boost::this_fiber::sleep_for(d);
toStop.push(serverDone);
toStop.close();
});
}
int main() {
unbuffered_channel_t chan;
auto dTime = std::chrono::duration<std::uint32_t, std::ratio<1, 1>>(3);
auto dWatch = std::chrono::duration<std::uint32_t, std::ratio<1, 1>>(1);
runDurationTimer(dTime, chan);
clientWatchChannel(dWatch, chan);
std::cout << "Hello, World!\n";
int i = 0;
chan.pop(i);
switch (i) {
case timeout:
std::cout << "Process done, duration: " << std::to_string(dTime.count())
<< ".";
break;
case interrupt:
std::cout << "Process done, received interrupt signal.";
break;
case serverDone:
std::cout << "Process done, server terminated the session.";
break;
default:
break;
}
std::cout << "\n";
return 0;
}
Hello, World!
Process done, server terminated the session.