Sunday, December 14, 2014

how to prevent output from getting interleaved

If an application only has one process and one thread, that is fine, there will be no competition to print information, which goes to stdout.
But what if there is one process but multiple threads? It is fine to use a single printf to print whatever you want, and that will not get interleaved with information printed by other threads. Below is an example.

void ThreadFn() {
    while (true) {
       printf("This is first info\nThis is second info\n");
       printf("This is third info\n");
   }
}

For the example above, you can always see a first info is followed by a second info, but may not followed by a third info. As far as we use a single printf, we are safe to guarantee that it is in integrity. However, information printed by more that one printf is vulnerable to get interleaved. The reason is in the implementation of printf, when it want to write to stdout, there is a line buffer(of course you can set it to unbuffered mode, but that is unusual). Multiple threads will use the same buffer for the same file, like stdout. To make sure the access to the buffer is thread-safe, it is considered by libraries to be a critical section which is protected by a mutex. Only up to one thread can access the buffer at a time. So the content written by a single printf is copied to the buffer in a single access to the buffer. But for two printf, the thread should apply for the mutex and access buffer more than once, there may be other thread accessing the buffer between two printf.

Then what about multiple processes? There is no synchronization mechanism between multiple processes for printing. When a process calls printf, the content goes to a buffer, when the buffer is full, it is dumped to the terminal/file by write/writev system call. There is no guarantee of the buffer size or how the buffer content will be dumped. To dump the buffer, some libraries may dump constant size at a time, others may dump all at once. So to guarantee integrity of information printed, we'd better call write/writev system call directly when multiple processes is getting involved. Below is an example.

void ProccessFn() {
   ...
   int nwrite = strlen(buf);
   char* p = buf;
   while (nwrite > 0) {
      int written = write(fileno(stdout), p, nwrite);
      if (written == -1) {
          if (errno != EINTR) {
             perror("write");
             break;
          }
      } else {
          nwrite -= written;
          p += written;
      }
   }
}

No comments:

Post a Comment