coder23
coder23

Reputation: 43

How to use boost channels (and fibers) properly in a class?

I am trying to use boost channels and fibers in a class. Here is a simple test case which works fine but it is not exactly what I want. If I move "line:1" to "loc:1" the programs hangs (gdb shows at a spinlock inside boost::fibers after c->push(a)). Can anyone help me by pointing what am I doing wrong? Thanks.

Here is the sample code which works and produces the following,

#include <iostream>
#include <boost/fiber/all.hpp>

using namespace std;

template <class T>
class Block
{
    private:
        typedef boost::fibers::buffered_channel<T> channel_t;
        typedef boost::fibers::fiber fiber_t;
        fiber_t _thread_send;
        fiber_t _thread_recv;
        size_t _n;
        channel_t* _chan;

    public:
        Block(size_t n) : _n(n), _chan(nullptr) {
            // >>>>>>>>>> loc:1 <<<<<<<<<<<
        }
        virtual ~Block() {}
        void _send(channel_t *c) {
            cout << __func__ << endl;
            int a = 1000;
            cout << "Sending: " << a << endl;
            c->push(a);
        }
        void _recv(channel_t *c) {
            cout << __func__ << endl;
            int a = 0;
            c->pop(a);
            cout << "Received: " << a << endl;
        }
        void do_work() {
            cout << "do_work\n";
            channel_t temp{_n}; _chan = &temp; // <<<<<<<<<<<< line:1
            _thread_send = boost::fibers::fiber(bind(&Block::_send, this, _chan));
            _thread_recv = boost::fibers::fiber(bind(&Block::_recv, this, _chan));
            _thread_send.join();
            _thread_recv.join();
        }
};

int main()
{
    Block<int> B(2);
    B.do_work(); 
    return 0;
}

Output:

do_work
_send
Sending: 1000
_recv
Received: 1000

Compiled using:

GNU/Linux 64 bit x86-64
g++ (GCC) 7.1.1 2017051
boost 1.64.0
g++ -c --std=c++14 -g -Wall -Wpedantic boost_channels.cpp -o boost_channels.o
g++ -lboost_context -lboost_fiber boost_channels.o -o boost_channels

Upvotes: 1

Views: 2059

Answers (3)

coder23
coder23

Reputation: 43

Ok, declaring channel_t as a member works fine. I guess it is pointing to garbage. Also I learned that boost sync primitives does not like being std::move(ed).

Thanks guys for helping.

Upvotes: 0

lakeweb
lakeweb

Reputation: 1939

When you construct the channel in the Block constructor and take a pointer to it, the pointer _chan is pointing at garbage when temp goes out of scope. You could just make temp a member of Block or leave it where it works so in can be forwarded.

Update: Brackets(braces) in C++ define scope

Block(size_t n) : _n(n), _chan(nullptr)
    //the scope of the constructor starts at this brace
{
    //temp gets instantiated
    channel_t temp{_n};
    //assign the pointer to the object
    _chan = &temp;

} //put a break point here

Then use a memory watch to look at _chan. As you move past the closing bracket you should see the memory turn to garbage as temp gets destroyed. If you trace in at that point you will see temp meet its distributor.

I would just leave the temp in do_work.

Upvotes: 1

xlrg
xlrg

Reputation: 2109

channel_t temp{_n}; _chan = &temp; // <<<<<<<<<<<< line:1

in Block() will not work because temp goes out of scope after leaving Block()'s body and _chan would point to garbage/ freed memory

two versions are possible:

1) keep channel temp a local variable of do_work():

template <class T>
class Block
{
private:
    typedef boost::fibers::buffered_channel<T> channel_t;
    typedef boost::fibers::fiber fiber_t;
    fiber_t _thread_send;
    fiber_t _thread_recv;
    size_t _n;

public:
    Block(size_t n) : _n(n) {
    }
    virtual ~Block() {}
    void _send(channel_t *c) {
        cout << __func__ << endl;
        int a = 1000;
        cout << "Sending: " << a << endl;
        c->push(a);
    }
    void _recv(channel_t *c) {
        cout << __func__ << endl;
        int a = 0;
        c->pop(a);
        cout << "Received: " << a << endl;
    }
    void do_work() {
        cout << "do_work\n";
        channel_t chan{_n};
        _thread_send = boost::fibers::fiber(bind(&Block::_send, this, & chan));
        _thread_recv = boost::fibers::fiber(bind(&Block::_recv, this, & chan));
        _thread_send.join();
        _thread_recv.join();
    }
};

2) keep channel temp a member variable of Block<>:

template <class T>
class Block
{
private:
    typedef boost::fibers::buffered_channel<T> channel_t;
    typedef boost::fibers::fiber fiber_t;
    fiber_t _thread_send;
    fiber_t _thread_recv;
    channel_t _chan;

public:
    Block(size_t n) : _chan(n) {
    }
    virtual ~Block() {}
    void _send(channel_t *c) {
        cout << __func__ << endl;
        int a = 1000;
        cout << "Sending: " << a << endl;
        c->push(a);
    }
    void _recv(channel_t *c) {
        cout << __func__ << endl;
        int a = 0;
        c->pop(a);
        cout << "Received: " << a << endl;
    }
    void do_work() {
        cout << "do_work\n";
        _thread_send = boost::fibers::fiber(bind(&Block::_send, this, & _chan));
        _thread_recv = boost::fibers::fiber(bind(&Block::_recv, this, & _chan));
        _thread_send.join();
        _thread_recv.join();
    }
};

both versions generate:

do_work
_send
Sending: 1000
_recv
Received: 1000

Upvotes: 1

Related Questions