Search code examples
c++boost-threadbad-alloc

Programm terminating "std::bad_alloc" after some time


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);

Solution

  • 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;
    }