2

I am trying to implement a Parent and Child class. The Parent class would start a thread and run a routine function. The Child class would override the routine function if needed.This seems perfectly possible but I run into trouble by following answers from this earlier post. As stated in one earlier comment, the thread always invoke the routine function in Parent class.

The following is my code:

// Base.h
#include <cstdint>
#include <cstdbool>
#include <thread>
#include <chrono>
#include <mutex>
#include <functional>
class Base
{
private:
protected:
    std::thread routineThread;
    uint16_t state = 0;
    bool isRoutineRunning = false;
    void routine();
public:
    Base();
    ~Base();
    void startRoutine();
    void stopRoutine();
    virtual void outputState();
};
// Base.cpp
#include "Base.h"
#include <stdio.h>
#include <time.h>

Base::Base()
: routineThread()
{
}

Base::~Base()
{
    isRoutineRunning = false;
    if (routineThread.joinable())
    {
        routineThread.join();
    }
}

void Base::outputState()
{
    printf("%ld: base class object state = %d\r\n", time(nullptr), state);
}

void Base::routine()
{
    while (true)
    {
        outputState();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void Base::startRoutine()
{
    printf("%ld: tried to start routine thread\r\n", time(nullptr));
    if (!isRoutineRunning)
    {
        routineThread = std::thread(&Base::routine, this);
        isRoutineRunning = true;
        printf("%ld: routine thread started\r\n", time(nullptr));
    }
}

void Base::stopRoutine()
{
    if (isRoutineRunning)
    {
        isRoutineRunning = false;
        if (routineThread.joinable())
        {
            routineThread.join();
        }
    }
}
// Derived.h

#include "Base.h"

class Derived : public Base
{
private:
protected:
public:
    Derived(/* args */);
    ~Derived();
    void init();
    void outputState() override;
};

// Derived.cpp
#include "Derived.h"
#include <stdio.h>
#include <time.h>

Derived::Derived()
{
}

Derived::~Derived()
{
}

void Derived::init()
{
    state = 1;
}

void Derived::outputState()
{
    printf("%ld: derived class ojbect state = %d\r\n", time(nullptr), state);
}

// main.cpp
#include "./classes/Base.h"
#include "./classes/Derived.h"

int main()
{
    Derived derived;
    derived.init();
    derived.outputState(); This line seems to work as expected if the following line is commented out
    derived.startRoutine();

    return 1;
}

The output from test function above is:

1761776592: derived class ojbect state = 1 // This is expected
1761776592: tried to start routine thread
1761776592: routine thread started
// The following output are from virtual function in Parent class while state variable is from Child class
1761776592: base class object state = 1
1761776593: base class object state = 1
1761776594: base class object state = 1
1761776595: base class object state = 1
1761776596: base class object state = 1

Update: the code above were compiled in Eclipse CDT on Ubuntu 24.04

Is there a way to make the thread invoke routine function in Child class?

8
  • Sorry I skipped header files. I will add them now. Also I compiled the code within Eclipse CDT on Ubuntu 24.04 Commented Oct 29 at 23:53
  • You could add derived.stopRoutine(); before exiting the main function and see what happens. See here for a minimal working example. Commented Oct 30 at 0:01
  • @abcdefg the test code was constructed in a hurry so I missed the join in main. But the code would keep running until I hit Ctrl + C to terminate it. Commented Oct 30 at 0:02
  • 1
    Maybe you could rename stopRoutine as join, which better tells the purpose there, i.e. waiting for the end of the thread (see) Commented Oct 30 at 0:07
  • @ZhiyongLi "the test code was constructed in a hurry" -- Bad idea. Stack Overflow questions are supposed to stand the test of time, so it's worth slowing down and not rushing. Commented Oct 30 at 0:33

2 Answers 2

5

Add derived.stopRoutine();, otherwise main() scope ends and derived is destroyed - the program exhibits undefined behavior.

The particular wrong behavior can be explained:

  • main() ends
  • derived is being destroying
    • Derived::~Derived() is called.
    • derived.vtbl is rebound to Base::vtbl
    • Base::~Base() is called
  • derived storage persists in memory, is lucky kept untouched and this points the storage with Base::vtbl.
  • the thread routine is calling Base::outputState().

Yet another issue: ~Base() is not virtual. Although this is not a problem in this code.

Sign up to request clarification or add additional context in comments.

1 Comment

Could you elaborate on this answer? I added derived.stopRoutine(); and all of a sudden the code starts to run as expected.
2

I think you have a logical error since there is no call to join on your thread.

The consequence is that you exit the main function with the destruction of derived, while the thread continues to use this deleted object with some undefined behaviour.

So you need to join your thread before exiting main, i.e. waiting for its termination. Actually, your Base::stopRoutine seems to do that but would be better renamed Base::join

In the end, your main function should look like

int main()
{
    Derived object;
    object.startRoutine();
    object.join();
    return 1;
}

Demo

1 Comment

Thanks for the answer. The unexpected behavior also explains why state is 1 while the default value in base class is 0. This is what confused me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.