1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * Process-based sum using a semaphore for locking.
 */

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

#include <sys/mman.h>

#include <semaphore.h>

#include <assert.h>

void syscall_ok(int result, const char *call) {
    if (result == -1) {
        fprintf(stderr, "Syscall %s failed: ", call);
        perror(NULL);
        exit(1);
    }
}

void sum_between(long n, long m, long *sum, sem_t *sum_lock) {
    for (long i = n; i <= m; ++i) {
        if (i % 101 == 0) {
            sem_wait(sum_lock); // lock
            *sum += i; // looks like a single operation
            sem_post(sum_lock); // unlock

        // What really happens:
        // mov sum, %rdx    // sum = 6              // sum = 6
        // addq i, %rdx     // parent: %rxd = 6     // child: %rdx = 6
        // mov %rdx, sum    //                      // sum = 6 + 11 + 12 + ... 20
        }
    }
}


// Conditions for a data races to occur: 
// At least two processes 
// Access the same memory
// At least one of them writes to the memory

int main(int argc, char **argv) {

    const long TOP = 1000000000;
    // how many workers
    const long NO_WORKERS = 10;
    int chunk = TOP / NO_WORKERS;

    pid_t workers[NO_WORKERS];

    // allocate a shared semaphore
    sem_t *sum_lock= mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    // allocate a shared sum
    long *sum = mmap(0, sizeof(long), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    // initialize our semaphore: start at 1 (unlocked); max value is 1 so only
    // two states (0 and 1)
    assert(sem_init(sum_lock, 1, 1) == 0);

    for (int i = 0; i < NO_WORKERS; ++i) {
        workers[i] = fork();
        syscall_ok(workers[i], "fork");

        if (workers[i] == 0) { // if in child, sum your chunk
            sum_between(i * chunk + 1, (i + 1) * chunk, sum, sum_lock);
            munmap(sum, sizeof(long)); // dealloc the shared sum in this child
            munmap(sum_lock, sizeof(sem_t)); // dealloc the shares semaphore in this child
            exit(0);
        }
    }

    for (int i = 0; i < NO_WORKERS; ++i) {
        waitpid(workers[i], 0, 0);
    }

    printf("sum = %ld\n", *sum);

    // deallocate shared vars in parent
    munmap(sum, sizeof(long));
    munmap(sum_lock, sizeof(sem_t));

    return 0;

}