Unable to find out where race condition occurs

I use Valgrind --tool = drd to test my application that uses Boost :: thread. Basically, an application fills a set of Book values โ€‹โ€‹with Kehai values โ€‹โ€‹based on inputs through a socket connection.

In a separate thread, the user can connect and receive books sent to them.

It's quite simple, so I decided to use boost :: mutex :: scoped_lock in the place that serializes the book, and the location that clears the book data should be sufficient to prevent any race conditions. Here is the code:

void Book::clear() { boost::mutex::scoped_lock lock(dataMutex); for(int i =NUM_KEHAI-1; i >= 0; --i) { bid[i].clear(); ask[i].clear(); } } int Book::copyChangedKehaiToString(char* dst) const { boost::mutex::scoped_lock lock(dataMutex); sprintf(dst, "%-4s%-13s",market.c_str(),meigara.c_str()); int loc = 17; for(int i = 0; i < Book::NUM_KEHAI; ++i) { if(ask[i].changed > 0) { sprintf(dst+loc,"A%i%-21s%-21s%-21s%-8s%-4s",i,ask[i].price.c_str(),ask[i].volume.c_str(),ask[i].number.c_str(),ask[i].postTime.c_str(),ask[i].status.c_str()); loc += 77; } } for(int i = 0; i < Book::NUM_KEHAI; ++i) { if(bid[i].changed > 0) { sprintf(dst+loc,"B%i%-21s%-21s%-21s%-8s%-4s",i,bid[i].price.c_str(),bid[i].volume.c_str(),bid[i].number.c_str(),bid[i].postTime.c_str(),bid[i].status.c_str()); loc += 77; } } return loc; } 

The clear () function and copyChangedKehaiToString () function are called in the data stream and the data stream, respectively. In addition, as a note, the class book:

  struct Book { private: Book(const Book&); Book& operator=(const Book&); public: static const int NUM_KEHAI=10; struct Kehai; friend struct Book::Kehai; struct Kehai { private: Kehai& operator=(const Kehai&); public: std::string price; std::string volume; std::string number; std::string postTime; std::string status; int changed; Kehai(); void copyFrom(const Kehai& other); Kehai(const Kehai& other); inline void clear() { price.assign(""); volume.assign(""); number.assign(""); postTime.assign(""); status.assign(""); changed = -1; } }; std::vector<Kehai> bid; std::vector<Kehai> ask; tm recTime; mutable boost::mutex dataMutex; Book(); void clear(); int copyChangedKehaiToString(char * dst) const; }; 

When using valgrind --tool = drd, I get race status errors, such as:

 ==26330== Conflicting store by thread 1 at 0x0658fbb0 size 4 ==26330== at 0x653AE68: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (in /usr/lib/libstdc++.so.6.0.8) ==26330== by 0x653AFC9: std::string::_M_replace_safe(unsigned int, unsigned int, char const*, unsigned int) (in /usr/lib/libstdc++.so.6.0.8) ==26330== by 0x653B064: std::string::assign(char const*, unsigned int) (in /usr/lib/libstdc++.so.6.0.8) ==26330== by 0x653B134: std::string::assign(char const*) (in /usr/lib/libstdc++.so.6.0.8) ==26330== by 0x8055D64: Book::Kehai::clear() (Book.h:50) ==26330== by 0x8094A29: Book::clear() (Book.cpp:78) ==26330== by 0x808537E: RealKernel::start() (RealKernel.cpp:86) ==26330== by 0x804D15A: main (main.cpp:164) ==26330== Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8 ==26330== Other segment start (thread 2) ==26330== at 0x400BB59: pthread_mutex_unlock (drd_pthread_intercepts.c:633) ==26330== by 0xC59565: pthread_mutex_unlock (in /lib/libc-2.5.so) ==26330== by 0x805477C: boost::mutex::unlock() (mutex.hpp:56) ==26330== by 0x80547C9: boost::unique_lock<boost::mutex>::~unique_lock() (locks.hpp:340) ==26330== by 0x80949BA: Book::copyChangedKehaiToString(char*) const (Book.cpp:134) ==26330== by 0x80937EE: BookSerializer::serializeBook(Book const&, std::string const&) (BookSerializer.cpp:41) ==26330== by 0x8092D05: BookSnapshotManager::getSnaphotDataList() (BookSnapshotManager.cpp:72) ==26330== by 0x8088179: SnapshotServer::getDataList() (SnapshotServer.cpp:246) ==26330== by 0x808870F: SnapshotServer::run() (SnapshotServer.cpp:183) ==26330== by 0x808BAF5: boost::_mfi::mf0<void, RealThread>::operator()(RealThread*) const (mem_fn_template.hpp:49) ==26330== by 0x808BB4D: void boost::_bi::list1<boost::_bi::value<RealThread*> >::operator()<boost::_mfi::mf0<void, RealThread>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, RealThread>&, boost::_bi::list0&, int) (bind.hpp:253) ==26330== by 0x808BB90: boost::_bi::bind_t<void, boost::_mfi::mf0<void, RealThread>, boost::_bi::list1<boost::_bi::value<RealThread*> > >::operator()() (bind_template.hpp:20) ==26330== Other segment end (thread 2) ==26330== at 0x400B62A: pthread_mutex_lock (drd_pthread_intercepts.c:580) ==26330== by 0xC59535: pthread_mutex_lock (in /lib/libc-2.5.so) ==26330== by 0x80546B8: boost::mutex::lock() (mutex.hpp:51) ==26330== by 0x805473B: boost::unique_lock<boost::mutex>::lock() (locks.hpp:349) ==26330== by 0x8054769: boost::unique_lock<boost::mutex>::unique_lock(boost::mutex&) (locks.hpp:227) ==26330== by 0x8094711: Book::copyChangedKehaiToString(char*) const (Book.cpp:113) ==26330== by 0x80937EE: BookSerializer::serializeBook(Book const&, std::string const&) (BookSerializer.cpp:41) ==26330== by 0x808870F: SnapshotServer::run() (SnapshotServer.cpp:183) ==26330== by 0x808BAF5: boost::_mfi::mf0<void, RealThread>::operator()(RealThread*) const (mem_fn_template.hpp:49) ==26330== by 0x808BB4D: void boost::_bi::list1<boost::_bi::value<RealThread*> >::operator()<boost::_mfi::mf0<void, RealThread>, boost::_bi::list0>(boost::_bi::type<void>, boost::_mfi::mf0<void, RealThread>&, boost::_bi::list0&, int) (bind.hpp:253) 

In life, I canโ€™t understand where the race condition is. As far as I can tell, kehai cleansing is done only after accepting the mutex, and the same is true when copying it to a string. Anyone have any ideas what could be causing this, or where should I look?

Thank you.

+7
c ++ multithreading boost valgrind
source share
4 answers

After your post, I took the time to learn about Valgrind and how its output should be read.

I see the following:

You call Book::clear , which alternately calls Book::Kehai::clear , where you assign a value to the string. Inside std::string::assign STL does what it stores some value at 0x0658fbb0.

Meanwhile, another thread turned to the same memory location, so this situation is considered a race condition.

Now let's look at the "context" of another thread. Valgrind does not show the exact location of the stack, however, it does show between which "segments" it occurred. According to Valgrind, a segment is a sequential memory access block, limited to synchronization operations.

We see that this block starts with pthread_mutex_unlock and ends with pthread_mutex_lock . Means - the same memory location was available when your mutex was not blocked, and this thread was somewhere outside of your two functions.

Now look at the conflicting memory information:

 Allocation context: BSS section of /usr/lib/libstdc++.so.6.0.8 

BSS means it is a global / static variable. And it is defined somewhere inside libstdc.

Output:

This race condition has nothing to do with your data structures. This is due to STL. One thread does something with std::string (assigns it to an empty string to be exact), while another thread probably also does something related to STL.

By the way, I remember a few years ago, I wrote a multi-threaded application, and there were problems with std::string . As I found out, the STL implementation (which was Dunkimware) actually implemented the string as a reference count, while the reference count was not thread safe.

Maybe this is what is happening to you? Perhaps you should set some flag / compiler option when creating a multi-threaded application?

+6
source share

This report can be safely ignored. This is due to the way std :: string is implemented in libstdC ++. This problem was resolved in the version of libstdC ++ included in gcc 4.4.4 or later. See also GCC bugzilla item # 40518 for details.

+2
source share

Nevermind I am an idiot and managed to forget that C ++ strings are mutable. I changed the code to use c-style strings and my problems with race conditions disappeared.

Anyway, who reads this post, does anyone know of a good immutable string library for C ++? I thought that the fort had one, but I could not find anything convincing about this.

Thanks.

0
source share

STL should be thread safe in the sense that you should not use them with a thread if you correctly block or just do multithreaded reads

Surpirse! Yes, he suggested, but let me tell you what really happened.

I had a multitasking application. There was a data structure with strings ( std::string ). He was blocked by the critical section.

Other objects in the end needed to take lines from there. They made a copy of these lines as follows:

 // Take a string std::string str; { Autolock l(g_CritSect); str = g_SomeStr; } 

The same strategy was used to configure these lines:

 // Put a string std::string str; { Autolock l(g_CritSect); g_SomeStr = str; } 

And guess what? Crashes!

But why? Because the line assignment operator does not really create a copy of the memory block containing the line. Instead, the same memory block is reused (link).

Well, this is not necessarily a bad thing. But the bad thing is that std :: string implemented string counting not in thread safe mode. He used regular arithmetic ++ and - instead of InterlockedIncrement , etc.

It so happens that the str object refers to the same line. And then he ends up casting it in his destructor (or when explicitly binding to another line). And this happens outside the locked area.

To prevent these lines from being used in a multi-threaded application. And itโ€™s almost impossible to implement the correct lock in order to work with this, because the actual reference data is silently transferred from object to object.

And that the STL implementation has been declared thread safe.

0
source share

All Articles