Question

I am working on a school project (simulation of virtual memory), where we are supposed to use detached threads. There are also other limitations to what we can use but I will mention that later. The problem is that when I give the pthread_create function the last argument as (void*)something and the created thread is detached, the function called by pthread_create will recieve the argument, but since the thread is detached, the original arguments are deleted as soon as the function from where I called pthread_create finishes - that means the argument in the called function is no longer valid and therefore I get segmentation faults etc.. Here is a part of the code (cant post it whole, its very big):

bool CProcManager::NewProcess(void* processArg, void(*entryPoint)(CCPU*, void*), bool copyMem) {
uint32_t free_page;
pthread_attr_t attr;
pthread_t thread;

if (proc_cnt >= 63)
    return 0;

pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

if (!FindFreePage(free_page)) return 0;

free_page = (free_page << 12) | 7;
CProcManager ccpu(m_MemStart, free_page);
ThrArgs args(entryPoint, (void *) processArg, ccpu);

proc_cnt++;
if (pthread_create(&thread, NULL, StartWork, (void *) &args))
    return 0;

//pthread_mutex_lock(&start_mtx);
//pthread_cond_wait(&start_cond, &start_mtx);
//pthread_mutex_unlock(&start_mtx);

return 1;
}

Here you can see that this function gets a poitner to void and a pointer to function as parameters. pthread_creates tells the thread to call the "StartWork" function with argument "args", which is a structure containing a pointer to function, a pointer to void and an object of type CProcManager.

void* CProcManager::StartWork(void* arguments) {
//pthread_mutex_lock(&start_mtx);
ThrArgs *args = (ThrArgs*) arguments;
CProcManager ccpu = args->ccpu;

//pthread_cond_signal(&start_cond);
//pthread_mutex_unlock(&start_mtx);

args->entryPoint(&ccpu, args->processArg);

ccpu.RemovePage((ccpu.m_PageTableRoot >> 12) * PAGE_SIZE, false);

pthread_mutex_lock(&end_mtx);
proc_cnt--;
pthread_cond_signal(&end_cond);
pthread_mutex_unlock(&end_mtx);
return (NULL);
}

In this function, I finally call the function I had a pointer to. As you can guess, the arguments become invalid when the "NewProcess" function ends, so I tried to overcome this by adding a condition variable and somehow make a copy of the arguments I have (havent come to a solution yet) and then let the "NewProcess" function end. But there are certain limitations in this project. The function "NewProcess" is being called many times and the threads should run simultaneously (I tried signaling the condition variable after calling entryPoint and that worked), so I cant finish one thread and then do the next. The parameter "void* processArg" of the first function is originally a certain object type and this type cannot be accessed from the two functions above.

So can anyone please give me a suggestion how copy the arguments so I dont have a segmantation fault?

Was it helpful?

Solution 2

There are two solutions. The simplest is often just to allocate the arguments dynamically: in NewProcess:

ThrArgs* args = new ThrArgs( entryPoint, processArg, ccpu );
bool results = pthread_create( &thread, NULL, StartWork, args ) == 0;
if (!results) {
    delete args;
}

(You could use std::unique_ptr or std::auto_ptr here as well. In which case, don't call release on it unless pthread_create succeeds!)

The alternative is to create a conditional variable over a boolean, then wait on it before leaving the function, and copy the parameters and then set the boolean in the new thread: in NewProcess:

pthread_mutex_lock( &theMutex );
freeToContinue = false;
bool results = pthread_create( &thread, NULL, StartWork, args ) == 0;
if ( results ) {
    while ( !freeToContinue ) {
        pthread_cond_wait( &theCond, &theMutex );
    }
    pthread_mutex_unlock( &theMutex );
}
return results;

and in StartWork (the function you pass to pthread_create must be extern "C", and so cannot be a member):

void* StartWork( void* arguments )
{
    pthread_mutex_lock( &theMutex );
    ThrArgs args = *static_cast<ThrArgs*>( arguments );
    freeToContinue = true;
    pthread_cond_broadcast( &theCond );
    pthread_mutex_unlock( &theMutex );
    //  ...
}

(Obviously, I've left out a lot of error checking here.)

Either way: you may have to do something similar with regards to the processArg argument, since its lifetime is determined by that of the caller.

And you don't need all of the casts to void*.

OTHER TIPS

The problem is very simple and is one of object lifetimes and ownership.

You are passing the address of 'args' to the thread, but 'args' is created on the stack and goes out of scope soon after the thread has been started.

do it something like this:

// note: The ThrArgs constructor should take ownership of processArg
std::unique_ptr<ThrArgs> args (new ThrArgs(entryPoint, (void *) processArg, ccpu));
if (pthread_create(&thread, NULL, StartWork, (void *) args.get())) {
    args.release();
    // ownership of the pointer now belongs to the thread
    return 0;
}

then in the thread function:

void* CProcManager::StartWork(void* arguments) {
    std::unique_ptr<ThrArgs> args (reinterpret_cast<ThrArgs*>(arguments)); 

    // now use args as a pointer

    ....


    // the arguments will be deleted here
    return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top