I wrapped my mind around this, yet can not find the error. Could anyone help me where I did bad programming.
One boost::thread
receives strings
over a socket, splits them to vector<string>
and sorts them into the right variable inside shared class
. Other threads read from there. I tried to make it thread safe via mutex as shown here.
I appreciate any help, even small hints :)
This is how the program terminates:
Looping...<enter to exit>
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted (core dumped)
And this is the corresponding file. It is entangled with ROS, but that part shouldn't be the verdict.
class shared
{
public:
shared() : count(0) {/*emp. body*/ } //constructor
void setvec(vector<string> &strVec, int type){
boost::upgrade_lock<boost::shared_mutex> lock(mtx);
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
switch(type) {
case 1: typ1acc = strVec; setsensor[0] = true; break;
case 2: typ2mag = strVec; setsensor[1] = true; break;
case 3: typ3 = strVec; setsensor[2] = true; break;
}/**/
}
vector<string> getvec(int type) {
boost::shared_lock<boost::shared_mutex> lock(mtx);
switch(type) {
case 1: tmp = typ1acc; break;
case 2: tmp = typ2mag; break;
case 3: tmp = typ3; break;
}
return tmp;
}
private:
boost::shared_mutex mtx;
vector<string> tmp;
vector<string> typ1acc;
vector<string> typ2mag;
vector<string> typ3;
};
shared c; //class object
Class is called from multiple boost::threads:
//socket function which sorts vectors into shared class
//this happens from one boost::thread
int type; //key sort by sensor type
vector<string> strVec;
c.setvec(strVec,type);
//multiple boost::threads call this to read the vectors
//this happens from multiple boost::thread
strVec = c.getvec(type);
I think the issue with having tmp
outside the function is that the mutex destructor will (or can) run before the copy operation from tmp
to the permanent variable resulting in a small window where tmp
can be overwritten and cause a potential data race.
You can see this if you create simple fake mutex/string classes that show when each of them is being run. The code at the end outputs the following for me (VC++ 2015):
CSimpleString Raw Constructor (g_tmp)
CSimpleString Raw Constructor (result)
CFakeMutex Constructor
CSimpleString Copy Raw Operator (TestFunction)
CSimpleString Copy Constructor (TestFunction)
CFakeMutex Destructor
CSimpleString Copy Operator (TestFunction)
CSimpleString Destructor (TestFunction)
Result = TestFunction
with the important lines indented showing that your mutex is destroyed/freed before the important copy takes place. If you put tmp
inside the function the order of operations doesn't appear to change but since tmp
is a local variable there is no potential data race that can occur.
Very basic code for testing this is below.
#include <string.h>
#include <vector>
class CFakeMutex
{
public:
CFakeMutex()
{
printf("CFakeMutex Constructor\n");
}
~CFakeMutex()
{
printf("CFakeMutex Destructor\n");
}
};
class CSimpleString
{
public:
CSimpleString() {
printf("CSimpleString Empty Constructor\n");
}
CSimpleString(const char* pString) : m_String(pString) {
printf("CSimpleString Raw Constructor (%s)\n", pString);
}
CSimpleString(const CSimpleString& String) : m_String(String.m_String) {
printf("CSimpleString Copy Constructor (%s)\n", String.m_String.c_str());
}
~CSimpleString()
{
printf("CSimpleString Destructor (%s)\n", m_String.c_str());
}
CSimpleString& operator=(const CSimpleString& Src)
{
if (&Src == this) return *this;
printf("CSimpleString Copy Operator (%s)\n", Src.m_String.c_str());
m_String = Src.m_String;
return *this;
}
CSimpleString& operator=(const char* pString)
{
printf("CSimpleString Copy Raw Operator (%s)\n", pString);
m_String = pString;
return *this;
}
std::string m_String;
};
CSimpleString g_tmp("g_tmp");
CSimpleString TestFunction()
{
CFakeMutex Mutex;
CSimpleString local_tmp("local_tmp");
//local_tmp = "TestFunction";
//return local_tmp;
g_tmp = "TestFunction";
return g_tmp;
}
int main()
{
CSimpleString result("result");
result = TestFunction();
printf("Result = %s\n", result.m_String.c_str());
return 0;
}