Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is fprintf thread safe on OS X?

Is fprintf thread safe on OS X? If so, where is this documented?

like image 471
anon Avatar asked Feb 02 '10 22:02

anon


People also ask

Is fprintf thread safe?

The fprintf() of Microsoft's multithreaded runtime library is thread safe.

Is fprintf reentrant?

Our version of these functions, which we call general_printf(), is as portable and versatile as we could make it, and it is completely reentrant.


2 Answers

The POSIX threads specification (AKA Pthreads), which OS X conforms to, requires that stdio functions are thread safe. It also provides the flockfile and funlockfile functions to ensure that other threads can't interleave I/O on a FILE * whilst it is locked.

See http://pubs.opengroup.org/onlinepubs/007908799/xsh/threads.html, specifically under the section titled "Thread-safety".

like image 103
TheJuice Avatar answered Sep 22 '22 23:09

TheJuice


This was a good question, although similar questions have been asked here many times. I was interested in the OSX aspect because I trying to get up to speed on that system myself. (maybe you should add the OSX tag)

I THINK fprintf() is thread safe on OSX. My first reason for this is that the Darwin people were going in that direction as evidenced by their choice to drop the old-school global 'errno' in favor of the function errno(). For documentation, just follow '/usr/include/errno.h'. Without that, none of the libc stuff would be thread safe. Yet the use of the errno() function doesn't prove anything about fprintf(). It's just a start. I'm sure everyone knows of at least one situation where Apple didn't carry through with a good idea.

Another reason I believe in the 'thread-safety' of fprintf() is the source code, which is supposed to be 'the real thing', at least until 10.6 when Apple closed (part / all) of OSX. Scan that code for "MT-Safe" and you will see a CLAIM that the non-locale version of 'vfprintf()' is thread safe. Again, that doesn't prove anything. Yet it is a form of documentation, which you wanted.

My final reason to believe fprintf() is thread-safe was a test case. This doesn't prove much of anything either. Maybe it proves the buffer space is thread safe. OK, it was an excuse to write a little program for fun. Actually, I didn't write it. I found a skeleton online, and modified it. The "FLUSH_BUFFER" definition allows you to more clearly see what's happening. If that macro isn't defined, you get 'sort-of' buffer test (same text without some line-terminators). I couldn't figure out a way to arrange a more meaningful collision of the threads.

I'll guess you might be writng to multiple files. Writing to a single file is probably a better test. The attached program isn't a definitive test. Although it could be extended, I'm not sure any program could really be definitive. Bottom line: maybe you should just MUTEX your calls to fprintf().

// artificial test for thread safety of fprintf()
// define FLUSH_BUFFER to get a good picture of what's happening, un-def for a buffer test
// the 'pretty print' (FLUSH_BUFFER) output relies on a mono-spaced font
// a writeable file name on the command line will send output to that file
//

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

#define FLUSH_BUFFER

#define NTHREAD     5
#define ITERATIONS  3

const char DOTS[] = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . ";
FILE *outFile;

void *PrintHello(void *threadid) {
    long tid;

    tid = (long)threadid;
    for (int i=1; i<=ITERATIONS; i++) {
        long delay = (NTHREAD-tid) * 100000 + (ITERATIONS-i+1) * 10000;
#ifdef FLUSH_BUFFER
        fprintf(outFile, "%*sStart thread  %d iteration %d\n", (tid+1)*4, " ", tid, i);
        usleep(delay);
        fprintf(outFile, "%*sFinish thread %d iteration %d %*.*sw/delay %d\n", 
               (tid+1)*4, " ", tid, i, (NTHREAD-tid+1)*4, (NTHREAD-tid+1)*4, DOTS, delay);
#else
        fprintf(outFile, "Start thread  %d iteration %d   ", tid, i);
        usleep(delay);
        fprintf(outFile, "Finish thread %d iteration %d w/delay %d\n", tid, i, delay);
#endif
    }
    pthread_exit(NULL);
}

int main (int argc, char *argv[]) {
    pthread_t threads[NTHREAD];
    char errStr[100];
    int rc;
    long t;

    if(argc > 1) {
        if(! (outFile = fopen(argv[1], "w"))) {
            perror(argv[1]);
            exit(1);
       }
    } else 
        outFile = stdout;

    for(t=0; t<NTHREAD; t++) {
        fprintf(outFile, "In main: creating thread %ld\n", t);
        if(rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t)) {
            sprintf(errStr, "ERROR; pthread_create() returned %d", rc);
            perror(errStr);
            exit(2);
        }
    }
    pthread_exit(NULL);
}
like image 43
gary Avatar answered Sep 22 '22 23:09

gary