Segmentation fault on void* parameter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <pthread.h>
using namespace std;

// const unsigned long  MAX_NUMBER = 15485863;
const unsigned long MAX_NUMBER = 30;

vector<unsigned long> numbers(MAX_NUMBER+1);

// thread specific arguments
struct thread_data
{
  unsigned int thread_id;
  unsigned long n; // data to sieve
  bool bw; // backward or forward search
};

void init_numbers(vector <unsigned long> * numbers);

void *prime_sieve(void *arg);

void init_numbers(vector <unsigned long> * numbers)
{
  unsigned long i;
  (*numbers)[2]=1;
  for (i=3;i<=MAX_NUMBER;i+=2){(*numbers)[i]=1;}
}

void *prime_sieve(void *arg)
{
  struct thread_data *my_data;

  my_data = (struct thread_data *)arg;
  cout << (my_data->n) << endl;

  unsigned long i;
  unsigned long end_pos = (int)(sqrt(my_data->n))+1;
  unsigned long c;
  unsigned long primes = 0;
  unsigned long pos_move;

  for (i=2;i <= end_pos;i++)
    {
      if (numbers[i] != 0)
        {
          primes++;
          c = i*i;
          pos_move = (i << 1);
          while (c <= my_data->n)
            {
              numbers[c]=0;
              c += pos_move;
            }
        }

    }
  for(i=(end_pos+1);i<=my_data->n;i++){primes+=numbers[i];}
  // return primes;
  cout << "Primes " << primes << endl;
  pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
  pthread_t threads[2];
  int rc;
  unsigned long n = MAX_NUMBER;
  init_numbers(&numbers);
  struct thread_data my_data;
  my_data.thread_id = 0;
  my_data.n = MAX_NUMBER;
  my_data.bw = false;
  rc = pthread_create(&threads[0], NULL, prime_sieve, (void *) &my_data);

  pthread_exit(NULL);
}


Output:

1076974296
Segmentation fault


When I try to load my local data in prime_sieve I get a segmentation fault / not the right values.

Why is that?

Last edited on
Possibly more importantly: why are you casting it to void* in the first place? Can't prime_sieve simply accept a thread_data*?
@altmann: You didn't call pthread_join() and terminated your main() before your spawned thread had a chance to do anything.

Add

1
2
        void* retval;
        pthread_join(threads[0], &retval);

at line 78, and get rid of pthread_exit() while you're at it.


@Gaminic it's a limitation of pthreads interface, C++ threads accept any kind of arguments, pthreads only accept a single void*


Mm, thank you very much. That helped me a lot.

However, there are some things which bother me still.

When I read https://computing.llnl.gov/tutorials/pthreads/

there is a section:

Discussion on calling pthread_exit() from main():

There is a definite problem if main() finishes before the threads it spawned if you don't call pthread_exit() explicitly. All of the threads it created will terminate because main() is done and no longer exists to support the threads.

By having main() explicitly call pthread_exit() as the last thing it does, main() will block and be kept alive to support the threads it created until they are done.


In my code I called pthread_exit, but it seems that main wasn't kept alive long enough.

On https://computing.llnl.gov/tutorials/pthreads/ I can read that a thread is set to joinable implementation dependent. So I thought perhaps it was just not set to joinable in my OS.

But then, if I run this code https://computing.llnl.gov/tutorials/pthreads/samples/hello_arg2.c slightly modified (added unistd.h), it works, but there is no pthread_join:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
******************************************************************************
 * FILE: hello_arg2.c
 * DESCRIPTION:
 *   A "hello world" Pthreads program which demonstrates another safe way
 *   to pass arguments to threads during thread creation.  In this case,
 *   a structure is used to pass multiple arguments.
 * AUTHOR: Blaise Barney
 * LAST REVISED: 01/29/09
 ******************************************************************************\
/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM_THREADS 8

char *messages[NUM_THREADS];

struct thread_data
{
  int thread_id;
  int  sum;
  char *message;
};

struct thread_data thread_data_array[NUM_THREADS];

void *PrintHello(void *threadarg)
{
  int taskid, sum;
  char *hello_msg;
  struct thread_data *my_data;

  // sleep(1);
  my_data = (struct thread_data *) threadarg;
  taskid = my_data->thread_id;
  sum = my_data->sum;
  hello_msg = my_data->message;
  printf("Thread %d: %s  Sum=%d\n", taskid, hello_msg, sum);
  pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
  pthread_t threads[NUM_THREADS];
  int *taskids[NUM_THREADS];
  int rc, t, sum;

  sum=0;
  messages[0] = "English: Hello World!";
  messages[1] = "French: Bonjour, le monde!";
  messages[2] = "Spanish: Hola al mundo";
  messages[4] = "German: Guten Tag, Welt!";
  messages[5] = "Russian: Zdravstvytye, mir!";
  messages[6] = "Japan: Sekai e konnichiwa!";
  messages[7] = "Latin: Orbis, te saluto!";

  for(t=0;t<NUM_THREADS;t++) {
    sum = sum + t;
    thread_data_array[t].thread_id = t;
    thread_data_array[t].sum = sum;
    thread_data_array[t].message = messages[t];
    printf("Creating thread %d\n", t);
    rc = pthread_create(&threads[t], NULL, PrintHello, (void *)
                        &thread_data_array[t]);
    if (rc) {
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
    }
  }
  pthread_exit(NULL);
}



Also, if I change unsigned long in vector to boolean and setting 0 to false and 1 to true, my thread program runs without problem getting the right values.

So why does the example at https://computing.llnl.gov/tutorials/pthreads/ works without pthread_join?
Last edited on
The tutorial is incorrect where it says
main() will block


The actual specification of pthread_exit() over at http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html states
After a thread has terminated, the result of access to local (auto) variables of the thread is undefined.
and
The process shall exit with an exit status of 0 after the last thread has been terminated


This means that pthread_exit() called from main() does block but only *after* exiting main() and destroying its stack frame (but before calling exit()). In your case, that means that even before your prime_sieve() get scheduled to run, my_data was gone and accessing my_data->n is what is probably segfaulting in your first example (it doesn't segfault on my linux actually, but it's undefined behavior anyway. You could debug if you're curious)

For your second test the observed effects of your undefined behavior changed, I guess because the data you're accessing now is on heap (std::vector stores its underlying array there) but it's hard to say, it's very implementation-specific. Just use join().

The tutorial example is accessing thread_data_array, which is a static object and exists even after main() exits.
Last edited on
Cool. Now I understand it.

Thanks a lot
Topic archived. No new replies allowed.