Search code examples
cshared-memorymemory-mapped-files

Named Shared Memory Issue in C


I'm writing a program that finds the prime numbers up to 4294967295 (unsigned int) using 5 threads. Each thread gets a range of numbers to check using the IsPrime() function (I removed the function from the code since it's not where the code breaks), and if the number is prime, it will put the number in a named shared memory file with the size of 500 MB. To make sure the program works, I started with small range of numbers: 0 to 25. So each thread gets 5 numbers to check and to put it in the mapped file. Here's the code:

#define _BSD_SOURCE

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <unistd.h>
#include <pthread.h>

/* The program is using 5 threads to find prime numbers.
therefore, each thread is responsible for finding the numbers
with in a specific range: unsigned int / 5
*/

#define THREAD_1_BEGIN 0
#define THREAD_1_END 5

#define THREAD_2_BEGIN 6
#define THREAD_2_END 10

#define THREAD_3_BEGIN 11
#define THREAD_3_END 15

#define THREAD_4_BEGIN 16
#define THREAD_4_END 20

#define THREAD_5_BEGIN 21
#define THREAD_5_END 25



// The shared variable to hold the total number of primes.
unsigned int totalPrimes = 0;

// Shared memory variables
unsigned char *bitmap;
unsigned int bitmap_size = 4294967295 / 8 + 1; // size of unsigned int
unsigned int *prime_numbers;
unsigned int object_size = 1024 * 1024 * 500; // 500 MB
void *addr;

// Threads
pthread_t threads[5];
pthread_mutex_t total_Mutex;

void *ThreadWorker(void *threadId);
int IsPrime(unsigned int number);
void *mount_shmem(char *path, unsigned int object_size); 

int main(int argc, char **argv)
{
  int i;
  void *status;

  // Set the threads attributes and mutex
  pthread_attr_t attr;

  pthread_mutex_init(&total_Mutex, NULL);

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

  // Set the shared memory
  addr = mount_shmem("threadedshm", object_size);
  bitmap = addr;

  prime_numbers = (unsigned int*)(bitmap + bitmap_size);

  for(i = 0; i<5; i++){
    pthread_create(&threads[i], &attr, ThreadWorker, (void *)i);
  }

  pthread_attr_destroy(&attr);

  // Wait for each thread
  for(i = 0; i<5; i++){
    pthread_join(threads[i], &status);
  }

  printf("\n TOTAL: %u \n", totalPrimes);

  pthread_mutex_destroy(&total_Mutex);
  pthread_exit(NULL);

}

void *ThreadWorker(void *threadId)
{
  int pos = (int) threadId;
  unsigned int t1;
  unsigned int t2;
  unsigned int t3;
  unsigned int t4;
  unsigned int t5;

  if(pos == 0){
    for(t1 = THREAD_1_BEGIN; t1 <= THREAD_1_END; t1++){
      if(IsPrime(t1)){
        printf("Thread 1: %u\n", t1);

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t1;

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
      }
    } 
  }else if(pos == 1){
    for(t2 = THREAD_2_BEGIN; t2 <= THREAD_2_END; t2++){
      if(IsPrime(t2)){
        printf("Thread 2: %u\n", t2);

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t2;

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
      }
    } 
  }else if(pos == 2){
    for(t3 = THREAD_3_BEGIN; t3 <= THREAD_3_END; t3++){
      if(IsPrime(t3)){
        printf("Thread 3: %u\n", t3);

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t3;

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
      }
    } 
  }else if(pos == 3){
    for(t4 = THREAD_4_BEGIN; t4 <= THREAD_4_END; t4++){
      if(IsPrime(t4)){
        printf("Thread 4: %u\n", t4);

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t4;

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
        printf("Thread 4: %u\n", t4);

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t4;

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
      }
    } 
  }else if(pos == 4){
    for(t5 = THREAD_5_BEGIN; t5 <= THREAD_5_END; t5++){
      if(IsPrime(t5)){
        printf("Thread 5: %u\n", t5);

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t5;

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
      }
    } 
  }

  pthread_exit((void*) 0);
}

void *mount_shmem(char *path, unsigned int object_size){
    int shmem_fd;
    void *addr;

    // create and resize the file
    shmem_fd = shm_open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (shmem_fd == -1){
        fprintf(stdout, "failed to open shared memory object\n");
        exit(EXIT_FAILURE);
    }
    // resize it to 500 MB
    if (ftruncate(shmem_fd, object_size) == -1){
        fprintf(stdout, "failed to resize shared memory object\n");
        exit(EXIT_FAILURE);     
    }

    addr = mmap(NULL, object_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmem_fd, 0);
    if (addr == MAP_FAILED){
        fprintf(stdout, "failed to map shared memory object\n");
        exit(EXIT_FAILURE);
    }

    return addr;
}

However, at the point of insertion of the number to the array (prime_numbers[totalPrimes] = t1;) I'm getting a segmentation fault error. I can't figure out what I'm doing wrong when initiating the shared memory file. I tried it with using only one thread, because I suspected that it might be a mutual exclusion issue where several threads try to update the file at the same time, but it wasn't the issue (I know I'm eventually going to need a mutex or semaphore). Any help would be appreciated.


Solution

  • This line:

    prime_numbers = (unsigned int*)(bitmap + bitmap_size);
    

    sets prime_numbers to point well beyond the extent of the shared memory region pointed to by bitmap (this is the cause of your crash).

    You also have a race condition, in the code like this:

        // put it in the shared memory file
        prime_numbers[totalPrimes] = t1;
    
        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
    

    Since you read totalPrimes outside the lock to determine the array index to write to, two threads can write to the same array index, leading to a lost update. You can fix this by fetching the array index to write to when you perform the increment:

        // Increment the counter by 1
        pthread_mutex_lock(&total_Mutex);
        thisPrime = totalPrimes++;
        pthread_mutex_unlock(&total_Mutex);
    
        // put it in the shared memory file
        prime_numbers[thisPrime] = t1;