1

I've got 2 functions, func1() and func2(). func2 takes a character array as input. Both functions run on different threads. I call func2 from func1. When I passed a stack allocated array to func2, I got garbage values when I printed the array from inside func2(). However, when I passed a heap allocated array to func2, I got the correct string inside func2() i.e.

func2(char * char_array)
{
/* Some code */
cout<<char_array;
}

/* This does not work(Garbage values were printed in func2()) */

func1()
{
char array_of_char[SIZE];
memset(array_of_char,0,SIZE);
strncpy(array_of_char,"SOME_STRING",SIZE);
func2(array_of_char); //Asynchronous call to func2(). func1() proceeds immediately.
/*
some code
*/
}

/* This works(Correct values were printed in func2) */

func1()
{
char * array_of_char=new char[SIZE];
memset(array_of_char,0,SIZE);
strncpy(array_of_char,"SOME_STRING",SIZE);
func2(array_of_char); //Asynchronous call to func2(). func1() proceeds immediately.
/*
some code
*/
}

Does this mean that in multi-threaded programs, whenever some pointer has to be passed between different threads, the pointer should always be pointing at a heap-allocated memory?

Please note that func2() is actually a callback function which executes on the occurrence of an event. I've hidden those details in the question. Func1() does not stop/wait for the execution of func2().

Edit: I feel like I need to provide some more details of my implementation. In my program, I'm using Datastax C++ client library for Cassandra. Please find the link in the end, containing some details of the functions used in the program:

int main()
{

func1();
/* some code */
return 0;
}

/* This does not work(Garbage is printed in func2) */

void func1()
{
/* some code */
char array_of_char[SIZE];
memset(array_of_char,0,SIZE);
strncpy(array_of_char,"SOME_STRING",SIZE);
CassFuture* l_query_future = NULL;
/* some code where query is created */
l_query_future = cass_session_execute(rtGetSession(), l_stmt); //l_stmt is the query statement, rtgetSession() returns CassSession *
cass_future_set_callback ( l_query_future, func2, (void *)array_of_char); //details in the link given in the end
/* some code */
}

/* This works(Correct values were printed in func2) */

void func1()
{
/* some code */
char * array_of_char=new char[SIZE];
memset(array_of_char,0,SIZE);
strncpy(array_of_char,"SOME_STRING",SIZE);
CassFuture* l_query_future = NULL;
/* some code where query is created */
l_query_future = cass_session_execute(rtGetSession(), l_stmt); //l_stmt is the query statement, rtgetSession() returns CassSession *
cass_future_set_callback ( l_query_future, func2, (void *)array_of_char);
/*
some code
*/
}


void func2(CassFuture* l_query_future, void * data)
{
/* some code */
cout<<(char *)data;
}

References for Datastax driver APIs:

  1. cass_future_set_callback
  2. CassFuture
  3. CassSession
  4. cass_session_execute
Vishal Sharma
  • 1,670
  • 20
  • 55
  • 2
    You are not creating threads in the sample you submitted, hard to tell exactly what's happening. Your problem probably comes from a bad lifetime of your stack memory in func1 – Clonk Jun 04 '18 at 08:17
  • Also, if you're using C++ I strongly advise using std::string – Clonk Jun 04 '18 at 08:18
  • `array_of_char` will be valid until the end of scope it is in, that means `func2` will have a valid pointer unless you launch a thread and pass `func2` and array pointer as params to it. But you have not shown this in your question. – Killzone Kid Jun 04 '18 at 08:40
  • 2
    No offence dear OP, but how does a crappy question like this attract so much attention? What on earth (to pick just one of many holes in it) does `Asynchronous call to func2()` mean? We should be asking for a [MCVE]. – Paul Sanders Jun 04 '18 at 08:43
  • @Paul Sanders, I've added some more details. Please check and let me know if you need some more details. – Vishal Sharma Jun 04 '18 at 09:25
  • Yes, that certainly helps, although, for me, it's hardly 'minimal' as I don't know anything about Cassandra. Anyway, we already know the answer to this question - don't play fast and loose with pointers to objects on the stack. As you now know, these objects are destroyed when the function exits so these pointers become invalid. After that, anything can happen. So instead, you need to allocate them on the heap and manage their lifetimes properly. To do this, I recommend you read up on [smart pointers](https://stackoverflow.com/a/106614/5743288) as these can make the job a lot easier. – Paul Sanders Jun 04 '18 at 09:34
  • And if I didn't care so much about my precious rep then I would vote _all_ these answers down, since nobody bothered to ask you to clarify what you were _actually_ doing. – Paul Sanders Jun 04 '18 at 09:35
  • That's why I had wanted to hide Cassandra specific details in the question earlier because obviously, not everyone is familiar with it. – Vishal Sharma Jun 04 '18 at 09:41
  • OK, well, good intentions I guess but hiding important details like that doesn't work. Perhaps you now have a better idea of what a [MCVE] actually is and can ask a more effective question next time around. I think your question, as you have now updated it, is actually pretty good. Please forget my gripe about not knowing Cassandra - that's my problem, not yours. Upvote for sticking with it. – Paul Sanders Jun 04 '18 at 09:58

4 Answers4

3

How do you run func1() and func2() under different threads? func1() directly calls func2(), so they run under the same thread. Even the first implementation of func1() should work, as the array is still in its place.

EDIT:

But calling func2() directly from within func1() isn't an "asynchronous call to func2()" (even if at some other point it's used as a thread function). "Asynchronous call" means creating a new thread with func2() as the thread function. If so, this behaviour is very much expected, because func1() may have exited when func2() runs, and the array wouldn't exist by that time. On the other hand, the heap-block would still be allocated, so this would work. func2() should release the block then.

EDIT2:

Ummm, yes, the 2nd version is indeed an "asynchronous call to func2()", so the objects' lifetime considerations listed above indeed apply.

Constantine Georgiou
  • 2,412
  • 1
  • 13
  • 17
2

Does this mean that in multi-threaded programs, whenever some pointer has to be passed between different threads, the pointer should always be pointing at a heap-allocated memory?

No. This means that you must keep track of the lifetimes of your objects properly. When thread 1 finishes execution, the stack will be automatically cleaned up, thus spoiling the results that thread 2 is working with. Heap memory on the other hand stays if not explicitely freed. You have to check in thread 1 whether thread 2 is still executing and wait until it is finished, e.g. by using the join function.

Jodocus
  • 7,493
  • 1
  • 29
  • 45
2

No, the pointer does not have to point to heap-allocated memory, but you have to ensure the memory (in this case - an array) will be available until you join the thread.

Here, in the version that doesn't work, the array is allocated on the stack, and when the function func1 finishes, it is destroyed. Hence the rubbish values - likely something has written at that address slready.

To work around this, you could wait until the thread finishes in func1. The local variable would in this case be OK.

hauron
  • 4,550
  • 5
  • 35
  • 52
  • So isn't it better to pass heap allocated memory in such situations so that we don't have to ensure that the memory being referred is still available? – Vishal Sharma Jun 04 '18 at 08:41
  • 1
    @VishalSharma depends - dynamic allocation costs, and maybe the data you wish to share can happily lay on the stack (i.e. perhaps the thread(s) are meant to update some object's state? maybe its queue?) – hauron Jun 04 '18 at 08:44
2

This code runs fine with the array allocated on the stack. As has been mentioned there is absolutely no threading happening here. To answer your question though, when passing pointers (or other data) between different threads (when you are actually using them) you would likely need some kind of synchronization such as a mutex or atomics, and of course ensure the lifetimes of any data.

Here is your code working,

#include <iostream>
#include <cstring>
using namespace std;

#define SIZE 20

void func2(char * char_array)
{
    /* Some code */
    cout<<char_array;
}

void func1()
{
    char array_of_char[SIZE];
    strncpy(array_of_char,"SOME_STRING",SIZE);
    func2(array_of_char);
}

int main() {
    func1();
    return 0;
}

Demo

I should note that you are copying additional garbage by strncpy'ing SIZE into the array, you really just want to copy strlen("SOME_STRING").

rmawatson
  • 1,909
  • 12
  • 20