Search code examples
c++multithreadingpthreadsaffinitythread-priority

Pthread freezes stdout?


Let's say there're two threads(pthread) in a C++ program:

  • the main thread
  • the child thread

What the program does is simple:

  1. Bind the two threads to two different cores.
  2. Set the priorities of the two threads to some very high values (-99 for the child thread and -98 for the main thread).
  3. The child thread is doing some heavy task that is using 100% of the CPU.
  4. The main thread is trying the call printf() after the child thread is created.

The problem is that once the child thread is created, it freezes stdout and nothing gets printed on the console anymore. However, when the program exits, all the messages suddenly shown up in the console. Below is a .cpp file demonstrating this effect:

main.cpp:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>

bool EXIT = false;

void signal_handler(int signal){
    EXIT = true;
}

void *child_thread(void *x_args){
    printf("Setting child thread CPU affinity (Core #1)...\n");
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(1, &cpuset);
    if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)){
        perror("Cannot set child thread CPU affinity");
        printf("Exit\n");
        exit(1);
    }

    printf("Locking memory of child thread...\n");
    mlockall(MCL_CURRENT | MCL_FUTURE);

    printf("Setting child thread priority (-99)...\n");
    struct sched_param sched_param;
    sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO)-1;
    if (sched_setscheduler(0, SCHED_FIFO, &sched_param)){
        perror("Cannot set child thread priority");
        printf("Exit\n");
        exit(1);  
    }

    printf("Entering while loop inside child thread...\n");
    while(!EXIT){}
    return NULL;
}

int main(){
    signal(SIGINT, signal_handler);

    pthread_t thread;

    printf("Setting main thread CPU affinity (Core #0)...\n");
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(0, &cpuset);
    if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)){
        perror("Cannot set main thread CPU affinity");
        printf("Exit.\n");
        exit(1);
    }

    printf("Locking memory of main thread...\n");
    mlockall(MCL_CURRENT | MCL_FUTURE);

    printf("Setting main thread priority (-98)...\n");
    struct sched_param sched_param;
    sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO)-2;
    if (sched_setscheduler(0, SCHED_FIFO, &sched_param)){
        perror("Cannot set main thread priority");
        printf("Exit.\n");
        exit(1);
    }

    printf("Creating child thread...\n");
    if (pthread_create(&thread, NULL, child_thread, NULL)){
        perror("Cannot create child thread");
        printf("Exit.\n");
        exit(1);
    }

    printf("Entering while loop in main thread...\n");
    while(!EXIT){
        sleep(1);
        printf("I can't see this until I press Ctrl+C!\n");
    }
    pthread_join(thread, NULL);

    printf("Exit.\n");
    return 0;
}

You can compile it with:

g++ main.cpp -pthread -o main

Then run it with:

sudo ./main

Then you should see stdout freezes after outputing the following:

Setting main thread CPU affinity (Core #0)...
Locking memory of main thread...
Setting main thread priority (-98)...
Creating child thread...
Entering while loop in main thread...
Setting child thread CPU affinity (Core #1)...

Even after an hour you just won't see anymore outputs. But when Ctrl+C is pressed. You will see all the messages coming out:

I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!

The main thread is actually running in the background because if you comment out the two lines inside the while loop (the sleep and printf), you can see that it is also using 100% of CPU. But ht

What am I missing here?


Solution

  • I won't claim to be an expert but you appear to have a single resource, stdout, and two threads attempting to use it. I believe printf is thread safe, but not reentrent. My fist instinct would be to use some kind of thread safe locking around the access to printf and stdout to ensure only one thread is calling it at a time.