5

I write a program using pthread.

Environment:windows 7 , CYGWIN_NT-6.1 i686 Cygwin , gcc (GCC) 4.5.3

The source code

#include<stdio.h>
#include<pthread.h>

void *th_func(void *p)
{
    int iLoop = 0;

    for(iLoop = 0;iLoop<100;iLoop++)
    {
        printf("Thread Thread Thread Thread\n");
    }

    return;
}

int main()
{
    int iLoop = 0;
    pthread_t QueThread;

    printf("Main : Start Main\n");

    printf("Main : Start Create Thread\n");
    pthread_create(&QueThread,NULL,th_func,NULL);
    printf("Main : End Create Thread\n");

    for(iLoop = 0;iLoop<100;iLoop++)
    {
        printf("Main Main Main Main\n");
    }

    pthread_join(QueThread,NULL);

    printf("Main : End Main\n");

    printf("---------------\n");

    return 0;
}

When I compile the source code, there are no warnings or errors,but it's output is weird.

A part of it's output

Main : Start Main
Main : Start Create Thread
Thread Thread Thread ThreThread Thread Thread Thread
Main Main Main Main
Thread Thread Thread Thread
Main Main Main Main

I want to know the cause of such phenomenon.

In this output, Main : End Create Thread is not printed completely. And at line 3, a newline \n at the end of "Thread Thread Thread Thread\n" disappear.

Is everyone's output like this? It does not occur every time, but occurs sometime.

If I use mutex to call printf safely,the weird output seem to be stopped.

POSIX says printf is thread-safe, and according to Cygwin.com, cygwin provides posix-style API. However, there is the unexpected output.

Is printf really thread-safe?

I executed the same program 100 times in Linux(Ubuntu), and this output did not occur.

In addition, I have not understood the reason why some words on the output disappeared.

taskinoor
  • 45,586
  • 12
  • 116
  • 142
Takashi Ikejima
  • 61
  • 1
  • 1
  • 4

5 Answers5

4

This looks like it may be a bug in Cygwin, or maybe something is misconfigured. Several answer here indicate that 'thread safe' only promises that the function won't cause harm to the program, and that thread safety doesn't necessarily mean that a function is 'atomic'. But, as far as I know, POSIX doesn't formally define 'thread safe' (if anyone has a pointer to such a definition, please post it in a comment).

However, not only does POSIX specify that printf() is thread safe, POSIX also specifies that:

All functions that reference ( FILE *) objects shall behave as if they use flockfile() and funlockfile() internally to obtain ownership of these ( FILE *) objects.

Since printf() implicitly references the stdout FILE* object, all printf() calls should be atomic with respect to each other (and any other function that uses stdout).

Note that this might not be true on other systems, but in my experience it does hold true for many multi threaded systems.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • The definition of "thread-safe" is in [section 3.399](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_399). Regarding the file lock, since `printf()` is a sequence of `putc()` calls, only the latter actually references the `FILE*` and needs to get ownership. – Barmar Nov 02 '12 at 07:21
  • Thanks for the pointer to the 'thread safe' definition (but I must say I think it leaves a lot to be desired). I also don't think that's the correct interpretation of referencing a `(FILE*)` object. I know it's just an appeal to authority, but here's a post by David Butenhof (author of "Programming with POSIX Threads") where he states that `printf()` must be atomic: http://newsgroups.derkeiler.com/Archive/Comp/comp.programming.threads/2009-06/msg00058.html – Michael Burr Nov 02 '12 at 07:46
  • I have begun to think that it's bug in Cygwin.When I executed the program in Linux,the weird output did not occur. – Takashi Ikejima Nov 04 '12 at 09:05
  • Like I said, my reading of the standard is that `printf()` must take the lock on the `stdout` `FILE*` object. Others seem to disagree and believe that only the character I/O functions need to take the lock. If glibc `printf()` takes the lock, that's not necessarily evidence that POSIX *requires* that behavior. However, It seems to make little sense to me that POSIX wouldn't require it. I'd argue that you'd have to read the standard specifically looking for a loophole to not read it as requiring `printf()` to take the lock. – Michael Burr Nov 04 '12 at 09:21
  • Do you mean I read the standard with a bias that printf() always ensures atomicity for all systems? Now I think so,too. I should have read the standard carefully. I'm sorry,I'm still beginner in English,so it's hard to understand your sentence. – Takashi Ikejima Nov 05 '12 at 04:35
  • @TakashiIkejima: I think that you and I are in agreement that `printf()` should be 'atomic'. However, several other answers have a different opinion. – Michael Burr Nov 05 '12 at 05:06
  • Yes,I wish printf() was atomic.As you say,there are some opinion.Finally I should read standard or source code. – Takashi Ikejima Nov 08 '12 at 06:57
4

The POSIX standard has functions like putc_unlocked() where the commentary says:

Versions of the functions getc(), getchar(), putc(), and putchar() respectively named getc_unlocked(), getchar_unlocked(), putc_unlocked(), and putchar_unlocked() shall be provided which are functionally equivalent to the original versions, with the exception that they are not required to be implemented in a thread-safe manner. They may only safely be used within a scope protected by flockfile() (or ftrylockfile()) and funlockfile(). These functions may safely be used in a multi-threaded program if and only if they are called while the invoking thread owns the (FILE *) object, as is the case after a successful call to the flockfile() or ftrylockfile() functions.

That clearly indicates that the low-level functions for single character I/O are normally thread-safe. However, it also indicates that the level of granularity is a single character output operation. The specification for printf() says:

Characters generated by fprintf() and printf() are printed as if fputc() had been called.

And for putc(), it says:

The putc() function shall be equivalent to fputc(), except that if it is implemented as a macro it may evaluate stream more than once, so the argument should never be an expression with side-effects.

The page for fputc() doesn't say anything about thread-safety, so you have to look elsewhere for that information.

Another section describes threads and says:

All functions defined by this volume of POSIX.1-2008 shall be thread-safe, except that the following functions need not be thread-safe.

And the list following includes the *_unlocked() functions.

So, printf() and fputc() have to be thread-safe, but the writing by printf() is done 'as if' by fputc(), so the interleaving of output between threads may be at the character level, which is more or less consistent with what you see. If you want to make calls to printf() non-interleaved, you would need to use the flockfile() and funlockfile() calls to give your thread ownership of stdout while the printf() is executed. Similarly for fprintf(). You could write an fprintf_locked() function quite easily to achieve this result:

int fprintf_locked(FILE *fp, const char *format, ...)
{
    flockfile(fp);
    va_list args;
    va_start(args, format);
    int rc = vfprintf(fp, format, args);
    va_end(args);
    funlockfile(fp);
    return rc;
}

You could insert a fflush(fp) in there if you wished. You could also have a vfprintf_locked() and have the function above call that to do the lock, format, (flush) and unlock operations. It's probably how I'd code it, trusting the compiler to inline the code if that was appropriate and doable. Supporting the versions using stdout is likewise pretty straight-forward.

Note the fragment of POSIX specification for flockfile() quoted by Michael Burr in his answer:

All functions that reference (FILE *) objects, except those with names ending in _unlocked, shall behave as if they use flockfile() and funlockfile() internally to obtain ownership of these (FILE *) objects.

Apart from the odd parentheses around the FILE *, these lines impact all the other standard I/O functions, but you have to know that these lines exist in one of the less frequently used man pages. Thus, my fprintf_locked() function should be unnecessary. If you find an aberrant implementation of fprintf() that does not lock the file, then the fprintf_locked() function could be used instead, but it should only be done under protest — the library should be doing that for you anyway.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I have not understood your answer completely yet,but I have gotten to grab what you say to me.In short,you say that the string printed by multi threads using printf() can be mixed at character level,don't you? Thank you for taking your time to search much information to answer my question! – Takashi Ikejima Nov 08 '12 at 06:27
  • Yes, I'm saying that the strings printed by `printf()` could be interleaved by different threads at the character level. It was interesting trying to pull together all the pieces; I'm not sure it's quite as rock solid as I'd like, but I think the trail is good enough — unless someone comes up with an alternative derivation and a different conclusion. – Jonathan Leffler Nov 08 '12 at 08:16
0

Just because a function is thread-safe, it doesn't mean it's atomic.

In your case, if you want to ensure that your output don't get interleaved, you need to use a mutex to ensure that only one thread calls printf at a time.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Year,a mutex prevent an output from getting interleaved. When I compile and execute the program in Linux,the output doesn't occur.. – Takashi Ikejima Nov 03 '12 at 14:17
0

Threads behave like this for a reason. If threads were executed one after another and not 'at the same time' (in an interleaved manner), there would be no point in this kind of 'concurrency'. When you use mutexes, the threads will be blocked according to your intention, and they generate the expected output.

Also, you write return; in a function that returns void * and that is undefined behavior, so anything can happen when running your program.

  • I have not known return should not be written in void function! Thanks! I delete return; ,but the same phenomenon occurs. Does your first sentence means it is not the same between interleaving and concurrency? – Takashi Ikejima Nov 04 '12 at 09:18
0

I will put this in a simple way you have two threads which are trying to access a resource. And also there is no kinds priority checks or anything like a mutex. Theoretically, threads without mutex or priority gets assigned with resources randomly. try creating two threads with one thread printing yes and the other one printing no. you will find this unusual behavior. Also remember running time is different for different threads in this case. If you try the same stuff with one thread writing the info to a file and other guy writing to console. You will not encounter such an issue. Hope that helps....

m4n1c
  • 127
  • 8