user2965601
user2965601

Reputation:

C - Creating n threads

I am working on the following function. This function should create n threads. Also it should print the tid of the child thread. But at the moment I am little bit confused. When I execute it and for example I create 5 threads, it returns all the time the same tid. As far as I understood the tid is the thread Id of the caller. Does the same caller call all these threads or did I something wrong. Here's the code:

void spawnThreads( unsigned int n) {
   int threads = n, ret = -1;
   pthread_t * thread = malloc(sizeof(pthread_t)*threads);
   pid_t tid;
   int i;
   for(i = 0; i < threads; i++) {
       ret = pthread_creation(&thread[i], NULL, (void *(*)(void *)) foo, NULL); // foo does not do anything

       if( ret != 0) {
           printf("pthread error!\n");
       }

       tid = syscall(SYS_gettid);
       printf("%d %d\n", i, tid);
       printf("I just created thread %d\n", i);

       pthread_join(thread[i],NULL);
}

void * foo(void) {
    return NULL;
}

For example, I get the for the following input spawnThreads(4) the following output:

 0 2411
 I just created thread 0

 1 2411
 I just created thread 1

 2 2411
 I just created thread 2

 3 2411
 I just created thread 3

To sum up, the function should print >i< >tid<. >tid< denotes the TID of the child and >i< runs from 1 to n.

But why do I get four times the same tid? What did I do wrong? I would appreciate it, if someone could explain me what went wrong.

Upvotes: 4

Views: 15263

Answers (2)

Duck
Duck

Reputation: 27542

There are three pieces of info people are generally interested in: the process id, the thread id and the pthreads thread id. All pthread calls are self-contained in that they use on their own thread ids. Process ids and os thread ids may be important for reasons outside the pthreads API.

Pthread ids are reported by pthread_create and pthread_self, the former reports the thread it created and the latter, well, itself. Otherwise it is a chicken and egg situation. One thread can't ask another thread what its id is unless it already knows what its id is. If this is important some mechanism, a global list, some IPC, etc., has to be built to accomplish it.

#define _GNU_SOURCE

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

typedef struct
{
    int       i;
    pid_t     pid;   // linux pid
    pid_t     tid;   // linux thread id
    pthread_t ptid;  // pthreads tid    
} data;

void *foo(void *args)
{
    data *p = (data *) args;

    p->pid  = getpid();
    p->tid  = syscall(SYS_gettid);
    p->ptid = pthread_self();

    return(p);
}

void spawnThreads(unsigned int numThreads)
{
    int ret;
    pthread_t *tids = malloc(sizeof(pthread_t) * numThreads);

    int i;

    for (i = 0; i < numThreads; i++)
    {
        data *dp = malloc(sizeof(data) * numThreads);
        memset(dp, '\0', sizeof(*dp));

        dp->i = i;

        ret = pthread_create(&tids[i], NULL, foo, (void *) dp);

        if ( ret != 0)
            perror("pthread create error");
    }

    for (int i = 0; i < numThreads; ++i)
    {
        data *status;

        ret = pthread_join(tids[i], (void *) &status);

        if ( ret != 0)
            perror("pthread join error");
        else
        {
            printf("thread num %d joined and reports pthreadId of %lu "
                   "process pid of %d and linux tid of %d\n",
                   status->i, status->ptid, status->pid, status->tid);

            free(status);
        }
    }

    free(tids);
}

int main(int argc, char *argv[])
{
    printf("main thread reports pthreadId of............ %lu "
           "process pid of %d and linux tid of %ld\n",
           pthread_self(), getpid(), syscall(SYS_gettid));

    spawnThreads(5);

    return (0);
}

Upvotes: 0

Dmitri
Dmitri

Reputation: 9375

The reason you're getting the same TID for each thread is that you're calling syscall(SYS_gettid) from the main thread each time, rather than from within the new thread you create. You need to call it from inside your thread function and then provide a way to pass the information back to the main thread if it's needed there.

As an example of one way to do it (some error checking omitted):

Create a struct to hold a mutex, condition, the TID, and a flag to indicate when the TID is valid.

struct s_threadId {
  pthread_mutex_t   mtx;    /* mutex & condition to allow main thread to
                               wait for the new thread to  set its TID */
  pthread_cond_t    cond;   /* '' */
  pid_t             id;     /* to hold new thread's TID */
  int               ready;  /* to indicate when 'id' is valid, in case
                               the condition wait gets interrupted */
};

Then change your thread function to lock, set, and signal (and move it so that it's declaration is visible before spawnThreads()):

void *foo(void *arg)
{
  struct s_threadId *thId = arg;

  /* Lock mutex... */
  pthread_mutex_lock(&thId->mtx);

  /* Get and save TID and ready flag.. */
  thId->id = syscall(SYS_gettid);
  thId->ready = 1;
  /* ..and signal main thread that we're ready */
  pthread_cond_signal(&thId->cond);

  /* ..then unlock when we're done. */
  pthread_mutex_unlock(&thId->mtx);

  /* ... */

  return NULL;
}

...and modify your spawnThreads function to initialize/cleanup the struct members and get the TID once the thread sets it:

void spawnThreads(unsigned int n)
{
  pthread_t thread; /* reused for each thread, since they run 1 at a time */

  /* struct to pass back TID */
  struct s_threadId threadId;
  pthread_cond_init(&threadId.cond, NULL);  /* init condition */
  pthread_mutex_init(&threadId.mtx, NULL);  /* init mutex */

  int i;
  for (i = 0; i < n; i++) {
    /* lock mutex *before* creating the thread, to make the new thread
       wait until we're ready before signaling us */
    pthread_mutex_lock(&threadId.mtx);

    /* clear ready flag before creating each thread */
    threadId.ready = 0;
    /* create threads and pass address of struct as argument */
    if (pthread_create(&thread, NULL, foo, &threadId)) {
      printf("pthread error!\n");
    } else {
      /* Wait on the condition until the ready flag is set */
      while (!threadId.ready) {
        pthread_cond_wait(&threadId.cond, &threadId.mtx);
      }
      /* Now we have the TID... */
      printf("%d %d\n", i, threadId.id);
      printf("I just created thread %d\n", i);
    }
    /* ..and unlock the mutex when done. */
    pthread_mutex_unlock(&threadId.mtx);

    pthread_join(thread, NULL);
  }

  /* When we're completely done with the struct we need to clean up the
     mutex and condition variable */
  pthread_mutex_destroy(&threadId.mtx);
  pthread_cond_destroy(&threadId.cond);
}

In the above, the mutex and condition wait are needed to make sure that the main thread doesn't try to print the TID until the new thread has had a chance to set it. The main thread starts the new one and then waits, and the new thread signal when it's done storing the TID so the main thread can continue.

Upvotes: 3

Related Questions