Accessing to return value of a thread by another one other than creating one

Why have copyfilepass return a pointer to the number of bytes copied when callcopypass can access this value as args[2]?

#include <unistd.h>
#include "restart.h"

void *copyfilepass(void *arg)  {
   int *argint;

   argint = (int *)arg;
   /* copyfile copies from a descriptor to another */
   argint[2] = copyfile(argint[0], argint[1]);
   close(argint[0]);
   close(argint[1]);
   return argint + 2;
}

callcopypass.c

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#define PERMS (S_IRUSR | S_IWUSR)
#define READ_FLAGS O_RDONLY
#define WRITE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC)
void *copyfilepass(void *arg);

int main (int argc, char *argv[]) {
   int *bytesptr;
   int error;
   int targs[3];
   pthread_t tid;

   if (argc != 3) {
      fprintf(stderr, "Usage: %s fromfile tofile\n", argv[0]);
      return 1;
   }

   if (((targs[0] = open(argv[1], READ_FLAGS)) == -1) ||
       ((targs[1] = open(argv[2], WRITE_FLAGS, PERMS)) == -1)) {
      perror("Failed to  open the files");
      return 1;
   }
   if (error = pthread_create(&tid, NULL, copyfilepass, targs)) {
      fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
      return 1;
   }
   if (error = pthread_join(tid, (void **)&bytesptr)) {
      fprintf(stderr, "Failed to join thread: %s\n", strerror(error));
      return 1;
   }
   printf("Number of bytes copied: %d\n", *bytesptr);
   return 0;
}

The authors answer that

if a thread other than the creating thread joins with copyfilepass, it has access to the number of bytes copied through the parameter to pthread_join.

I don't even comprehend the answer. How another thread can access the return value (i.e. change the value?) other than creating one? Could you explain it if possible with an example?

Upvotes: 0

Views: 100

Answers (2)

vgru
vgru

Reputation: 51204

The problem isn't that a different thread would want to "change the return value", it's whether a different thread will have access to the input parameters (targs). Generally, pthread_join allows you to get the result value from a certain thread, from any place in your program, as long as you have the thread id. So isn't it sensible to use this value to return the result of the async operation?

However, this example is rather poorly written (as an example for good multithreading practices), for a number of reasons:

  1. There is only a single function, and the scope of all variables extends through main. Written this way, everyone has access to the input args anyway. You are right when you say that reading the result through pthread_join is unnecessary in this case.

  2. Passing a stack variable (targs) to a thread is a bad idea. The variable will go out of scope when the function ends, so the only safe way for the program not to crash is to join the thread immediatelly, preventing targs from going out of scope. Which means you don't get any benefits of multithreading (unless main does some extra work before joining). They should be either made global, or allocated on the heap (a malloc/free pair).

  3. Files are opened inside main, but closed inside copyfilepass. This shift of responsibility is unnecessary, although not uncommon. I would either pass the file names to the function and handle the opening there, or close the handles outside the thread, after the files are copied.

Anyway, the point that author of the code had was that you don't need to have access to the input arguments at the place where you're joining the thread:

// removed all error checks for simplicity
int main (int argc, char *argv[]) {

   pthread_t tid;    

   // removed all error checks for simplicity
   pthread_create(&tid, NULL, copy_file, argv);

   // note that this function only accepts the thread id
   wait_until_copied(tid);

   return 0;
}

void wait_until_copied(pthread_t tid)
{
    int *bytesptr;
    // no way to access input args here
    pthread_join(tid, (void **)&bytesptr);
    printf("Number of bytes copied: %d\n", *bytesptr);
}

Upvotes: 2

colavitam
colavitam

Reputation: 1322

The crux of the answer is that you could foreseeably want to read the result of the copyfilepass thread (in this case the number of bytes copied) from a thread other than the thread which created it. Assume, for the sake of example, we have a third thread, monitorcopy, and tid is a global instead of a local variable. monitorcopy is spawned after copyfilepass from the main method.

void* monitorcopy(void* params) {
  void *result
  pthread_join(tid, &result);
  /* Point A: Attempt to read result */
}

Assume copyfilepass returned NULL, or a meaningless value. At Point A, result is NULL and we have no way of retrieving the number of bytes copied, as it is stored in targs[2] in the main method, which is out of scope.

Assume instead copyfilepass returned argint + 2. result is now a pointer to the number of bytes copied, even though we are not in the same scope as targs. Thus, in the absence of any memory lifetime issues, we can access the number of bytes copied as follows:

void* monitorcopy(void* params) {
  void *result
  pthread_join(tid, &result);
  int bytesCopied = *((int*) result);
}

Upvotes: 2

Related Questions