-1

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.

  • There is no relationship for threads unlike processes like successor-predecessor. – Soner from The Ottoman Empire Jun 13 '19 at 20:33
  • 2
    Possible duplicate: [How can you get the Linux thread Id of a std::thread()](https://stackoverflow.com/questions/15708983/how-can-you-get-the-linux-thread-id-of-a-stdthread) – gudok Jun 13 '19 at 20:37
  • 1
    Possible duplicate of [How can you get the Linux thread Id of a std::thread()](https://stackoverflow.com/questions/15708983/how-can-you-get-the-linux-thread-id-of-a-stdthread) – L. F. Jun 14 '19 at 05:09

2 Answers2

0

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 running\n";
        }
    }
};

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 running\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
};

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

    ManualStartThread b;
    std::cout << b.get_proc_path()
              << "\t// ManualStartThread not started, no path\n";
    b.start();
    std::cout << b.get_proc_path()
              << "\t// ManualStartThread will now have a path\n";
    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
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • This is actually the other way round of what I wanted, I don't like using gettid() from the worker(I feel it bloats perf, as one would need to gaurd or keep checking a global variable for tid to be populated). For instance with `std::thread t = std::thread(&wrapper);` ; `t.get_id()` from the context of the main would give the actual thread id. what do you think? – Shashank Hegde Jun 13 '19 at 21:58
  • Ok, I see. Sure, you could do it the other way around so that the master queries for the worker's paths. Just add a `condition_variable` for each thread that signals master that the native `pid_t` has been put in the `map`. Perhaps best make a class around the thread object with some synchronization so you can do `th.get_path()` in a safe way. – Ted Lyngmo Jun 13 '19 at 22:12
  • @ShashankHegde Made a new version. Hope I got you right this time :) – Ted Lyngmo Jun 13 '19 at 23:24
0

You can launch the thread through a function whose first task will be to message it's id, e.g., either classically using mutexes and condvars:

#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>

struct tid_msg{
    pthread_mutex_t mx;
    pthread_cond_t cond;
    pid_t tid;
};
void *thr(void*A)
{
    struct tid_msg *msg = A;
    pid_t tid = syscall(SYS_gettid);
    pthread_mutex_lock(&msg->mx);
    msg->tid = tid;
    pthread_mutex_unlock(&msg->mx);
    pthread_cond_signal(&msg->cond);
    printf("my tid=%lu\n", (long unsigned)tid);
    return 0;
}
int main()
{
    struct tid_msg msg = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, -1 };
    pthread_t ptid;
    pthread_create(&ptid,0,thr,&msg);

    pthread_mutex_lock(&msg.mx);
    while(-1==msg.tid) pthread_cond_wait(&msg.cond,&msg.mx);
    pthread_mutex_unlock(&msg.mx);

    printf("their tid=%lu\n", (long unsigned)msg.tid);
    pthread_join(ptid,0);
}

or simply via an atomic variable (relaxed memory ordering should be fine here, but you can play it safe and use the sequentially consistent default):

#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdatomic.h>

void *thr(void*A)
{
    _Atomic pid_t *tidp = A;
    pid_t tid;
    tid = syscall(SYS_gettid);
    atomic_store_explicit(tidp, tid, memory_order_relaxed);
    printf("my tid=%lu\n", (long unsigned)tid);
    return 0;
}
int main()
{
    _Atomic pid_t tid=-1;
    pthread_t ptid;
    pthread_create(&ptid,0,thr,&tid);

    while(-1==atomic_load_explicit(&tid,memory_order_relaxed)) ;

    printf("their tid=%lu\n", (long unsigned)tid);
    pthread_join(ptid,0);
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Both options are essentially waiting on CV to spin. I'm wondering which option is better for performance? esp. when there are a lot of threads to manage? Thanks so much :) – Shashank Hegde Jun 14 '19 at 17:01
  • @ShashankHegde I'd bet on the spinning version (atomics). Thread creation shouldn't so long as to warrant putting the parent thread to sleep. – Petr Skocik Jun 14 '19 at 18:19