0

I'm trying to implement monostate class which manages some std::thread. Thread is running until flag become equals to false. After flag changes to false - thread stops. But looks like I have to call stoping method explicitly. Calling it in destructor brings me runtime errors (Tested on GCC 4.8 for ARM, GCC 4.9 for x86_64 and MSVC 2017). Am I right that such behavior is due to

"Static members of a class are not associated with the objects of the class: they are independent objects with static storage duration or regular functions defined in namespace scope, only once in the program."

so destructor call is omited?

Code sample:

#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>


void runThread(const std::atomic<bool> &_isRunning) {

    while (_isRunning) {

        std::cout << "Me running.." << std::endl;

        std::this_thread::sleep_for(std::chrono::milliseconds(30));

    }

}

class test {

    static std::thread          thread;
    static std::atomic<bool>    isRunning;


public:

    test();
    ~test();

    static void go();
    static void stop();


};

std::thread         test::thread;
std::atomic<bool>   test::isRunning{ false };


test::test() {}

void test::go() {

    isRunning = true;
    thread = std::thread(runThread, std::ref(isRunning));

}

void test::stop() {

    isRunning = false;

    if (thread.joinable()) {

        thread.join();

    }

}

test::~test() {

    stop();

}


int main() {

    test::go();

    std::this_thread::sleep_for(std::chrono::seconds(5));

    std::cout << "Done here!!!!!!!!!!!!!!!!!";

    // Will not crash anymore if uncomment
    //test::stop();

    return 0;

}

Using std::async with std::feature gives same result but without error. Thread just keeps running.

P.S.


Making the class non-monostate solves runtime errors but leaves me with this question. Is managing resources a bad practice for monostate classes/static members?

Ihor Baklykov
  • 543
  • 7
  • 24
  • 2
    If you never instantiate a class, it's destructor will never be called. –  May 14 '18 at 21:33
  • What you're trying doesn't work, so what are you asking? – juanchopanza May 14 '18 at 21:34
  • @NeilButterworth it's a monostate - it doesn't need to be instantiated itself - all members are static. And thouse members are instantiated. Check source please – Ihor Baklykov May 14 '18 at 21:36
  • 3
    @IGR94 And if it never gets instantiated the destructor will never be called. – juanchopanza May 14 '18 at 21:37
  • @juanchopanza I'm asking why it doesn't actually work. Trying to dig as deep as possible – Ihor Baklykov May 14 '18 at 21:38
  • I know what a monostate is - one of its features is that its destructor will never be called, and calling the destructor seems to be what your code depends on. –  May 14 '18 at 21:41
  • @NeilButterworth I thought constructor is implicitly called on `test::go()`, so somewhere should be destructor – Ihor Baklykov May 14 '18 at 21:42
  • @IGR94 Well, I'm afraid you were misinformed. No constructor call and no destructor call. –  May 14 '18 at 21:44
  • Easy demo - add to test ctor a cout. Note the ctor of test does not report, and thus not called. Now invoke the ctor in main, just before "test::go()" ... ctor now does report, and dtor reports, and it works. – 2785528 May 14 '18 at 21:47
  • @NeilButterworth just checked it - you are right. Sorry, my bad. Looks like I have to reread standard. Well, now everythig clear. Add an answer please so I can mark it – Ihor Baklykov May 14 '18 at 21:47
  • @DOUGLASO.MOEN yeah, just got it. My bad – Ihor Baklykov May 14 '18 at 21:49

2 Answers2

1
 ~test();

should be called before destroying any "test" object. You do not create "test" objects in your code, so you are right,

Static members of a class are not associated with the objects of the class: they are independent objects with static storage duration or regular functions defined in namespace scope, only once in the program.

1

The constructor of a static object is called before main is executed, and the destructor is called after main is completed (from within atexit, typically).

Put a breakpoint in the destructor, it's easy to see.

Aganju
  • 6,295
  • 1
  • 12
  • 23