-1

Here is the header file defining the template: Based on a few answers, I made modifications to make template single thread safe. But the program still crashes, so I believe the focus here should not be if the template is thread safe. It's about what happened when C1 inherits from both CBase and Singleton. Apparently, the program crashes when C1 is trying to call the function of the member variable it's parent Class CBase. It seems to me, something is messed up in the memory when multiple inheritance and the single template are used together.

#ifndef _T_SINGLETON_HH_
#define _T_SINGLETON_HH_

template <class T>
class Singleton
{
public:
    static T*   getInstance()
    {   
        static T instance_;
        return &instance_;
    }   

    template <class A>
    static T*   getInstance(A a)
    {   
        static T instance_(a);
        return &instance_;
    }   

protected:
    Singleton() {}
    virtual ~Singleton() {}
private:
    Singleton(Singleton const&);
    Singleton& operator=(Singleton const&);
};
#endif  // _T_SINGLETON_HH_ //

And here is how this template was used:

#include <iostream>

#include <unistd.h>
#include <pthread.h>

#include "singleton.hh"

using namespace std;

class X;
class X1; 
class ABase;
class BBase;
class BB; 
class CBase;

class X { 
public:
    virtual int getX() = 0;
};

class X1 : public X { 
public:
    int getX() { return 1; }
};

class Timer
{
private:
   static Timer* t_; 
   BBase* b_; 

   Timer();
   static void* go_helper(void* context);
   void go();

public:
    virtual ~Timer() {}

    static Timer* getInstance();
    void subscribe(BBase* b); 
    void unsubscribe(BBase* b) { b_ = NULL; }
};

class CBase {
public:
    CBase(BB& bb) : bb_(bb) {}
    virtual void run() = 0;
protected:
    BB& bb_;
};

class C1 : public CBase,
           public Singleton<C1>
{
public:
    C1(BB& bb) : CBase(bb) {}
    void run();
};

class BBase {
public:
    BBase(ABase& a) : a_(a) { Timer::getInstance()->subscribe(this); } 
    virtual ~BBase() { Timer::getInstance()->unsubscribe(this); }
    virtual void run() = 0;
protected:
    ABase&  a_; 
};

class BB : public BBase {
public:
    BB(ABase& a) : BBase(a) {
        c = C1::getInstance(*this);
//      c = new C1(*this);    IF WE USE THIS INSTEAD, THEN IT WILL WORK
    }
    int getX();
    void run();
private:
    CBase*  c;
};

class ABase {
public:
    ABase() {
        x = new X1;
        b = new BB(*this);
    }
    void run();
    int getX() { return x->getX(); }
private:
    X* x;
    BBase* b;
};

Timer* Timer::t_ = NULL;

Timer::Timer() : b_(NULL)
{
    pthread_t th;
    pthread_create(&th, NULL, &Timer::go_helper, this);
}

Timer*
Timer::getInstance() {
    if (t_ == NULL)
        t_ = new Timer;
    return t_;
}

void
Timer::subscribe(BBase* b) { b_ = b; }

void*
Timer::go_helper(void* context) {
    Timer *t = reinterpret_cast<Timer*>(context);
    t->go();
    return NULL;
}

void
Timer::go()
{
    while(1) {
        sleep(1);
        if (b_) b_->run();
    }
}

void ABase::run() {
    cout << __PRETTY_FUNCTION__ << getX() << endl;
    cout << __PRETTY_FUNCTION__ << x->getX() << endl;
    b->run();
    while(1)
        sleep(1);
}

int BB::getX() {
    return a_.getX();
}
void BB::run() {
    cout << __PRETTY_FUNCTION__ << endl;
    c->run();
}

void C1::run() {
    cout << __PRETTY_FUNCTION__ << bb_.getX() << endl;
}

int main()
{
    ABase*  a = new ABase;
    a->run();
}

If you still think it is a thread safe issue, then here is the simplified one thread version using the template:

#include <iostream>

#include <unistd.h>
#include <pthread.h>

#include "singleton.hh"

using namespace std;

class X;
class X1; 
class ABase;
class BBase;
class BB; 
class CBase;

class X { 
public:
    virtual int getX() = 0;
};

class X1 : public X { 
public:
    int getX() { return 1; }
};

class CBase {
public:
    CBase(BB& bb) : bb_(bb) {}
    virtual void run() = 0;
protected:
    BB& bb_;
};

class C1 : public CBase,
           public Singleton<C1>
{
public:
    C1(BB& bb) : CBase(bb) {}
    void run();
};

class BBase {
public:
    BBase(ABase& a) : a_(a) { } 
    virtual ~BBase() { } 
    virtual void run() = 0;
protected:
    ABase&  a_; 
};

class BB : public BBase {
public:
    BB(ABase& a) : BBase(a) {
        c = C1::getInstance(*this);
//      c = new C1(*this);
    }   
    int getX();
    void run();
private:
    CBase*  c;  
};

class ABase {
public:
    ABase() {
        x = new X1; 
        b = new BB(*this);
    }   
    void run();
    int getX() { return x->getX(); }
private:
    X* x;
    BBase* b;
};

void ABase::run() {
    cout << __PRETTY_FUNCTION__ << getX() << endl;
    cout << __PRETTY_FUNCTION__ << x->getX() << endl;
    b->run();
    while(1)
        sleep(1);
}

int BB::getX() {
    return a_.getX();
}
void BB::run() {
    cout << __PRETTY_FUNCTION__ << endl;
    c->run();
}

void C1::run() {
    cout << __PRETTY_FUNCTION__ << bb_.getX() << endl;
}

When I run this program, it crashes:

void ABase::run()1
void ABase::run()1
virtual void BB::run()
zsh: segmentation fault (core dumped)  ./a.out

Here is the stack information given by GDB:

#0  0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89
89        int getX() { return x->getX(); }
[Current thread is 1 (Thread 0x7faa0f050740 (LWP 5351))]
(gdb) bt
#0  0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89
#1  0x0000000000400e0c in BB::getX (this=0x7ffed6e821f0) at tst_sigill.cc:138
#2  0x0000000000400e71 in C1::run (this=0xaeedd0) at tst_sigill.cc:146
#3  0x0000000000400e51 in BB::run (this=0xaeec60) at tst_sigill.cc:142
#4  0x0000000000400de3 in ABase::run (this=0xaeec20) at tst_sigill.cc:132
#5  0x0000000000400ed1 in main () at tst_sigill.cc:152

For some unknown reason, this pointer passed to BB::getX and ABase::getX was messed up. I can't see what's wrong with the code.

2 Answers2

2

Well, the major problem seems to be that your getInstance() implementation isn't thread safe.

I'd recommend forgetting about all that construct() / destruct() stuff also and simply leave that to the CRT0 code provided with the ABI.


You should rather use an implementation following the Scott Meyer's Singleton that is guaranteed to be thread safe (at least for the current standard):

template <class T>
class Singleton {
public:
    static T& getInstance() {   
        static T instance_;
        return instance_;
    }   

protected:    
    Singleton() {}
    virtual ~Singleton() {}

    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;
};

If there's need to change any of the Singletons properties later that could be accessed asynchronously, keep in mind to provide thread safe operations as well.

You could support that with your template singleton base like:

template <class T>
class Singleton {
   // ...
protected:    
    mutable std::mutex internalDataGuard;
};

class C1 : public CBase, public Singleton<C1> {
public:
     C1(BB& bb) : CBase(bb) {}
     void run();

     void setSomeData(T data) {
         std::unique_lock(Singleton<C1>::internalDataGuard);
         // change internal data member ...
     }

     const T& getSomeData() const {
         std::unique_lock(Singleton<C1>::internalDataGuard);
         // return internal data member ...
     }
};
Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • That'll blow OP up some time later, but OP's code won't even get that far. – user4581301 May 25 '16 at 18:34
  • 1
    @user4581301 I'd say that's the root problem though, and the OP have to change their design drastically. – πάντα ῥεῖ May 25 '16 at 18:35
  • Thank you for your answer, but this doesn't solve my problem. It still crashes every time. – user3776886 May 25 '16 at 19:06
  • @user3776886 You probably need some more improvements regarding thread safety. At least provide a [MCVE] in your question, that exactly reproduces your problem. – πάντα ῥεῖ May 25 '16 at 19:09
  • 1
    @user3776886 To simplify your example, you can strip out all of the threading. I'm putting my money on a dangling reference, but also not interested in debugging 200+ lines of code. – user4581301 May 25 '16 at 19:11
  • @user4581301 Your guts are gnarling probably right. So the _singleton template_ doesn't have to do anything with what's asked for alltogether. – πάντα ῥεῖ May 25 '16 at 19:14
  • @user3776886 `C1::getInstance(*this);` combined with `getInstance(A a)` going to copy `this` into a temporary `a`. It looks like `a` is going to be stored later... and then go out of scope. Bad times, man. – user4581301 May 25 '16 at 19:21
  • 1
    @user4581301 Yes, as mentioned that code needs to be changed drastically. – πάντα ῥεῖ May 25 '16 at 19:31
0

A quick note on using the stack trace to pick off some useful information. The program crashes where it crashes, but sometimes the trace can give you a few hints on where things started to go wrong:

#0  0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89

Died here. this=0x1, says that this is pointing to nonsense; 0x1 is almost certainly an invalid memory location. I can't think of a modern platform where 1 is valid memory. Doesn't mean there isn't one, but it's highly unlikely and thus a suspect.

#1  0x0000000000400e0c in BB::getX (this=0x7ffed6e821f0) at tst_sigill.cc:138

Here this is questionable. Skipping down over the next few lines, we see that all of the this pointers are in and around 0xaeeXXX. 0x7ffed6e821f0 is pretty far out there, so we should start by looking at references to BB

#2  0x0000000000400e71 in C1::run (this=0xaeedd0) at tst_sigill.cc:146

Specifically those inside C1 since it was the last object we were in with a sane address.

Maybe the rest of the stack trace matters, maybe it doesn't. Maybe we'll get back to it for more clues, but for the moment let's run down the current leads.

Looking in on C1, I see:

C1(BB& bb) : CBase(bb) {}

Takes a BB reference and passes it to CBase's constructor

CBase(BB& bb) : bb_(bb) {}

Stores the BB in bb_

So where does the BB come from? C1 is instantiated by the singleton. I don't see any calls to construct, so this leaves getInstance(A a), which does not take a reference. This means C1(BB& bb) is called with a temporary. The temporary is ultimately stored in CBase::bb_ which is a BB&, so bb_ is a reference to a variable that will no longer exist after returning from getInstance. Attempting to use bb_ after this point will invoke Undefined Behaviour.

I am not going to suggest a fix for this because this code is too convoluted for it's own good and, as πάντα ῥεῖ points out in his answer, isn't going to work without a significant redesign.

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Changing getInstance(A a) to getInstance(A &a) fixed my problem. I thought BB& will be taken as A in the template. – user3776886 May 25 '16 at 20:22
  • Solves the immediate problem but you are still wide open for the other nastiness πάντα ῥεῖ warns about and provides a solution, that would also have solved this, for. – user4581301 May 25 '16 at 20:49