Reputation: 20891
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 topthread_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
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:
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.
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).
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
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