0

Please consider the below code:

cuckoo.c

#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>

void *thread_fn(void *vargp);

int main(int argc, char **argv){
        if (argc < 2){
                fprintf(stderr, "Meh, error!\n");
                return 1;
        }
        pthread_t span[argc];
        for (int i=1; i < argc; i++){
                int input = atoi(argv[i]);
                printf("input: argv[%d] = %s\n",i, argv[i]);
                int rc = pthread_create(&span[i], NULL, thread_fn, &input);
                assert (rc == 0);
        }
        for (int i=1; i < argc; i++){
                int *output;
                int err = pthread_join(span[i], (void **)&output);
                assert (err == 0);
                printf("in main: from thread %lu, input = %s, output = %d\n", span[i], argv[i], *output);
                free(output);
        }
}

void *thread_fn(void *vargp){
        int *input = (int *)vargp;
        int *output = malloc( sizeof(*output) );
        for (int i=0; i <= *input; i++){
                *output += i;
        }
        printf("in thread_fn: %lu, input = %d, output = %d\n", pthread_self(), *input, *output);
        pthread_exit(output);
}

When I run this with individual arguments, it behaves well:

$ ./a.out 4
input: argv[1] = 4
in thread_fn: 139691607996160, input = 4, output = 10
in main: from thread 139691607996160, input = 4, output = 10
$ ./a.out 5
input: argv[1] = 5
in thread_fn: 140564160374528, input = 5, output = 15
in main: from thread 140564160374528, input = 5, output = 15

However, if a pass a number of arguments, it falls all over:

$ ./a.out $(seq 1 5)
input: argv[1] = 1
input: argv[2] = 2
in thread_fn: 139922608498432, input = 2, output = 3
input: argv[3] = 3
input: argv[4] = 4
in thread_fn: 139922518308608, input = 4, output = 10
input: argv[5] = 5
in thread_fn: 139922375698176, input = 5, output = 15
in thread_fn: 139922600105728, input = 5, output = 15
in thread_fn: 139922509915904, input = 5, output = 15
in main: from thread 139922608498432, input = 1, output = 3
in main: from thread 139922600105728, input = 2, output = 15
in main: from thread 139922518308608, input = 3, output = 10
in main: from thread 139922375698176, input = 4, output = 15
in main: from thread 139922509915904, input = 5, output = 15

What am I doing wrong here? Is this approach not recommended? I was able to accomplish this instead with a struct as below, and I still cannot make the above snippet function similarly. I would still like to learn and fix the above pasted code.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <pthread.h>

void *pappanava(void *vargp);
struct payload{
        int input;
        int sum;
};


int main(int argc, char **argv){
        if (argc < 2){
                fprintf(stderr, "Invalid usage\n");
                return 1;
        }

        //struct payload *this = malloc( sizeof(*this) );
        pthread_t span[argc];
        for (int i=1; i< argc; i++){
                printf("i/p: %s\n", argv[i]);
                struct payload *this = malloc( sizeof(*this) );
                this->input = atoi(argv[i]);
                int rc = pthread_create(&span[i], NULL, pappanava, &this->input);
        }

        for (int i=1; i< argc; i++){
                struct payload *this;
                pthread_join(span[i], (void**)&this);
                printf("In _main_: thread: %lu, input: %d, sum: %d\n", span[i], this->input, this->sum);
                free(this);
        }

        return 0;

}


void *pappanava(void *vargp){
        struct payload *this = ( struct payload *) vargp;
        int sum = 0;
        for (int i=0; i <=  this->input; i++){
                sum += i;
        }

        this->sum = sum;
        printf("In fn: thread: %lu, input: %d, sum: %d\n", pthread_self(), this->input, this->sum);

        pthread_exit(this);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
struggling_learner
  • 1,214
  • 15
  • 29
  • 2
    [don't print `pthread_t` with `%lu`](https://stackoverflow.com/q/1759794/995714), and [don't use `atoi`](https://stackoverflow.com/q/17710018/995714) – phuclv May 22 '18 at 02:56
  • 3
    I would think that `input` would be at the same memory location in every iterations in the `main` for loop, causing a [race condition](https://en.wikipedia.org/wiki/Race_condition). You could solve this by using an `array` of `int` for `input`. – dvhh May 22 '18 at 03:00
  • 2
    @dvhh agree. Instead of using `int input`, I use `int input[argc]`, the output will be right. – Paul May 22 '18 at 03:05

1 Answers1

2

Looking at the compilation result of the first program

It look like int input is using the same memory location when it is passed to your thread, instead of using a new memory location, like you would expect, causing a race condition.

One way of solving your issue would be to use an array of input

...
pthread_t span[argc];
int input[argc];
for (int i=1; i< argc; i++){
     input[i] = atoi(argv[i]);
     ...
     int rc = pthread_create(&span[i], NULL, thread_fn, &input[i]);
     ...

Other way would be to allocate memory for the input

 ...
 pthread_t span[argc];
 for (int i=1; i< argc; i++){
     int *input=malloc(sizeof(int));
     *input = atoi(argv[i]);
     ...
     int rc = pthread_create(&span[i], NULL, thread_fn, input);
     ...

Of course using this solution to avoid memory leak you would leave it to the thread procedure to free the memory allocated for the input.

dvhh
  • 4,724
  • 27
  • 33
  • assert aside, it seems these two int assignments are also susceptible then: `int rc = pthread_create(&span[i], NULL, thread_fn, &input);` and `int err = pthread_join(span[i], (void **)&output);`. What's the best practice to ensure these int values aren't stale, in case of iterative loops such as these? Use `int input[argc], rc[argc], err[argc];` or setup, use and then tear down `int *rc` and `int *err` for each iteration? – struggling_learner May 22 '18 at 05:02
  • `rc` and `err` are only used locally in a single threaded environment (and within the stack on main), the value within the thread will remain consistent as long as no other thread touches them. Where input is modified within this thread but read from others, there is no guarantee ( except if you use locks, critical section, any mean of synchronization to ensure order ) of the order of assignment and read. – dvhh May 22 '18 at 05:38