Skip to content
Advertisement

How to extract taskid(tid) of a pthread from the parent thread?

I’m using std::thread to launch threads. Also, I need stats for the worker thread available at /proc/[pid]/tasks/[tid]. I need tid to be able to monitor thread stats. I was wondering if there was a way to extract tid from the parent thread. I know that syscall gettid() from the worker returns its id, but I want the threadId from the master and not the slave. Is there a way to extract tid from the thread_id gor from std::thread.get_tid() ?

I believe there might be better ways of doing this, please suggest 🙂

UPDATE:
How can you get the Linux thread Id of a std::thread() this provides some information on getting tid from the worker, adds an overhead to the thread launch. For instance, std::thread t = std::thread(&wrapper); t.get_id() can be called from the launcher thread. I was/am looking if there was a to do the same thing from the main/launcher thread in a safe way.

Advertisement

Answer

All threads have a unique id:
std::thread::id this_id = std::this_thread::get_id();

You can store it in a variable when the program starts and it’ll be accessible from the other threads.

I understand what you mean when you say parent thread, but even though one thread gave birth to another, they are siblings.

if you want the master thread to be able to get the /proc path to each worker thread, you could wrap the worker thread object in a class that, when it starts the actual thread, creates a path property that the master can later get.

An example:

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

// A base class for thread object wrappers
class abstract_thread {
public:
    abstract_thread() {}

    abstract_thread(const abstract_thread&) = delete;
    abstract_thread(abstract_thread&& rhs) :
        m_th(std::move(rhs.m_th)), m_terminated(rhs.m_terminated), m_cv{}, m_mtx{} {}
    abstract_thread& operator=(const abstract_thread&) = delete;
    abstract_thread& operator=(abstract_thread&& rhs) {
        terminate();
        join();
        m_th = std::move(rhs.m_th);
        m_terminated = rhs.m_terminated;
        return *this;
    }

    virtual ~abstract_thread() {
        // make sure we don't destroy a running thread object
        terminate();
        join();
    }

    virtual void start() {
        if(joinable())
            throw std::runtime_error("thread already running");
        else {
            std::unique_lock<std::mutex> lock(m_mtx);
            m_terminated = true;
            // start thread and wait for it to signal that setup has been done
            m_th = std::thread(&abstract_thread::proxy, this);
            m_cv.wait(lock, [this] { return m_terminated == false; });
        }
    }
    inline bool joinable() const { return m_th.joinable(); }
    inline void join() {
        if(joinable()) {
            m_th.join();
        }
    }
    inline void terminate() { m_terminated = true; }
    inline bool terminated() const { return m_terminated; }

protected:
    // override if thread specific setup needs to be done before start() returns
    virtual void setup_in_thread() {}
    // must be overridden in derived classes
    virtual void execute() = 0;

private:
    std::thread m_th{};
    bool m_terminated{};
    std::condition_variable m_cv{};
    std::mutex m_mtx{};

    void proxy() {
        {
            std::unique_lock<std::mutex> lock(m_mtx);
            setup_in_thread(); // call setup function
            m_terminated = false;
            m_cv.notify_one();
        }
        execute(); // run thread code
    }
};

// an abstract thread wrapper capable of returning its /proc path
class proc_path_thread : public abstract_thread {
public:
    // function to call from master to get the path
    const std::string& get_proc_path() const { return m_proc_path; }

protected:
    void setup_in_thread() override {
        m_proc_path =
            std::move(std::string("/proc/")) + std::to_string(syscall(SYS_gettid));
    }

private:
    std::string m_proc_path{};
};

// two different thread wrapper classes. Just inherit proc_path_thread and implement
// "execute()". Loop until terminated() is true (or you're done with the work)
class AutoStartThread : public proc_path_thread {
public:
    AutoStartThread() { start(); }

private:
    void execute() override {
        while(!terminated()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << std::this_thread::get_id() << " AutoStartThread runningn";
        }
    }
};

class ManualStartThread : public proc_path_thread {
    void execute() override {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::cout << std::this_thread::get_id() << " ManualStartThread runningn";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
};

int main() {
    AutoStartThread a;
    std::cout << a.get_proc_path() << "t// AutoStartThread, will have pathn";

    ManualStartThread b;
    std::cout << b.get_proc_path()
              << "t// ManualStartThread not started, no pathn";
    b.start();
    std::cout << b.get_proc_path()
              << "t// ManualStartThread will now have a pathn";
    b.join();

    std::this_thread::sleep_for(std::chrono::milliseconds(1500));
    // terminate() + join() is called automatically when abstract_thread descendants
    // goes out of scope:
    //
    // a.terminate();
    // a.join();
}

Possible output:

/proc/38207 // AutoStartThread, will have path
    // ManualStartThread not started, no path
/proc/38208 // ManualStartThread will now have a path
139642064209664 ManualStartThread running
139642072602368 AutoStartThread running
139642072602368 AutoStartThread running
139642072602368 AutoStartThread running
139642072602368 AutoStartThread running
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement