Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++, threads, and pointers

I am using std::thread to execute multiple threads. I pass a pointer to an array as an argument, something akin to:

my_type* rest[count];
//Fill rest array
std::thread(fnc, rest, count);

The issue I seem to be having, is that somewhere along the way, the pointer values in 'rest' get corrupted. I print out the pointer values before the call to std::thread, and first thing in the function that std::thread calls on my behalf, and the values do not match. It seems fairly random, sometimes they will match, and sometimes not (and a segfault results when the latter happens).

I know (from what little I could find on the topic) that std::thread copies the arguments, and I am thinking that my issue stems from this, and that there is a special function std::ref() that allows it to pass references, but none of them mention pointers specifically. I have tried various techniques to attempt to pass this array with std::ref() but I have yet to solve the issue.

Am I correct in thinking that this could be the cause of my issue, or am I barking up the wrong tree?

like image 538
latreides Avatar asked Oct 04 '13 01:10

latreides


People also ask

What is a thread C?

A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate path of execution.

Is C language multi threaded?

Can we write multithreading programs in C? Unlike Java, multithreading is not supported by the language standard. POSIX Threads (or Pthreads) is a POSIX standard for threads.

How to pass two arguments to thread in C?

Example 1 - Thread Argument Passing long taskids[NUM_THREADS]; for(t=0; t<NUM_THREADS; t++) { taskids[t] = t; printf("Creating thread %ld\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]); ... } See the source code.

How to return values from threads in C?

If you want to return only status of the thread (say whether the thread completed what it intended to do) then just use pthread_exit or use a return statement to return the value from the thread function.


1 Answers

if gets converted in some fashion (the array pointer, not the contents) then I would have a problem.

Yes, that's exactly what happens.

It's often incorrectly said that arrays are just pointers. The truth of the matter is that whenever you declare a function that takes an array:

void foo(int x[10]);

The declaration is 'adjusted' so that the parameter is a pointer:

void foo(int *x); // C++ can't tell the difference between this and the first declaration

and when you call the function:

int x[10];
foo(x);

There's an implicit conversion equivalent to the following:

int x[10];

int *tmp = &x[0];

foo(tmp);

So what happens is that you have a block of memory containing your pointers to long lived objects:

my_type *rest[count] = {new my_type, new my_type, new my_type};

You pass a pointer to that block of memory to the thread:

thread(fnc, &rest[0], count);

Then when the function returns rest goes out of scope, and that block of memory is no longer valid.

Then the thread follows the pointer to the block of memory and reads garbage. If by some chance it does read the correct array contents then it can access the long lived objects just fine. The problem is getting the pointers to the long lived objects from the corrupt block of memory where rest used to be on the stack.

Is there a way to suppress this behavior?

In most cases the only thing that makes sense is not use raw arrays as function parameters. You can wrap a raw array in a struct and get the sensible behavior:

struct int_array {
  int x[10];
};

void foo(int_array x);

int main() {
  int_array x = {1,2,3,4,5,6,7,8,9,0};
  foo(x); // the array is copied rather than getting strangely converted
}

This is pretty much exactly what std::array does, so you're better off using it.

In cases where you don't want a copy of the array you can take a reference to the array:

int foo(int (&x)[10]);

This gives you essentially the same behavior as the weird 'adjustments' and implicit conversions that are done behind your back with int foo(int x[10]); foo(x);. The benefit here is that it's explicit and that you get type checking on the size of the array. That is, due to the 'adjustment' the following does not result in a compiler error:

int foo(int x[10]);

int x[3];
foo(x);

Whereas this will:

int foo(int (&x)[10]);

int x[3];
foo(x); // the implicit conversion to &x[0] does not get happen when the function takes a reference to array
like image 73
bames53 Avatar answered Sep 23 '22 15:09

bames53