[ home ] [ math / cs / ai / phy / as / chem / bio / geo ] [ civ / aero / mech / ee / hdl / os / dev / web / app / sys / net / sec ] [ med / fin / psy / soc / his / lit / lin / phi / arch ] [ off / vg / jp / 2hu / tc / ts / adv / hr / meta / tex ] [ chat ] [ wiki ]

Viewing source code

The following is the source code for post >>>/os/4

\textbf{Mutual exclusion problem}

Assume two threads want to copy a bulk of data into the same buffer. For example, thread A does \`memset(buffer, 'a', length)\` while thread B does \`memset(buffer, 'b', length)\`. We'll assume length is arbitrary but smaller than the buffer length  (\`int length = (rand() % sizeof(buffer));\`).

Because copying something is rather slow, multiple threads can concurrently write into buffer and overwrite each other's data. If there were also threads reading from the buffer, they would be utterly confused how to interpret the data because it's a jumbled mess that's been overwritten multiple times by different threads and in different places.

Thus, writing to and reading from a shared buffer should be atomic: only one thread should be able to have access to the buffer during its operations with it. Using semaphores, this is done by creating a mutex semaphore initialized to 1, \`mutex = 1\`. A mutex can be obtained (locked) and released (unlocked) by attempting to decrement or increment the mutex semaphore respectively.

The first thread to "lock" the mutex - by decrementing it from 1 to 0 - will continue on using the buffer, and since the value of the semaphore is now zero every other thread that attempts to obtain the mutex will be blocked. Once the thread releases the mutex, other threads have the chaince to obtain it. If a thread never releases a mutex in its possession, other threads will remain permanently blocked. If a thread locks its own mutex twice, it'll block itself and every thread will be in a permanent deadlock.


\textbf{Implementation}:

Initialization:
```
sem_t mutex;
int main() {
    sem_init(&mutex, 0, 1);
    ...
    sem_destroy(&mutex);
}
```
Thread A:
```
void* a(void* data) {
    for ( ; ; ) {
        sem_wait(&mutex);
        memset(buffer, 'a', sizeof(buffer));
        sem_post(&mutex);
    }
    return NULL;
} 
```
Thread B:
```
void* b(void* data) {
    for ( ; ; ) {
        sem_wait(&mutex);
        memset(buffer, 'b', sizeof(buffer));
        sem_post(&mutex);
    }
    return NULL;
} 
```

Full code:
```

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

#define RED(s) ("\e[7m\e[31m" s "\e[0m")
#define GREEN(s) ("\e[32m" s "\e[0m")
#define SIZE 1024

char buffer[SIZE * BUFSIZ];

sem_t mutex;
void* a(void* data)
{
    for ( ; ; ) {
        assert(sem_wait(&mutex) == 0);
        printf(RED("(---"));
        memset(buffer, 'a', (rand() % sizeof(buffer)));
        printf(RED("---)"));
        assert(sem_post(&mutex) == 0);

        usleep(1);
    }
    return NULL;
}

void* b(void* data)
{
    for ( ; ; ) {
        assert(sem_wait(&mutex) == 0);
        printf(GREEN("(---"));
        memset(buffer, 'b', (rand() % sizeof(buffer)));
        printf(GREEN("---)"));
        assert(sem_post(&mutex) == 0);

        usleep(1);
    }
    return NULL;
}

int main()
{
    assert(sem_init(&mutex, 0, 1) == 0);
    pthread_t ta, tb;
    assert(pthread_create(&ta, NULL, a, NULL) == 0);
    assert(pthread_create(&tb, NULL, b, NULL) == 0);
    assert(pthread_join(ta, NULL) == 0);
    assert(pthread_join(tb, NULL) == 0);
    assert(sem_destroy(&mutex) == 0);
    return 0;
}
```