lbenedetto
lbenedetto

Reputation: 2114

Pass a value into a thread from a while loop

I have a while loop that gets a value and calls a function in a new thread with the that information. The problem is I can't get it to give me a new address for that information in each iteration of the while loop. So the while loop ends up modifying the information being used by the running thread.

Here is an excerpt from my code. The printf statement reveals that the address of string is the same, and is therefore overwritten while the thread is running

    char * string = (char *) malloc(1024 * sizeof(char));
    strcpy(string, item);
    if (pthread_create(&thread, NULL, printWithDelay, &string)) {
        fprintf(stderr, "Error creating thread\n");
        return 1;
    }
    printf("Dispatched thread for %s at %p\n", string, &string);
    free(string);

I have also tried without malloc and that doesn't work either. Is there any way to do this?

Before you ask, yes I am a student, and yes this is for an assignment. However, the assignment didn't ask me to do any of this. We aren't even supposed to know threads exist yet. I'm just trying to push myself to go beyond the bounds of the assignment and am having a bit of trouble. I'm trying to do sleepsort as a joke to the grader whom I know personally.

Full code and assignment description here: http://hastebin.com/cetepakevu.c

Upvotes: 1

Views: 1339

Answers (2)

Craig Estey
Craig Estey

Reputation: 33631

With your present pthread_create call, you have a race condition because you're passing &string instead of string (i.e. you're passing a pointer to the main thread's string variable instead of its contents). Thus, two [or more] child threads could end up using the same string buffer.

Also, you have the main thread doing the free on the string. This must be done in the child thread. A free works globally for all threads, so main does the free before the child can use the data [or gets it yanked out from under itself shortly thereafter].

Further, a "minor" problem that is a consequence of that: Because main does the malloc and the free [assuming no child thread does any malloc], it is likely that malloc will always return the same value. So, all child threads could be using the same string buffer, race condition or not.

On the other hand, if a child thread does a malloc for something else, it is now racing against the use of string [already allocated but freed] and the new allocation. Thus, someone holding/using a prior value for string could cause heap corruption [and/or segfault] if either of the two areas is written to in the "right" way, because the malloc/heap link pointers may be in the middle of a different allocation that overlaps.


Here's what I think you have [because you didn't show the child function]:

void
main_thread(void)
{

    while (1) {
        char *string = (char *) malloc(1024 * sizeof(char));

        strcpy(string, item);

        // BUG: race condition -- on the second loop, string can be changed
        // before thread 1 has a chance to dereference it, so thread 1
        // could end up using thread 2's string (or thread 3's ...)
        if (pthread_create(&thread, NULL, printWithDelay, &string)) {
            fprintf(stderr, "Error creating thread\n");
            return 1;
        }

        // BUG: child must do the free
        printf("Dispatched thread for %s at %p\n", string, &string);
        free(string);
    }
}

void *
printWithDelay(void *arg)
{
    char **strptr = arg;
    char *string;

    // BUG: this is the race condition -- we can get our string or
    // if we're delayed, we'll get the string for the next thread, so
    // we'd both use the same address
    string = *strptr;

    // use string ...

    return (void *) 0;
}

Here's the corrected code:

void
main_thread(void)
{

    while (1) {
        char *string = (char *) malloc(1024 * sizeof(char));

        strcpy(string, item);

        if (pthread_create(&thread, NULL, printWithDelay, string)) {
            fprintf(stderr, "Error creating thread\n");
            return 1;
        }
    }
}

void *
printWithDelay(void *arg)
{
    char *string = arg;

    // use string ...

    free(string);

    return (void *) 0;
}

Upvotes: 4

marshal craft
marshal craft

Reputation: 447

Though pthread_create() takes a void * for it's arg, you can pass by value by casting. I haven't verified this however this answer seems to do what you want. pthread_create: Passing argument by value

I think the best thing would be to consider a different thread api (or language) or simply write a clone function and pass the cloned variable's address.

Upvotes: 0

Related Questions