10

Following is a program which uses pthreads.

#include <pthread.h> // posix threads 
#include <stdio.h>
#include <stdlib.h>

/* to compile use -lpthread */

void * sample_thread(void *);

#define MAX 10

int main() 
{
  pthread_t tid; 
  pthread_attr_t attr; 
  int k;  

  pthread_attr_init(&attr); // set default attributes 
  pthread_create(&tid, &attr, sample_thread, NULL); // create new thread
  // sample_thread will run as the new thread 

  for(k=0; k<MAX; k++) { 
    printf("Hi I'am %s %d \n",__func__,k); 
  }


  //this would kill all the threads,
}

void * sample_thread(void * p)
{ 
  int k; 
  for(k=0; k<MAX; k++) { 
    printf("Hi I'am %s %d \n",__func__,k); 
  }

}

Each time when I run the program I am expecting to get different number of execution numbers from the main thread and the child thread (because the main thread might exit before the child). I am getting this expected output sometimes. But I got an output as follows, which I am unable to understand why.

Hi I'am main 0 
Hi I'am main 1 
Hi I'am main 2 
Hi I'am main 3 
Hi I'am main 4 
Hi I'am main 5 
Hi I'am main 6 
Hi I'am main 7 
Hi I'am main 8 
Hi I'am main 9 
Hi I'am sample_thread 0 
Hi I'am sample_thread 0 
Hi I'am sample_thread 1 
Hi I'am sample_thread 2 
Hi I'am sample_thread 3 
Hi I'am sample_thread 4 
Hi I'am sample_thread 4 
Hi I'am sample_thread 5 

Why did the sample thread 0 and 4 print twice?

alk
  • 69,737
  • 10
  • 105
  • 255
DesirePRG
  • 6,122
  • 15
  • 69
  • 114
  • 1
    Can you tell us what OS you're using? There does not seem to be anything wrong with your program and I suspect it might be a bug in the implementation (`exit` is required by POSIX to synchronize with stdio operations, but if it failed to do so, there could be data races (and random corruption) when it flushes `stdout` simultaneously with another thread writing to `stdout`). – R.. GitHub STOP HELPING ICE Oct 06 '14 at 17:20

1 Answers1

11

As highlighted by @R.. in the comments, this appears to be a bug in the implementation of glibc (assuming you are using Linux -- I can reproduce this on Linux 2.17 compiled with GCC 4.9.1), in that exit() doesn't ensure, while flushing and closing streams, there's no race when it's called by one thread when multiple threads use stdout.

The following from flockfile manual clearly indicates that the behaviour observed is not correct:

The stdio functions are thread-safe. This is achieved by assigning to each FILE object a lockcount and (if the lockcount is nonzero) an owning thread. For each library call, these functions wait until the FILE object is no longer locked by a different thread, then lock it, do the requested I/O, and unlock the object again.

In light of this, the following options can be considered as a workaround (as there's no response to the bug report) to this particular case that we observed here.


Both the threads "share" the stdout stream and I think, the "extra" output is printed because of the premature exit of main thread.

printf buffers (in sample_thread()) the output and before it could clear it's buffer (due to \n in printfs), main thread exits. Hence forcing the flush of stdout buffer when the process exits.

To fix,

1) you could call setbuf() in main() before creating the thread:

setbuf(stdout, NULL);

to not buffer stdout at all.

Or
2) call pthread_exit() in both threads so that the process continues if either thread dies.

Or
3) call pthread_join() in main thread so that main thread waits for the completion of sample_thread thread.

Either one of these will avoid the issue.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • about the first option. where exactly should i call setbuf function? – DesirePRG Oct 06 '14 at 07:59
  • 1
    about the second option isn't calling pthread_exit only in the main function enough? – DesirePRG Oct 06 '14 at 08:07
  • 1
    1. Place `setbuf()` before the first usage of the buffer to be set. Here: `stdout` implicitly use by `printf()`. 2. Calling `pthread_exit()` in `main()` is sufficient. – alk Oct 06 '14 at 08:31
  • @DesirePRG In this case, yes. In general, if you want "other" threads to continue, then you may need `pthread_exit()` in many/all threads. As long as, you understand what it does, the choice is yours where to call/not to call, depending on your application. For your 1st question, @alk answered it. – P.P Oct 06 '14 at 08:40
  • @R.. why? what would be the correct answer then? can you give your answer – DesirePRG Oct 06 '14 at 13:03
  • 1
    @BlueMoon: I commented because I believe downvotes should be accompanied by comments that give accountability for who gave them and their reasoning ("It's wrong" and "It's incomplete" and "It's giving harmful advice" are all very different reasons). I'll work on trying to find the cause behind this if I can even reproduce it (I was using my phone at the time of comment). – R.. GitHub STOP HELPING ICE Oct 06 '14 at 17:14
  • 2
    @BlueMoon: I was able to reproduce this on Linux 3.5/glibc 2.17. I suspect it's a manifestation of [bug 14697](https://sourceware.org/bugzilla/show_bug.cgi?id=14697); see my comment on the question for an explanation I why I believe this. IMO your answer would be good with minor changes if it just explained that OP's code is correct and supposed to work as-is, but that it's probably hitting a bug in the system, and that your existing answer is one possible workaround for that. – R.. GitHub STOP HELPING ICE Oct 06 '14 at 17:46
  • @R.. After reading the link, it does appear to be an issue that stdio is not locked properly and the reported to be the cause. I still believe my reasoning was accurate though (despite not identifying the bug). Your point (that it's a bug) is valid since OP uses pthreads and POSIX requires `exit` to be thread-safe. It's interesting to note that C11 doesn't defines it(or lack of) as C11 doesn't say how `exit` should behave when it flushes and closes all the open streams since it has it's own threads and not necessarily have to conform posix behaviour. – P.P Oct 06 '14 at 20:21
  • @BlueMoon: I don't think you should delete it, and if you make some edits to clarify the things we just discussed I'll be happy to remove the downvote and change it to an upvote. – R.. GitHub STOP HELPING ICE Oct 06 '14 at 20:41
  • @BlueMoon: Regarding C11 threads, C11 guarantees that the standard functions do not result in data races unless it documents otherwise for a particular function. C11 doesn't need to specify that locking happens here (whereas POSIX does, because POSIX has explicit locking via `flockfile` where the locking is actually observable); the fact that the behavior is specified makes the double-output (or other weird effects arising from data races) non-conforming. – R.. GitHub STOP HELPING ICE Oct 06 '14 at 20:44
  • @R.. Thanks. I amended the answer. – P.P Oct 06 '14 at 22:41
  • Just for reference: an older instance of this problem: http://stackoverflow.com/questions/13550662/pthread-one-printf-statement-get-printed-twice-in-child-thread I'd agree that the duplicated output is a glibc bug. – Michael Burr May 22 '15 at 22:00