/**
* 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;
}