1

I have a function that takes in a pair of piped file descriptors, reads numbers from one pipe, sorts them and writes back up to the parent through a 2nd pipe:

void *sort_chunk(int fds[][2]) {
    close(fds[DOWN][WRITE]);
    FILE* fddr = fdopen(fds[DOWN][READ], "r");
    char str[25];
    vector<long int> nums;

    // Read from parent and sort
    while (fgets(str, 20, fddr)) {
        string fstr = string(str);
        fstr.erase(fstr.length() - 1); // Remove trailing line return
        nums.push_back(stol(fstr));
    }
    fclose(fddr);

    bubblesort(nums);

    // Write back to parent
    close(fds[UP][READ]);
    FILE* fduw = fdopen(fds[UP][WRITE], "w");
    for (auto it = nums.begin(); it != nums.end(); it++) {
        fprintf(fduw, "%ld\n", *it);
    }
    fclose(fduw);
    exit(0);
}

I want to run this function in multiple child threads:

int fds[2][2];
pipe(fds[DOWN]);
pipe(fds[UP]);
pthread_create(&threads[i], NULL, sort_chunk, fds);

When I try to create the thread though, i get:

mysort.cc:139:38: error: invalid conversion from ‘void* (*)(int (*)[2])’ to ‘void* (*)(void*)’ [-fpermissive]
  139 |    pthread_create(&threads[i], NULL, sort_chunk, fds);
      |                                      ^~~~~~~~~~
      |                                      |
      |                                      void* (*)(int (*)[2])
Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
doctopus
  • 5,349
  • 8
  • 53
  • 105
  • That's because `pthread_create` only accepts that one type of function (pointer) as an argument, you cannot pass something else in there. What exactly is unclear about it? – UnholySheep Sep 27 '20 at 18:05
  • 1
    The prototype of your thread function must exactly match `void* (*)(void*)`. Anything else will not compile. If you force it to compile with a cast, it will likely fail at runtime. You must make an appropriate function. You can cast the `void*` parameter to its true type inside the function. – user4581301 Sep 27 '20 at 18:09
  • @UnholySheep It takes a 4th argument which are args to the function – doctopus Sep 27 '20 at 18:09
  • 1
    Here's how to do it in C: [C , how to create thread using pthread_create function](https://stackoverflow.com/questions/6990888/c-how-to-create-thread-using-pthread-create-function). In C++ [you should use `std::thread`](https://en.cppreference.com/w/cpp/thread/thread) instead.. – user4581301 Sep 27 '20 at 18:13
  • @user4581301 you're right, i needed to cast the the args, so passing in `(void*) fds` instead. How do I recast `fds` to `int[2][2]` inside `sort_chunks()` though>? – doctopus Sep 27 '20 at 18:16
  • Side note: Multithreading file IO often isn't as useful as you'd like. All of the threads bottleneck over access to the file store. – user4581301 Sep 27 '20 at 18:16
  • `exit(0);` is a bad thing to have at the end of the thread. It will kill the whole program. You want `pthread_exit`. – user4581301 Sep 27 '20 at 18:31

1 Answers1

3

You need to fix your thread function to match the prototype that pthread_create expects:

void *sort_chunk(void *fds_) {
    int (*fds)[2] = fds_;   // works for C -- need an explicit cast for C++

The basic problem is that pthread_create expects a particular kind of function pointer with a single void * argument, so you can't safely call it any other way.

This works pretty well in C, but in C++, you'll need an explicit static_cast in the function, which is ugly. But for C++, you should probably be using std::thread anyways.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • I tried your code it didn't work. Instead I did `int (*fds)[2] = (int(*) [2]) fds_;` and it worked – doctopus Sep 27 '20 at 18:23
  • That's pretty much what Chris said to do, but he recommended a more restrictive cast. Avoid using the c-style cast. It turns off all of the compiler's brains and will allow horrible mistakes to bypass the type checking and become runtime errors. Use `static_cast(fds_)`, but seriously consider discarding `pthread` and using `std::thread`. It's more portable and harder to up. – user4581301 Sep 27 '20 at 18:28
  • Side note: be absolutely certain `int fds[2][2];` will outlive the thread. Really sucks when data the thread's pointing at goes out of scope. That's one of the things `std::thread` tries to help with. It defaults to taking a copy of the data. That said, you can't copy an array. They always pass by pointer because of array decay. You'd want a `std::array`. – user4581301 Sep 27 '20 at 18:29