cprocessshared-memorymmap

shared list among processes in C modifications do not reflect in the list again


I am trying a simple programm that creates multiple processes, each process takes a pair of an int and a bool from a list changes the boolean value and reinserts it if it is found false otherwise just prints it. This thing is done by multiple forked processes and the list stores in shared memory with mmap. Each process does the computation and leaves the loop so no more children are created.

list file:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdbool.h>
#include <string.h>

typedef struct node{
    void* value;
    struct node* prev;
    struct node* next;
}node_t;

typedef struct{
    node_t* head;
    node_t* tail;
    size_t length;
}list_t;





void* shmalloc(size_t length){
    int protection = PROT_READ|PROT_WRITE;
    int visibility = MAP_SHARED|MAP_ANONYMOUS;
    return mmap(NULL, length, protection, visibility, -1, 0);
}

bool empty(list_t* slist){
    return (!slist->head && !slist->tail);
}

void push_back(list_t* slist, void* value, size_t val_len, bool shared){
    node_t* n_node= (shared)? (node_t*)shmalloc(sizeof(node_t)) : (node_t*)malloc(sizeof(node_t));
    n_node->value=(shared)? shmalloc(val_len) : malloc(val_len);
    n_node->next=NULL;
    memcpy(n_node->value, value, val_len);
    if(empty(slist)){
        slist->head=slist->tail=n_node;
        slist->head->prev=NULL;
    }
    else{
        slist->tail->next=n_node;
        n_node->prev=slist->tail;
        slist->tail=n_node;
    }
    slist->length++;
}

void* pop_front(list_t* slist, size_t val_len, bool shared){
    if(!empty(slist)){
        void* val=malloc(val_len);
        memcpy(val, slist->head->value, val_len);
        node_t* to_remove=slist->head;
        if(slist->head==slist->tail){
            slist->head=slist->tail=NULL;
        }
        else{
            slist->head=slist->head->next;
            slist->head->prev=NULL;
        }
        slist->length--;
        if(shared)
            munmap(to_remove, sizeof(to_remove));
        else
            free(to_remove);
        return val;
    }
    return NULL;
}

main file:

#include <stdio.h>
#include <unistd.h>
#include <threads.h>
#include <stdbool.h>
#include <sys/wait.h>
#include "list_process_utils.c"


struct mPair{
    int value;
    bool exists;
};

struct mPair make_pair(int a, bool b){
    struct mPair to_ret ={a, b};
    return to_ret;
}

mtx_t* shared_list_mtx;

void modify_list(list_t* mlist){
    mtx_lock(shared_list_mtx);
    struct mPair* mp = (struct mPair*)pop_front(mlist, sizeof(struct mPair), true);
    mtx_unlock(shared_list_mtx);
    if(!mp->exists){
        printf("process: %d found %d: %d I am reinserting it\n", getpid(), mp->value, mp->exists);
        mp->exists=true;
        mtx_lock(shared_list_mtx);
        push_back(mlist, mp, sizeof(struct mPair), true);
        mtx_unlock(shared_list_mtx);
    }
    else{
        printf("process: %d found %d: %d\n", getpid(), mp->value, mp->exists);
    }
}


int main(){
    list_t* mlist=(list_t*)shmalloc(sizeof(list_t));
    shared_list_mtx=(mtx_t*)shmalloc(sizeof(mtx_t));
    mtx_init(shared_list_mtx, mtx_plain);
    for(int j=0; j<3; ++j){
        struct mPair mp=make_pair(j, false);
        push_back(mlist, &mp, sizeof(struct mPair), true);
    }
    pid_t processes[6];
    for(int j=0; j<6; ++j){
        processes[j]=fork();
        if(processes[j]==0){
            modify_list(mlist);
            break;
        }
        else{
            waitpid(processes[j], NULL, 0);
        }
    }
}

however in the reinserted data the value of exists for some reason remains false even i change it in modify list, here is the output:

process: 5763 found 0: 0 I am reinserting it
process: 5764 found 1: 0 I am reinserting it
process: 5765 found 2: 0 I am reinserting it
process: 5766 found 0: 0 I am reinserting it
process: 5767 found 1: 0 I am reinserting it
process: 5768 found 2: 0 I am reinserting it

how can the changes be reflected on the list as well?


Solution

  • C11 mutexes work with multithreading, but, unlike some other mutexes, they are not specified to be suitable for multiprocessing. That does not necessarily imply that any particular implementation won't work for multiprocessing, but there is good reason to doubt it. For example, pthreads mutexes need special configuration to support multiprocess use, and C11 doesn't specify any way to request such configuration. If your mutexes don't provide for mutual exclusion and proper shared memory semantics across processes then there is no reason to expect that one of your processes will observe shared memory modifications made by other, concurrently running processes.

    Additionally, your list implementation has a serious deficiency for multiprocess use: new items cannot safely be added while the list is shared among multiple processes. That's because the mmap() performed by your shmalloc() produces a mapping only in the calling process. Such mappings are inherited across a fork(), but they are not otherwise communicated among your processes. That could sort of work if new node creation were limited to the parent, before any children were forked. But even then it would leak memory mappings, because only the child that removes a given node unmaps it. The node remains mapped but inaccessible in all other processes, including the parent.

    Generally speaking, multi-process shared-memory data structures cannot rely on pointers. Although there are circumstances in which different collaborating processes have a shared interpretation of certain pointer values, in the general case, pointer values are process-specific. Not even pointers to objects inside the same shared memory area are necessarily safe. For a data structure such as a dynamic list, the usual alternative is to use indexes into an array instead. Of course, that means you have to put the array into shared memory too, and do your own management of which elements are free for use. That also means that you need either to accept your list having a fixed capacity determined before it is shared, or else implement a mechanism by which you can expand the capacity dynamically across all the participating processes.

    Pretty much none of your current list implementation is suitable for multiprocessing.