0

I have two class that follow this manner to encapsulate threads inside a C++ object.

class MyThreadClass
{
public:
   MyThreadClass() {/* empty */}
   virtual ~MyThreadClass() {/* empty */}

   /** Returns true if the thread was successfully started, false if there was an error starting the thread */
   bool StartInternalThread()
   {
      return (pthread_create(&_thread, NULL, InternalThreadEntryFunc, this) == 0);
   }

   /** Will not return until the internal thread has exited. */
   void WaitForInternalThreadToExit()
   {
      (void) pthread_join(_thread, NULL);
   }

protected:
   /** Implement this method in your subclass with the code you want your thread to run. */
   virtual void InternalThreadEntry() = 0;

private:
   static void * InternalThreadEntryFunc(void * This) {((MyThreadClass *)This)->InternalThreadEntry(); return NULL;}

   pthread_t _thread;
};

Now these i need the instances of these two class to do something together, generally, a instance of class B polling a flag and do its own job until the flag is set by class A:

class A : public MyThreadClass{
protected:
    virtual void InternalThreadEntry(){
        doSomething();
        flag.store(true);
        doOtherThing();
    }
private:
    std::atomic_bool& flag;
};
class B : public MyThreadClass{
protected:
    virtual void InternalThreadEntry(){
        do{
            doOwnJob();
            // thus cannot use condition variable
        }while(flag.load() == false);
        doOther();
    }
private:
    std::atomic_bool& flag;
};

Since the lifecycle of this two classes instances are bounded, i create a manager class to handle some shared variables instead of using global variable.

class Manager{
private:
    A a;
    B b;
    std::atomic_bool flag; // to shared between
};

Of course, these codes cannot be compiled, since the reference field must be be included in constructor's initialized list.

One of the ways to work around is to change class A/B's constructor signature to accept a reference to flag, since the flag lifecycle is bounded to these two class instances. However, this is not a valid choice since i am working on an existed code base, such change might involve massive code modification.

So it seems that i can only use a "setter" two set this flag field within each instance. And that makes the reference member declaration impossible. Intuitively, i think about using shared_ptr/raw_ptr to shared one flag between two class instances. But i wonder if this is "safe" manner or not? Or what is the better solution to deal with such situation?

user8510613
  • 1,242
  • 9
  • 27
  • The referenced answer is mostly outdated, why not use std::thread? – prehistoricpenguin Sep 17 '21 at 02:33
  • I am working on an existed code base, so.. – user8510613 Sep 17 '21 at 02:36
  • 1
    If i understand your problem correctly i would say u can go for shared ptr. – Sunandan Nandi Sep 17 '21 at 02:41
  • I believe it would even be appropriate to use a raw pointer in this case. If, as you said, you are managing the lifetime of the objects together and you can guarantee that the a and b threads stop before you destroy Manager, there's no reason to allocate the atomic_bool on the heap. – Ipiano Sep 17 '21 at 03:17
  • shared_ptr reference counting is threadsafe (I use it a lot to share data between threads because of that). I won't recommend raw pointers for threads, assignment isn't atomic and its hard to figure out which thread will be the "last" owner of the data pointed to (race conditions, memory leaks are bound to happen). – Pepijn Kramer Sep 17 '21 at 04:02
  • @PKramer I don't know that he needs to worry about a memory leak in the case of raw pointers. My suggestion was to keep the `Manager` type he has there, and then call `a.set_flag(&flag)` and `b.set_flag(&flag)`, giving the threads pointers to the member variable (which Manager owns, along with a and b). Then a and b can do `flag->load()` and `flag->store()`, but neither of them takes ownership of the flag and neither is responsible for deleting it. – Ipiano Sep 17 '21 at 04:05
  • @Ipiano you're right. I generalized too quickly instead of giving a focused answer – Pepijn Kramer Sep 17 '21 at 04:09
  • 1
    It's safe to pass a reference to `flag` to the constructors of `a` and `b` as long as `flag` is declared first in your `Manager` class. (Member-variables are always created in the order they are declared, and destroyed in reverse-order) – Jeremy Friesner Sep 17 '21 at 04:15

0 Answers0