0

The following code can be found in the book "Pthreads programming" (O'Reilly media):

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

#define TCOUNT 10
#define WATCH_COUNT 12

int count = 0;
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_threshold_cv = PTHREAD_COND_INITIALIZER;
int thread_ids[3] = {0,1,2};

void watch_count(int *idp);
void inc_count(int *idp);

extern int
main(void)
{
    pthread_t threads[3];
    pthread_create(&threads[0], NULL, inc_count,   &thread_ids[0]);
    pthread_create(&threads[1], NULL, inc_count,   &thread_ids[1]);
    pthread_create(&threads[2], NULL, watch_count, &thread_ids[2]);

    for(int i=0; i<3; i++)
        pthread_join(threads[i],NULL);

    return 0;
}

void watch_count(int *idp)
{
    pthread_mutex_lock(&count_mutex);
    printf("Waiting condition\n");
    while(count <= WATCH_COUNT)
    {
        pthread_cond_wait(&count_threshold_cv,&count_mutex);
        printf("watch_count(): Thread %d, Count is %d\n", *idp, count);
    }
    pthread_mutex_unlock(&count_mutex);
}

void inc_count(int *idp)
{
    for(int i=0; i<TCOUNT; i++)
    {
        pthread_mutex_lock(&count_mutex);
        count++;
        printf("inc_counter(): Thread %d, old count %d, new count %d\n", *idp, count-1, count);
        if(count == WATCH_COUNT)
        {
            printf("release\n");
            pthread_cond_signal(&count_threshold_cv);
        }    

        pthread_mutex_unlock(&count_mutex);
    }
}

The intended behavior was to create 2 threads that would increment the same global variable "count" up to 20, when it reached 12 it was suposed to release the condition and to reenter the watch_counter thread, but for some reason it does not go right away to it.

The output is the following

inc_counter(): Thread 0, old count 0, new count 1
inc_counter(): Thread 1, old count 1, new count 2
Waiting condition
inc_counter(): Thread 0, old count 2, new count 3
inc_counter(): Thread 1, old count 3, new count 4
inc_counter(): Thread 0, old count 4, new count 5
inc_counter(): Thread 1, old count 5, new count 6
inc_counter(): Thread 0, old count 6, new count 7
inc_counter(): Thread 1, old count 7, new count 8
inc_counter(): Thread 0, old count 8, new count 9
inc_counter(): Thread 1, old count 9, new count 10
inc_counter(): Thread 0, old count 10, new count 11
inc_counter(): Thread 1, old count 11, new count 12
releasing
inc_counter(): Thread 0, old count 12, new count 13
inc_counter(): Thread 1, old count 13, new count 14
watch_count(): Thread 2, Count is 14
inc_counter(): Thread 0, old count 14, new count 15
inc_counter(): Thread 1, old count 15, new count 16
inc_counter(): Thread 0, old count 16, new count 17
inc_counter(): Thread 1, old count 17, new count 18
inc_counter(): Thread 0, old count 18, new count 19
inc_counter(): Thread 1, old count 19, new count 20

Does anyone knows why is this happening?

1 Answers1

1

Threads can execute in any order unless you compel some particular order.

The only order you compel is that watch_count can't make forward progress while count is less than or equal to WATCH_COUNT. That clearly doesn't happen.

If you need to enforce any other ordering, you need to write code to do that. Otherwise, threads can execute in any order. A good implementation won't switch threads significantly more often than it has to, and you probably have a good implementation.

Your expectation that it will go "right away" to it doesn't seem to be based on anything. The other threads are already running and don't wait, so why shouldn't they continue to make forward progress? The two inc_count threads already running and they never wait for anything.

The pthread_cond_signal function causes one thread that is currently waiting on the condition variable to stop waiting on the condition variable. It still cannot make forward progress until it acquires the mutex since it needs the mutex to check if can exit the while condition. There are two other threads contending for that mutex and no guarantee it will win.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278