0

I'm trying to run some code with flow graph, it compiled successfully, however when running it gives a segmentation fault in the tbb flow graph library files after invoking input node, I can't seem to be able to find the reason of it. My input node is like this:

class InputNode{
public:
    // constructor, copy constructor, destructor are implemented
    bool operator() (InputResult &v){
        //some logic here that defines wether to process or not
        if(shouldProcess){
            v = InputResult();
            // some logic to set values inside v
            return true;
        } else
            return false;
    }
};

This node is connected to node of typetbb::flow::multifunction_node<InputResult, std::tuple<InputResult>>, connection is done by tbb::flow::make_edge(src, firstNodeFilter);. InputResult is a pointer, I have checked and confirmed it was set to a valid value not to null.

Exception is being thrown in tbb::flow::interface11::internal::broadcast_cache by function try_put_task at task *new_task = (*i)->try_put_task(t); which is called from tbb::flow::interface11::input_node by function apply_body_bypass at task *last_task = my_successors.try_put_task(v);

----------edit------------

Sorry for unclarity about input_node implementation. I have this code implemented for it.

tbb::flow::input_node<InputResult> src(g, InputNode());

When I try to change the InputNode to match the InputNodeBody I get compiler error in _flow_graph_body_impl.h:

error: no match for call to `InputResult&`
    bool operator()((Output &output) __TBB_override {return body( output ); }

And says there's no known cast from tbb::flowcontrol& to InputResult&

-------------edit 2 ------------------------ The following includes more of my code, removed some template parameters for readability. This code is after upgrading oneTBB.

file 1:

class NavigationQueryExecuter {
public:    
    EdgesRepoClass &edges;
    NodeRepoClass &nodes;
    LinkageRepoClass &linkage;

    NavigationQueryExecuter(NodeRepoClass& nodeRepo, EdgesRepoClass& edgeRepo, LinkageRepoClass& linkageRepo): nodes(nodeRepo),edges(edgeRepo), linkage(linkageRepo){ };

    template<class TQuery>
    TQuery* Execute(){
        typedef typename TQuery::InputResult TInputResult;
        auto query = new TQuery();
        class InputNode{
        private:
            const unsigned int Count = 1024; //const count.

            NodeRepoClass &nodes;
            TQuery& query;
            TPage* lastPage;
            TPid pid;
            const TPid lpid;
            unsigned int idx;
        public:
            InputNode(TQuery&q, NodeRepoClass& nodeRepo, TPid firstPageId, TPid lastPageId): query(q), nodes(nodeRepo), lpid(lastPageId){
                idx=0;
                pid=firstPageId;
                lastPage= nodes.GetPage(pid);
            }

            InputNode(const InputNode &other): query(other.query) ,nodes(other.nodes), pid(other.pid), lpid(other.lpid), idx(other.idx){
                lastPage = nodes.GetPage(other.lastPage->Id);
            }

            ~InputNode(){
                if(lastPage){
                    nodes.ReleasePage(lastPage);
                    lastPage= nullptr;
                }
            }

            TInputResult operator() (tbb::flow_control &fc){ //this function is invoked once only before exception is thrown.
                //logic to skip unused objects removed for simplicity.
                auto node = new NodeClass(lastPage, idx);
                while(idx < Count){
                    if(node->InUse()){
                        auto res = query.ProcessInput(node);
                        delete node;
                        return res; //res is set correctly, breaks after returning without touching any other parts of my code.
                    }
                    node->Id = ++idx;
                }
                delete node;
                fc.stop();
                return nullptr;
            }
        };
        auto g = tbb::flow::graph();
        tbb::flow::input_node<TInputResult> src(g, InputNode(*query,nodes, 0, 40));
        query->BuildGraph(g, src);
        src.activate();
        g.wait_for_all();
        return query;
    }
};

file 2:

class QueryExample{
    EdgesRepoClass &edges;
    NodeRepoClass &nodes;
    LinkageRepoClass &linkage;
public:
    struct Result{
        int n1, n2, e1;
    };

    typedef Result* InputResult;
    typedef std::vector<InputResult> OutputResult;
    typedef tbb::flow::multifunction_node<InputResult, std::tuple<InputResult>> FilterNodeType;

    OutputResult result;

    FilterOnNode(NodeRepoClass& nodeRepo, EdgesRepoClass& edgeRepo, LinkageRepoClass& linkageRepo): nodes(nodeRepo),edges(edgeRepo), linkage(linkageRepo){
        result=OutputResult();
    }

    InputResult ProcessInput(typename NodeRepoClass::TEntry* node){
        //initialize, and process all nodes.
        Result* res = new Result();
        res->n1 = node->Id;
        return res;
    }

    void BuildGraph(tbb::flow::graph &g, tbb::flow::input_node<InputResult> &src) {

        auto firstNodeFilter = FilterNodeType(
                g,
                tbb::flow::unlimited,
                [&](const InputResult &input, typename FilterNodeType::output_ports_type &op) { 
                //processing logic can either output to connected nodes or drop unncessary nodes.
                //this function is never reached, code breaks before it.
                });
        // couple more multifunction_node are created.
        tbb::flow::make_edge(src, firstNodeFilter);
        tbb::flow::make_edge(tbb::flow::output_port<0>(firstNodeFilter), generateEdgesFilter);
        tbb::flow::make_edge(tbb::flow::output_port<0>(generateEdgesFilter), secondNodeFilter);
        tbb::flow::make_edge(tbb::flow::output_port<0>(secondNodeFilter), outputNodeFilter);

    }
};

The code breaks where indicated in file 1 in the comment.

T.Aoukar
  • 653
  • 5
  • 19

1 Answers1

0

The interface of the input node (former source node) with the passed message to be filled by reference has a flaw that results in potential using of the same memory location concurrently in the input node and its successors. Consider looking into this direction to find the root cause of the segmentation fault.

Also, consider using the new interface, which is based on the tbb::flow_control and returning of the produced message from the node body: input_node, its body requirements

Aleksei Fedotov
  • 395
  • 1
  • 9
  • I've updated the question. Apparently the new interface is not working in my case. Does that mean I'm using an older version? if so, how would I check/update ? – T.Aoukar Feb 02 '21 at 11:46
  • It seems TBB 2020 U2 is being used (you can check that by setting "TBB_VERSION" to "1" before running the executable. So, in your case, you can try using [TBB 2020 U3](https://github.com/oneapi-src/oneTBB/releases/tag/v2020.3) or updating to the revamped version of TBB, called [oneTBB](https://github.com/oneapi-src/oneTBB/releases/tag/v2021.1.1). I recommend following the latter variant though some of the usage patterns might need to be rewritten as well. – Aleksei Fedotov Feb 03 '21 at 20:04
  • Do I need to uninstall the previous TBB or something? I thought I'd have the latest since I installed from package manager – T.Aoukar Feb 03 '21 at 23:27
  • It depends on the package manager being used and whether it allows to manage several versions of the same product simultaneously. You can always download binaries manually and source them in the environment so that the build system uses paths to that downloaded product. – Aleksei Fedotov Feb 04 '21 at 06:59
  • After struggling to setup correct clean installation of latest version from oneapi TBB, I can compile the code successfully with tbb::flow_control parameter, but still runtime gives a segmentation fault at the same line. – T.Aoukar Feb 07 '21 at 07:09
  • Do the pointer values generated by each invocation of the input_node's body differ? – Aleksei Fedotov Feb 17 '21 at 07:47
  • 1.I assume so, pointers are created with new keyword. 2. There's no second invocation of input_node body, it breaks soon as it returns. – T.Aoukar Feb 17 '21 at 10:58
  • That looks strange. Could you please share the reproducer? You can do this right here by further editing the question or by opening an issue on the GitHub: https://github.com/oneapi-src/oneTBB – Aleksei Fedotov Feb 21 '21 at 17:43
  • I've updated the question with more detailed code. Sorry for the delay as I didn't notice your comment earlier. – T.Aoukar Mar 07 '21 at 04:19
  • Are the nodes and the graph object alive while it is working? I am not sure, but it might the reason of segfaults. The graph and the nodes attached should outlive the message processing happening in the graph. – Aleksei Fedotov Mar 14 '21 at 18:30
  • I think that is the problem, I might have assumed since the body of node gets copied then the actual nodes also gets copied. – T.Aoukar Mar 25 '21 at 22:34