Search code examples
c++linuxmultithreadingclone

wait() for thread made via clone?


I plan on rewriting this to assembly so I can't use c or c++ standard library. The code below runs perfectly. However I want a thread instead of a second process. If you uncomment /*CLONE_THREAD|*/ on line 25 waitpid will return -1. I would like to have a blocking function that will resume when my thread is complete. I couldn't figure out by looking at the man pages what it expects me to do

#include <sys/wait.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

int globalValue=0;

static int childFunc(void*arg)
{
    printf("Global value is %d\n", globalValue);
    globalValue += *(int*)&arg;
    return 31;
}

int main(int argc, char *argv[])
{
    auto stack_size = 1024 * 1024;
    auto stack = (char*)mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
    if (stack == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }

    globalValue = 5;

    auto pid = clone(childFunc, stack + stack_size, /*CLONE_THREAD|*/CLONE_VM|CLONE_SIGHAND|SIGCHLD, (void*)7);
    sleep(1); //So main and child printf don't collide
    if (pid == -1) { perror("clone"); exit(EXIT_FAILURE); }
    printf("clone() returned %d\n", pid);

    int status;
    int waitVal = waitpid(-1, &status, __WALL);

    printf("Expecting 12 got %d. Expecting 31 got %d. ID=%d\n", globalValue, WEXITSTATUS(status), waitVal);
    return 0;
}

Solution

  • If you want to call functions asynchronously with threads I recommend using std::async. Example here :

    #include <iostream>
    #include <future>
    #include <mutex>
    #include <condition_variable>
    
    int globalValue = 0;    // could also have been std::atomic<int> but I choose a mutex (to also serialize output to std::cout)
    std::mutex mtx;         // to protect access to data in multithreaded applications you can use mutexes 
    
    int childFunc(const int value)
    {
        std::unique_lock<std::mutex> lock(mtx);
        globalValue = value;
        std::cout << "Global value set to " << globalValue << "\n";
        return 31;
    }
    
    int getValue()
    {
        std::unique_lock<std::mutex> lock(mtx);
        return globalValue;
    }
    
    int main(int argc, char* argv[])
    {
        // shared memory stuff is not needed for threads
    
        // launch childFunc asynchronously
        // using a lambda function : https://en.cppreference.com/w/cpp/language/lambda
        // to call a function asynchronously : https://en.cppreference.com/w/cpp/thread/async
        // note I didn't ues the C++ thread class, it can launch things asynchronously
        // however async is both a better abstraction and you can return values (and exceptions)
        // to the calling thread if you need to (which you do in this case)
        std::future<int> future = std::async(std::launch::async, [] 
        {
            return childFunc(12); 
        });
    
        // wait until asynchronous function call is complete
        // and get its return value;
        int value_from_async = future.get();
    
        std::cout << "Expected global value 12, value = " << getValue() << "\n";
        std::cout << "Expected return value from asynchronous process is 31, value = " << value_from_async << "\n";
        return 0;
    }