3

I know what the books say about std::deque being moderately thread safe but my experience is proving otherwise. I'm using VS 2010. There are at least two threads (could be N threads but adding threads only makes the problem happen sooner) each running the same code. Each thread contains the same code however a pointer to a unique instance of a struct that contains a deque is passed to each thread so in theory they each have their own deque to work with. However at varying times, I get errors when the threads try to access the deque (always a read). The deque is defined something like this:

struct A
{
   deque<TAS*> dqTas;
}

TAS is a pointer to another struct.

Struct A is created as

A* Aptr = new A;  

The TAS struct is also created the same way

TAS* pTas = new TAS

The errors are characterized by:

1) they happen at random times in the code. The threads may run for minutes processing data reading/writing to the deque before the error occurs.

2) More threads cause the problem to happen faster. The problem never happens with 1 thread.

3) Error msgs vary saying either the deque can't be dereferenced or the index is out of scope. If the deque can't dereference error occurs then the reason why from examining the data is completely undetectable. Everything looks in order, pointers, existing data in the deque, etc. If the problem is index out of range then somehow one or more data items (out of several hundred) suddenly get corrupted in the deque itself.

I've removed all deletes from every workflow pathway so that inadvertent deletion of memory can't be the issue.

The only thing it seems that could cause this is a global counter or pointer in the std::deque code. The character of these errors indicate a thread collision source. I've even verified that the addresses of each struct instance are different. In theory there should be zero chance of collisions since each thread has its own deque copy. The only way this happens with this setup is if the std::deque code has a global ptr or counter in it.

Has any one else had this experience? Would the boost deque functions perform better in such a scenario?

In case you'e wondering, this is the code that gpfs:

pTs->dqTas.push_front( pTb );    <<GPF happens after a write

#if defined (DEBUG)
    long d2 = pTs->dqTas.size()-1;
    if( d2 > 0 )
    {
        TASBAR* pDel2;
        //pDel2 = pTs->dqTas[d2];
        pDel2 = pTs->dqTas.at(d2);   //<<GPF happens here
    }
#endif

RESOLUTION:

Thank you all for your comments. The problem was resolved and it had nothing to do with the deque container. The random deque corruption was a symptom of the problem. The problem was caused by some old static variables declared locally in a function of a class that the thread instantiated. Those variables were being overwritten with addresses of other objects being saved in the same deque in another thread. I just got rid of those and everything started working as expected. As elementary as it may sound, the lesson here to remember is that static variables are essentially global variables (even if defined locally within a function), across threads. Probably best to avoid them altogether in any code that goes into threads that can run multiple instances of the same code, unless it's very clear why and how they're being used.

brimaa
  • 169
  • 3
  • 11
  • 9
    'moderately thread-safe'? Isn't that another way of saying 'not thread-safe'? Thread safety isn't really something you can do halfway... – Jeremy Friesner Apr 10 '14 at 00:03
  • I see nothing protecting that structure or that deque from concurrency issues. If you have a writer (and you do) you *must* provide concurrency protection in one form or another on both the write **and** read sides. How you choose to do that is entirely up to you, but since the posted uncompilable code has not-even-a-hint of threading or attempts at concurrency protection, you're asking us to help you solve a problem where we *literally* see *none*. – WhozCraig Apr 10 '14 at 00:09
  • 1
    The author is claiming that each thread has its own separate instance. – i_am_jorf Apr 10 '14 at 00:10
  • Can you show us how you create the `deque`s for each thread? – i_am_jorf Apr 10 '14 at 00:11
  • @jeffamaphone I don't buy that either unless I see it. It is conceivable even if it is the case that an iterator or index from one container is being used against another (which would obviously be bad). but at least `.at()` is in there to catch the out-of-bounds, rather than the unprotected `[]`, so we got that to help. – WhozCraig Apr 10 '14 at 00:13
  • Are you saying there is *no* chance any thread has any pointer or reference of any kind to either your struct or the deque within (including iterators) that is *ever* access by different threads? – WhozCraig Apr 10 '14 at 00:16
  • Thanks for all your responses. Each thread gets it's own copy of the deque created through "new" precisely so it would be thread safe. There should be no collisions. I'm trying it with C++11 and see if the problem goes away. WhozCraig, let me think about that. See above for how deque is created. – brimaa Apr 10 '14 at 00:20
  • If possible, try and strip this down to bare-bones, because if it is as you say, this simply should not be happening. I have.. concern... over the dynamic nature of the elements, since you're making a copy of the deque you're making copies of all those pointers and not the objects they point to, but it sounds like you may already know this and it is by design (and of course, those objets need to be thread-safe as well). – WhozCraig Apr 10 '14 at 00:23
  • I would like to see those books. – StackedCrooked Apr 10 '14 at 00:25
  • http://stackoverflow.com/questions/4105930/is-using-stddeque-or-stdpriority-queue-thread-safe – brimaa Apr 10 '14 at 00:34
  • I don't get this either. If each thread gets its own deque instance, and only that thread reads/writes to that instance, (ie. the deque is not being used for inter-thread comms), I can't see that 'thread-safety' would even be a consideration. The object instances on the deque may be an issue but that is not a deque problem:) – Martin James Apr 10 '14 at 07:11

1 Answers1

3

Generally no container is "thread-safe". It's just a container. I would recommend you to make it thread safe yourself. Creating an std::lock_guard object with an std::mutex on the stack will make your code thread-safe. Hope it helps: Below a code example:

std::mutex lockMutex;
std::lock_guard<std::mutex> lock(lockMutex);
santahopar
  • 2,933
  • 2
  • 29
  • 50
  • Thanks I'll try this. When you say put it on the stack, do you mean surround every read/write to the deque with this code or in the struct where the deque is defined? I'd be a bit concerned about a performance hit. But I'll have to see. – brimaa Apr 10 '14 at 00:30
  • Create on the stack meaning do not "new" it, that way you will know that the lock will be destroyed after your function will exit. Just use the code the way it is and you should be fine – santahopar Apr 10 '14 at 05:12