c++multithreadingvisual-c++concurrencyconcurrent-vector

concurrent_vector invalid data


using : VC++ 2013

concurrency::concurrent_vector<datanode*> dtnodelst

Occasionally when I do dtnodelst->at(i) .... I am getting an invalid address (0XCDCD.. ofc) which shouldn't be the case cause after I do push back, I never delete or remove any of the itms ( even if I delete it should have returned the deleted old address... but I am not ever deleting so that is not even the case )

dtnodelst itm = new dtnodelst ();
....
dtnodelst->push_back(itm);

any ideas on what might be happening ?

p.s. I am using windows thread pool. some times .. I can do 8million inserts and find and everything goes fine .... but sometimes even 200 inserts and finds will fail. I am kind of lost. any help would be awesomely appreciated!!

thanks and best regards

actual code as an fyi

p.s. am I missing something or is it pain in the ass to past code with proper formatting ? I remember it being auto align before ... -_-

struct datanode {       
     volatile int nodeval;
     T val;
};
concurrency::concurrent_vector<datanode*> lst
inline T find(UINT32 key)
{
    for (int i = 0; i < lst->size(); i++)
    {
       datanode* nd = lst->at(i);
       //nd is invalid sometimes
       if (nd)  
       if (nd->nodeval == key)
       {
         return (nd->val);
       }
    }
    return NULL;
}
inline T insert_nonunique(UINT32 key, T val){
   datanode* itm = new datanode();
   itm->val = val;
   itm->nodeval = key;
   lst->push_back(itm);
   _updated(lst);                       
   return val;
}

Solution

  • The problem is using of concurrent_vector::size() which is not fully thread-safe as you can get reference to not yet constructed elements (where memory contains garbage). Microsoft PPL library (which provides it in concurrency:: namespace) uses Intel TBB implementation of concurrent_vector and TBB Reference says:

    size_type size() const | Returns: Number of elements in the vector. The result may include elements that are allocated but still under construction by concurrent calls to any of the growth methods.

    Please see my blog for more explanation and possible solutions.

    In TBB, the most reasonable solution is to use tbb::zero_allocator as underlying allocator of concurrent_vector in order to fill newly allocated memory with zeroes before size() will count it too.

    concurrent_vector<datanode*, tbb::zero_allocator<datanode*> > lst;
    

    Then, the condition if (nd) will filter out not-yet-ready elements.