7eRoM
7eRoM

Reputation: 453

std::jthread runs a member function from another member function

Here is my code:

#include <iostream>
#include <zconf.h>
#include <thread>

class JT {
public:
    std::jthread j1;

    JT() {
        j1 = std::jthread(&JT::init, this, std::stop_token());
    }

    void init(std::stop_token st={}) {

        while (!st.stop_requested()) {
            std::cout << "Hello" << std::endl;
            sleep(1);
        }
        std::cout << "Bye" << std::endl;
    }
};

void init_2(std::stop_token st = {}) {
    while (!st.stop_requested()) {
        std::cout << "Hello 2" << std::endl;
        sleep(1);
    }
    std::cout << "Bye 2" << std::endl;
}

int main() {
    std::cout << "Start" << std::endl;
    JT *jt = new JT();
    std::jthread j2(init_2);
    sleep(5);
    std::cout << "Finish" << std::endl;
}

Here is the output:

Start
Hello
Hello 2
Hello
Hello 2
Hello
Hello 2
Hello
Hello 2
Hello
Hello 2
Finish
Bye 2
Hello

The problem is I could get Bye 2 message but not Bye message.

I know the passed stop_token variable results in this problem but I do not know how to pass it to a member function inside another member function.

Upvotes: 4

Views: 3313

Answers (3)

Liviu Stancu
Liviu Stancu

Reputation: 45

The std::stop_token must be received as parameter by the JT::init function, during the thread construction. You can use either std::bind

j1 = std::jthread{ std::bind(&JT::init, this, std::placeholders::_1) };

or, more simpler, std::bind_front as in @Hasturkun answer.

Note Obtaining the std::stop_token after the thread has been constructed as bellow, results in a race condition, because the line j1 = std::jthread(&JT::init, this); involves a constructor and a move operator, the second one concurring with the line auto st = j1.get_stop_token();, as demonstrated bellow:

#include <thread>
#include <iostream>

using namespace std::chrono_literals;
class JT {
public:
    std::jthread j1;

    JT() {
       j1 = std::jthread(&JT::init, this);
    }

    ~JT() {
        j1.request_stop();
        j1.join();
    }

    void init() {

        auto st = j1.get_stop_token();
        while (!st.stop_requested()) {
            std::this_thread::sleep_for(1ms);
            std::cout << "Hello" << std::endl;
        }
       std::cout << "Bye" << std::endl;
    }
};

int main() {
    std::cout << "Start" << std::endl;
    for (int i = 0; i < 1000; i++) {
        JT jt;
        std::this_thread::sleep_for(5ms);
    }
}

Which results in:

Start
Hello
Bye
Hello
Bye
Hello
Hello
Hello
Hello
Hello
Hello
....

and program never ending. I've tested on release with gcc 12.1.0 and msvc (VS 2019 16.11.5).

This can be fixed by using the constructor initializer list, which will not call the move operator:

JT() : j1(std::jthread(&JT::init, this)) {
}

Upvotes: 2

Hasturkun
Hasturkun

Reputation: 36412

If I'm understanding the problem correctly (my understanding being that for std::jthread(&JT::init, this) jthread wants to call JT::init(std::stop_token st, this), which isn't going to work), you probably want to use std::bind_front to give it a Callable that works. e.g.

    JT() {
    j1 = std::jthread(std::bind_front(&JT::init, this));
}

Upvotes: 15

7eRoM
7eRoM

Reputation: 453

According to the useful comments, I have rewritten the class code as below:

class JT {
public:
    std::jthread j1;

    JT() {
        j1 = std::jthread(&JT::init, this);
    }

    void init() {
        auto st = j1.get_stop_token();
        while (!st.stop_requested()) {
            std::cout << "Hello" << std::endl;
            sleep(1);
        }
        std::cout << "Bye" << std::endl;
    }
};

You must get the stop_token on the fly through auto st = j1.get_stop_token();.

And the revised main function:

int main() {
    std::cout << "Start" << std::endl;
    JT *jt = new JT();
//    auto jt = std::make_unique<JT>();
    std::jthread j2(init_2);
    sleep(5);
    std::cout << "Finish" << std::endl;
    delete jt;
}

You need to delete the class object directly or use RAII (like smart pointers).

Upvotes: 4

Related Questions