简体   繁体   中英

Posix pthread worker that is only running when needed

I have a worker thread doing a polling activity (it's targeting libcurl but that is not relevant).

Many actions from the application can start a worker thread but if it is already running, no new thread should be created. A new request from the application is simply joined with any other polling already ongoing.

In summary, a single worker thread that exits when there is nothing left to do.

The code is:

pthread_t thread = NULL;
struct timespec sleep_val = {0, 20000000};
void *worker_thread(void *threadid)
{
    do {
        poll();
        nanosleep(&sleep_val, NULL);
    } while (no_of_handles_running > 0);
    pthread_exit(NULL);
    thread = NULL;
}

void start_worker_thread_if_needed() {
    if (thread == NULL) {
        pthread_create(&thread, NULL, worker_thread, NULL);
    }
}

My doubt is about thread safety. The function start_worker_thread_if_needed() can be called at any time any number of times.

So, what if start_worker_thread_if_needed() is called exactly when we exit the while loop and therefor interrupts the worker thread. If that happens the condition if (thread == NULL) will be FALSE since the worker thread is interrupted and pthread_exit + thread = NULL has not yet completed.

So now start_worker_thread_if_needed() will exit without creating a new thread but as soon as control is returned to the old worker thread it will continue to the pthread_exit line and the worker thread is destroyed.

The issue will be that the request that was just made triggering start_worker_thread_if_needed() will be lost and the polling will not start until the next call to start_worker_thread_if_needed().

Edit: Here is a suggestion with mutex but I still have the same doubt. What happens if the main thread interrupts just after we exit the while loop and before the worker can take the mutex. Then the main thread does not create a new thread and then the worker exit.

void *worker_thread(void *threadid)
{
    do {
        poll();
        nanosleep(&sleep_val, NULL);
    } while (no_of_handles_running > 0);
    pthread_mutex_lock(&my_mutex);
    thread = NULL;
    pthread_mutex_unlock(&my_mutex);
    pthread_exit(NULL);
}

void start_worker_thread_if_needed() {
    pthread_mutex_lock(&my_mutex);
    if (thread == NULL) {
        pthread_create(&thread, NULL, worker_thread, NULL);
    }
    pthread_mutex_unlock(&my_mutex);
}

I think I have a flaw in how I have set this up and it would be highly appreciated if someone could help out with the proper solution.

Thanks to for all the help in the comments above. Kaylums suggestion to use condition variables seems to be a good approach to not risk ending up with the corner case I was concerned about.

The final code (with logs removed) is the one below and it appears to work just fine.

static int no_of_handles_running;
static int RUN_THREAD = 0;
pthread_t thread = NULL;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv  = PTHREAD_COND_INITIALIZER;;

void *worker_thread(void *threadid)
{
    RUN_THREAD = 1;
    do {
        poll();  //Will update no_of_handles_running
        if (no_of_handles_running == 0) {
            pthread_mutex_lock(&mutex);
            pthread_cond_wait(&cv, &mutex);
            pthread_mutex_unlock(&mutex);
        }
    } while (RUN_THREAD);
    thread = NULL;
    pthread_exit(NULL);

}
void start_worker_thread() {
    if (thread == NULL) {
        int rc = pthread_create(&thread, NULL, worker_thread, NULL);
        if (rc){
            return;
        } 
    }
    pthread_mutex_lock(&mutex);
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&mutex);
}

void stop_worker_thread() {
    RUN_THREAD = 0;
    pthread_mutex_lock(&mutex);
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&mutex);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM