Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is fprintf() thread safe?

I'm writing a C solution to some variable of the dining savages problem. Now, I create threads which every thread gets a FILE* to the same debug file; inside the thread I'm doing some printing using fprintf(). The printed statements are not protected by any kind of mutex etc..

I don't observe any interleaving behavior in the debug file; it seems that it is thread safe although nowhere online I found a definitive statement that this is the case.

What I did see is the following:

  1. in Unix the fprintf is thread safe
  2. using the c++11 compiler the fprintf needs to be thread safe

The reason I'm asking that is because this is a university assignment that works, but I still have the doubt that in another windows based computer the program can cause a problem due to the uncertainty discussed above.

I’ll attach the thread code so you will see that the prinintg is not protected:

    DWORD WINAPI RoomateThread(LPVOID lpParam) {
    /*=================================================
    "RoomateThread" this is the roomate thread handler function where
    the thread logic is implemented
    Input:  1. lpParam holds a roomate, runtime and pointers to both files
    Output: 1. return an DWORD value {0}->Success {-1}->Failure
            2. A code telling if the run was successful (debug file)
    A roommate follows this logic after wake up:
        1. If there are clothes available in the closet:
            a. Wait for mutex to be available, take it
            b. Check if the basket is full
                b.1. If it is - start the machine and wait for it to finish
            c. Throw an item in the basket
            d. release mutex
        2. If the closet is empty, wait for laundry_is_empty signal, then goto (1.a)
    =================================================*/
    /*variable declerations*/
    DWORD wait_res, delta;
    BOOL release_res;
    LONG previous_count;
    roomate_thread  *elem;
    elem = (roomate_thread*)lpParam;
    /*thread logic*/
    while (TRUE) {
        /*calculate the delta between the total run time and the time the roomate had run so far*/
        delta = total_time - elem->run_time;
        /*wait until the minimum between period Ti and delta*/
        Sleep(min(elem->roomate->run_time,delta));
        fprintf(elem->debug, "RoomateThread(): Line %d, roomate %d: slept for %d mili sec, starting...\n", __LINE__, elem->roomate->roomate_id, min(elem->roomate->run_time, delta));
        /*as instructed, each roomate is active since the wakeup*/
        fprintf(elem->report, "\nRoomate %d active", elem->roomate->roomate_id);
        /*update the element total run time since start*/
        elem->run_time = elem->run_time + min(elem->roomate->run_time, delta);
        if (time_to_stop < elem->run_time) {
            /*if the element total run time is bigger then the global variable update the global*/
            time_to_stop = elem->run_time;
        }
        /*its time to close the thread properly*/
        if (time_to_stop == total_time) {
            /*if the laundry basket has clothes in it, and the roomate run as much as the total time
            activate rhe robot once more and exit*/
            if (elem->run_time == total_time && items_in_laundry!=0) {
                release_res = ReleaseSemaphore(
                    laundry_is_full,
                    1,
                    &previous_count);
                if (release_res == FALSE) {
                    fprintf(elem->debug, "MachineThread(): Line %d, released semaphore 'laundry_is_full' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                    return FAILURE;
                }
            }
            break;
        }
        /*checks that the roomate has clothes in the closet to continue*/
        if (elem->roomate->clothes_in_laundry < elem->roomate->clothes-1) {/*roomate has clothes available*/
            fprintf(elem->debug, "RoomateThread(): Line %d,  roomate id= %d, number of dirty clothes=%d, number of total dirty clothes=%d\n",__LINE__, elem->roomate->roomate_id, elem->roomate->clothes_in_laundry, items_in_laundry);
        }
        // It's empty:
        else {
            /*waits until one of the roomates will activate the robot, cause there is no clothes in the roomate closet*/
            fprintf(elem->debug, "DAVIDS roomate %d have no clothes, waiting!!!\n", elem->roomate->roomate_id);
            elem->roomate->closet_empty = TRUE;
            /*Wait until the machine is done*/
            wait_res = WaitForSingleObject(laundry_is_empty, INFINITE);
            fprintf(elem->debug, "RoomateThread(): Line %d, roomate %d have clothes,finish waiting!!!\n",__LINE__, elem->roomate->roomate_id);
            if (wait_res != WAIT_OBJECT_0) {
                fprintf(elem->debug, "RoomateThread() error: Line %d, waiting for sempahore 'laundry_is_empty' failed\nthe last error is: 0X%x\n", __LINE__,GetLastError());
                return FAILURE;
            }
            fprintf(elem->debug, "RoomateThread(): Line %d, laundry_is_empty semaphore aquired , roomate: %d\n", __LINE__,elem->roomate->roomate_id);
        }
            /* Wait for mutex (machine start and clothes add "rights")*/
            wait_res = WaitForSingleObject(mutex, INFINITE);
            if (wait_res != WAIT_OBJECT_0) {
                fprintf(elem->debug, "RoomateThread() error: Line %d, waiting for 'mutex' failed\nthe last error is: 0X%x\n", __LINE__,GetLastError());
                return FAILURE;
            }
            fprintf(elem->debug, "RoomateThread(): Line %d, mutex aquired , roomate: %d\n", __LINE__, elem->roomate->roomate_id);
            fprintf(elem->debug, "RoomateThread(): Line 200, mutex aquired , roomate: %d\n",elem->roomate->roomate_id);
            /*Check if basket it full*/
            if (items_in_laundry == total_items) {
            /*Start Machine*/
            release_res = ReleaseSemaphore(
                    laundry_is_full,
                    1,
                    &previous_count);
            if (release_res == FALSE) {
                fprintf(elem->debug, "MachineThread(): Line %d, released semaphore 'laundry_is_empty' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                return FAILURE;
            }
            fprintf(elem->debug, "RoomateThread(): Line 210, released semaphore 'laundry_is_full' last  count is: %ld\n", previous_count);
            /*Wait for it to finish*/
            wait_res = WaitForSingleObject(laundry_is_empty, INFINITE);
            if (wait_res != WAIT_OBJECT_0) {
                fprintf(elem->debug, "RoomateThread() error: Line %d, waiting for sempahore 'laundry_is_empty' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
                return FAILURE;
            }
            items_in_laundry = 0;
        }
        /*Throw in a dirty cloth*/
        elem->roomate->clothes_in_laundry++;
        items_in_laundry++;
        /*Release the mutex*/
        release_res = ReleaseMutex(mutex);
        if (release_res == FALSE) {
            fprintf(elem->debug, "RoomateThread(): Line %d, released 'mutex' failed\nthe last error is: 0X%x\n", __LINE__, GetLastError());
            return FAILURE;
        }
        fprintf(elem->debug, "RoomateThread(): Line %d, mutex released , roomate: %d\n", __LINE__, elem->roomate->roomate_id);
    }
    fprintf(elem->debug, "RoomateThread(): Line %d, thread of roomate %d ended\n", __LINE__, elem->roomate->roomate_id);
    return SUCSSES;
}

just reinforming, this runs on windows using visual 2015

will appreciate some help!!!

**in case you will need some more code i will add, although the rest are not that informative to the question asked

like image 899
David Avatar asked Dec 21 '17 18:12

David


1 Answers

C2011 -- the first version of the standard to acknowledge the existence of threads in the first place -- places no limitation on how fprintf() calls in different threads may or do interact. In that sense, fprintf() is not thread-safe.

POSIX, however, does specify that fprintf() calls from different threads of the same process do not interfere with each other, and if that if they both specify the same target file, their output will not be intermingled. POSIX-conforming fprintf() is thus thread-safe in that sense.

I cannot speak to whether standard C++ places requirements that have the effect of requiring fprintf() to be thread safe. I would find that surprising, but it could be true. To be sure, it is safe to write to an iostream object from multiple threads, but that does not imply that the same is true of fprintf().

But none of that really matters if you're asking about Windows C or C++, however, which (the C in particular) are well known to be non-conforming. If you want to know about Windows's fprintf() in particular, then that has already been answered here (yes).

like image 103
John Bollinger Avatar answered Sep 27 '22 19:09

John Bollinger