4

I have multi thread application in which I'm creating a thread like this:

int main(int argc,char *argv[])
{
    pthread_t thread_id[argc-1];
    int i;
    struct parameter thread_data[argc-1];
    int status;
    for(i=0;i<argc-1;i++)
    {
      thread_data[i].ip_filename = argv[i+1];
      strcpy (thread_data[i].op_filename,argv[i+1]);
      strcat (thread_data[i].op_filename,".h264");
    }

    for(i=0;i<argc-1;i++)
    {
      pthread_create (&thread_id[i], NULL , &thread_function, &thread_data[i]);
    }      
}

Now in the thread function, I want to redirect stderr & stdout in one separate file as per thread. Something like a thread log file.

How can I do so?

Edit:

If thread specific prints can be displayed on different terminal..? I mean if there are 2 threads then it opens 2 terminals & prints each threads data on different terminals.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Jeegar Patel
  • 26,264
  • 51
  • 149
  • 222

5 Answers5

7

If you really must do this...

First you need to create 2 pthread_key_ts, one for stdout and one for stderr. These can be created using pthread_key_create, and they must be accessable from all threads. Let's call them stdout_key and stderr_key.

When a thread is being created:

FILE *err = ..., *out = ...;
pthread_setspecific(stdout_key, out);
pthread_setspecific(stderr_key, err);

and then in your header file:

#define stdout (FILE*)pthread_getspecific(stdout_key)
#define stderr (FILE*)pthread_getspecific(stderr_key)
#define printf(...) fprintf(stdout, ##__VA_ARGS__)

then just use:

fprintf(stderr, "hello\n");
fprintf(stdout, "hello\n");
printf("hello\n");

I don't recommend this approach though.

Emil Romanus
  • 794
  • 1
  • 4
  • 8
  • 1
    `pthread_getspecific` does not take a string argument; it takes a `pthread_key_t` created by `pthread_key_create`. If your code is working, it's purely due to chance - probably the address of each of these strings is unique modulo the max number of keys. However if the addresses happened not to be unique in the low bits, you'd have a collision. In any case this is undefined behavior and also completely illegal C if a prototype exists (since the argument type will be wrong). – R.. GitHub STOP HELPING ICE Sep 09 '11 at 12:40
  • @R the fact that the functions do not take take strings is clearly stated at the bottom of the answer. – Emil Romanus Sep 09 '11 at 12:46
  • @R I suppose it was you who downvoted my answer as well, maybe you should learn how to read before you downvote and complain. – Emil Romanus Sep 09 '11 at 12:48
  • Fair enough, but why not just `s/"stdin"/stdin_key/` etc. If you make an edit I'll remove the downvote but it's locked in until there's an edit. This is SO's policy not my decision; even with tons of rep I can't override it (well I could edit your answer myself but I generally consider that inappropriate..). – R.. GitHub STOP HELPING ICE Sep 09 '11 at 12:50
  • I thought introducing the keys would add too much complexity for him to decide if he even wants to go down that path, the purpose of my answer is to only show that it IS possible. The real problem is not how to do it but probably the design of his program since he needs this in the first place. – Emil Romanus Sep 09 '11 at 12:53
  • I've edited the question now to remove the confusion, thanks for the suggestion. – Emil Romanus Sep 09 '11 at 13:00
  • So, is the above method a good one? You say you dont recommend it. That is why. Is there a better approach? – user489152 Sep 14 '11 at 08:10
  • @user489152 Except for the fact that `stderr` and `stdout` are re `#defined`, it's a decent solution. The OP's requirement was to have a different log file per thread, but still use `printf` and `stderr` and `stdout` in his code. As I mentioned earlier this is a flaw in the design of his code and "in the real world" you would never do it that way. But yes, thread specific storage in itself is of course a good way of storing one file descriptor per thread. To clarify, it's the intrusion of the identifiers that is what's wrong. – Emil Romanus Sep 14 '11 at 08:22
  • @Emil: Thanks.I have a multithreadded application where I want to log all useful printfs messages to a logger thread, so that I can debug it. I can also clearly see the sequence of the operations of the threads. I was wondering if the above method can be used. – user489152 Sep 14 '11 at 13:03
  • @Emil: Of course I do not want to redefine stdout or stderr. I just need to be sure my threads "exclusively" access the log files. – user489152 Sep 14 '11 at 13:05
5

I don't think this is possible directly. stdin/stdout are global variables, shared between threads, and so are file descriptors.

You'll have to create your own files, and change the printf into fprintf.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
1

stdout and stderr are unique streams, by definition. Imagine, how would shell redirect your streams if there were multiple stdouts? You would need to create your own output streams/file variables and use them instead. Is there a problem with that?

You might find it useful to use thread-specific storage.


You cannot make a process output something on a different terminal. The process doesn't know about terminal, it just outputs the stream, and the terminal picks it up and displays.

What you can however do is to direct output from one of the streams into a file, and run tail -f <filename> in a different terminal.

Vlad
  • 35,022
  • 6
  • 77
  • 199
1

You will have to keep track of what FILE* / fd to use for each thread and use fprintf etc. There's no other way.

For multiple terminals, you need to open each terminal in your program. There's no way to automatically figure out which one to open. Run /bin/tty in the shell of the terminal you want to open, and then open that terminal in your program.

An alternative method would be to have a AF_UNIX socket listening for connections. Then you write a separate program which connects to that socket. You could run that program in the terminal where you wish output to appear.

Per Johansson
  • 6,697
  • 27
  • 34
0

I use a fork() inside the thread for redirect the stdout of the forked process while the "true" thread is in waitpid(). The problem is how to pass the file where you want to redirect stdout. I use a global thread pool, and the thread will find itself through pthread_equal(pthread_self(),iterator), then in the global thread pool structure there is the outfile where the program should redirect the stdout. In my case I create a tmpnam() and write it to the thread struct, but you can use it how you wish.

Here is some example code: (written on the fly)

pthread_t *t_cur=NULL;
int i,pid,newout;
char *outfile=NULL;

for(i=0;i<MAX_THREADS;i++)
  if(pthread_equal(pthread_self(),globals.tpool[i]->thread))
    break;
if(i==MAX_THREADS)
{
   printf("cannot find myself into global threads pool.\n");
   pthread_exit(&i);
 }
if(globals.tpool[i]->outfile == NULL) //  redirect stdout only if outfile is not set ( this is specfic for my purposes )
{
  outfile = globals.tpool[i]->outfile = malloc(L_tmpnam*sizeof(char));
  tmpnam(outfile);
}

if((pid = fork()) == 0)
{
   if(outfile!=NULL)
   {
     newout = open(outfile,O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
     dup2(newout,STDOUT_FILENO);
     close(newout);
   }
   /* your code here */
}
else
  waitpid(pid,NULL);
pthread_exit(&i);

I really wrote it on the fly, I haven't tested this code, so take care to fix any errors. I didn't post my real code because of calls to my own library. Here I didn't check the return values from tmpnam(), fork(), open() and malloc(), which you should do.

tux_mind
  • 306
  • 1
  • 5
  • 15
  • A `fork()` is a separate process, though... Also it may return -1 which you probably shouldn't pass down to `waitpid()`. – Alexis Wilke Jan 14 '18 at 23:18