COMS 4995 Advanced Systems Programming

POSIX threads

Creating threads

Example: bank0.c

#include <stdio.h>
#include <pthread.h> // pthread_create, pthread_join

int balance = 0;

void* deposit(void *arg) {        
    for (int i = 0; i < 1e7; i++) {
        ++balance;
    }
    long r = 10 * (long)arg;
    return (void *)r;
}

void* withdraw(void *arg) {        
    for (int i = 0; i < 1e7; i++) {
        --balance;
    }
    long r = 10 * (long)arg;
    return (void *)r;
}

int main() {
    /* int pthread_create(pthread_t *thread, 
                          const pthread_attr_t *attr,
                          void *(*start_routine)(void *), 
                          void *arg); 
           Returns: 0 if OK, error number on failure */

    pthread_t t1, t2;
    pthread_create(&t1, NULL, &deposit,  (void*)1);        
    pthread_create(&t2, NULL, &withdraw, (void*)2);

    /* int pthread_join(pthread_t thread, void **retval);
           Returns: 0 if OK, error number on failure */

    void *r1;
    void *r2;
    pthread_join(t1, &r1);
    pthread_join(t2, &r2);

    printf("t1 returned %ld\n", (long)r1);
    printf("t2 returned %ld\n", (long)r2);
    printf("balance = %d\n", balance);
}

Processes vs. threads:

Thread synchronization problem:

Mutex

Use a mutex lock to synchronize the threads (bank1.c):

int balance = 0;

pthread_mutex_t balance_lock = PTHREAD_MUTEX_INITIALIZER;

void* deposit(void *arg) {        
    for (int i = 0; i < 1e7; i++) {

        pthread_mutex_lock(&balance_lock);
        ++balance;
        pthread_mutex_unlock(&balance_lock);

    }
    long r = 10 * (long)arg;
    return (void *)r;
}

void* withdraw(void *arg) {        
    for (int i = 0; i < 1e7; i++) {

        pthread_mutex_lock(&balance_lock);
        --balance;
        pthread_mutex_unlock(&balance_lock);

    }
    long r = 10 * (long)arg;
    return (void *)r;
}

POSIX Mutex API:

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                       const pthread_mutexattr_t *restrict attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);

        // Both return: 0 if OK, error number on failure

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

        // All return: 0 if OK, error number on failure

#include <pthread.h>
#include <time.h>

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                            const struct timespec *restrict tsptr);

        // Returns: 0 if OK, error number on failure

Deadlock

Condition variables

POSIX Condition Variables API:

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond,
                      const pthread_condattr_t *restrict attr);

int pthread_cond_destroy(pthread_cond_t *cond);

        // Both return: 0 if OK, error number on failure

int pthread_cond_wait(pthread_cond_t *restrict cond,
                      pthread_mutex_t *restrict mutex);

int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                           pthread_mutex_t *restrict mutex,
                           const struct timespec *restrict tsptr);

        // Both return: 0 if OK, error number on failure

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

        // Both return: 0 if OK, error number on failure

Example from APUE 11.6.6:

#include <pthread.h>

struct msg {
    struct msg *m_next;
    /* ... more stuff here ... */
};

struct msg *workq;

pthread_cond_t qready = PTHREAD_COND_INITIALIZER;

pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;

void process_msg(void)
{
    struct msg *mp;

    for (;;) {
        pthread_mutex_lock(&qlock);

        while (workq == NULL)
            pthread_cond_wait(&qready, &qlock);
        
        mp = workq;
        workq = mp->m_next;
        
        pthread_mutex_unlock(&qlock);
        
        /* now process the message mp */
    }
}

void enqueue_msg(struct msg *mp)
{
    pthread_mutex_lock(&qlock);
    
    mp->m_next = workq;
    workq = mp;
    
    pthread_cond_signal(&qready);
    pthread_mutex_unlock(&qlock);

    // In the textbook, the last two lines are written in reverse order:
    //
    //     pthread_mutex_unlock(&qlock);
    //     pthread_cond_signal(&qready);
    //
    // Taking pthread_cond_signal() call out of the mutex region is
    // allowed (and better) in this particular case, but it is not
    // always safe to do so.
}

Condition Variables in Java:


Last updated: 2024-02-11