Home > Back-end >  Is it possible to send the instance of class with member function between two process?
Is it possible to send the instance of class with member function between two process?

Time:10-04

Consider I have this kind of class in process 1. And run process2 whose code is written in completely different file.

class A{
public:
  int data;
  A()
  int f();
}

I want to make instance a of class A in process1, send it to process2, and run a.f() in process2. I studied about IPC mechanism including POSIX shared memory or message queue and read many examples, but the most of examples only expalin how to send single data like integer, or struct without member function.

  1. Is it possible?
  2. If it's possible, how can I do it using POSIX shared memory? Can you give me short example?

CodePudding user response:

Do not to use IPC, but RPC (Remote Procedure Calls) for that. E.g. gRPC : https://grpc.io/docs/what-is-grpc/introduction/. Sharing data "manually" with shared memory can be done but you end up having to write synchronization mechanism yourself (locks on data, signals that data has been written into shared memory etc.)

CodePudding user response:

It is definitely possible to do it in shared memory.

Take for example the following class:

struct A {                                                                                                                                                                                                                                                                     
    A(int initial_value) {                                                                                                                                                                                                                                                     
        internal_value = initial_value;                                                                                                                                                                                                                                        
    }                                                                                                                                                                                                                                                                          
    void write(int value) {                                                                                                                                                                                                                                                    
        internal_value = value;                                                                                                                                                                                                                                                
    }                                                                                                                                                                                                                                                                          
    int read() const {                                                                                                                                                                                                                                                         
        return internal_value;                                                                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                                                          
    void watch(int value) {                                                                                                                                                                                                                                                    
        while (internal_value == value) usleep(1000);                                                                                                                                                                                                                          
    }                                                                                                                                                                                                                                                                          
    std::atomic<int> internal_value;                                                                                                                                                                                                                                           
};       

The class uses an atomic counter inside that will be shared between two processes, both reading and writing from it.

Let's first create some shared memory space for it.

    int mapsize = getpagesize();                                                                                                                                                                                                                                               
    int fid = ::open("/tmp/shared.dat", O_CREAT | O_RDWR, S_IRWXU | S_IRWXG);                                                                                                                                                                                                  
    ftruncate(fid, mapsize);                                                                                                                                                                                                                                                   
    void* ptr = ::mmap(nullptr, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fid, 0);              

Notice I am not checking the return values for brevity but you should do in your code.

Now with this memory space allocated, let's initialize an object of class "A" inside that memory with placement new.

    A* a = new (ptr) A(0);  // placement new                                                                                                                                                                                                                                   

Then we fork another process to simulate the IPC mechanism working

    pid_t pid = fork();                                           

For the parent process code, we loop from 0 to 10 first waiting for it to be different from zero then 2, 4 etc. Then in the end we wait for the child to finish.

    if (pid != 0) {                                                                                                                                                                                                                                                            
        for (int j = 0; j < 10; j  = 2) {                                                                                                                                                                                                                                      
            printf("-->parent %d\n", j);                                                                                                                                                                                                                                       
            a->watch(j);                                                                                                                                                                                                                                                       
            a->write(j   2);                                                                                                                                                                                                                                                   
        }                                                                                                                                                                                                                                                                      
        printf("Finishing parent\n");                                                                                                                                                                                                                                          
        int status;                                                                                                                                                                                                                                                            
        wait(&status);                                                                                                                                                                                                                                                         

For the child process, we write 1 and wait for 1, then write 3 and wait for 3 and so on like the parent but with odd numbers.

    } else {                                                                                                                                                                                                                                                                   
        for (int j = 1; j < 10; j  = 2) {                                                                                                                                                                                                                                      
            printf("-->child %d\n", j);                                                                                                                                                                                                                                        
            a->write(j);                                                                                                                                                                                                                                                       
            a->watch(j);                                                                                                                                                                                                                                                       
        }                                                                                                                                                                                                                                                                      
        printf("Finishing child\n");                                                                                                                                                                                                                                           
    }   

In the end of both we unmap the memory and close the file

    ::munmap(ptr, mapsize);                                                                                                                                                                                                                                                    
    ::close(fid);                                                                                                                                                                                                                                                              

It prints:

-->parent 0
-->child 1
-->parent 2
-->child 3
-->parent 4
-->child 5
-->parent 6
-->child 7
-->parent 8
-->child 9
Finishing parent
Finishing child

Compiler Explorer link: https://godbolt.org/z/5orPaEhPM

If you need two independent processes running side by side you just need to be careful with how you create them, synchronize them.

Start the same way opening a file and memory mapping it

    int mapsize = getpagesize();                                                                                                                                                                                                                                               
    const char* filename = "/tmp/shared.dat";                                                                                                                                                                                                                                  
    int fid = ::open(filename, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG);                                                                                                                                                                                                           
    void* ptr = ::mmap(nullptr, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fid, 0); 

Notice that at this point if you write or read from the pointer ptr you will get a segfault.

Then you need to lock the file

    while (flock(fid, LOCK_EX) != 0) {                                                                                                                                                                                                                                         
        usleep(10000);   // sleep a bit
    }                    

Then you need to check the file. If the size is zero, it is uninitialized. If the file is already initialized, you do not use placement new, you just cast the raw pointer.

    A* a;                                                                                                                                                                                                                                                                      
    if (getfilesize(fid) == 0) {                                                                                                                                                                                                                                               
        ftruncate(fid, mapsize);                                                                                                                                                                                                                                               
        a = new (ptr) A(0);                                                                                                                                                                                                                                                    
    } else {                                                                                                                                                                                                                                                                   
        a = reinterpret_cast<A*>(ptr);                                                                                                                                                                                                                                         
    }     

After that you read your value and increment it while the file is still locked.

    int value = a->read();                                                                                                                                                                                                                                                     
    value  = 1;                                                                                                                                                                                                                                                                
    a->write(value);                                                                                                                                                                                                                                                           

Then you can unlock the file

    flock(fid, LOCK_UN);  // unlock         

Then it's just the same drill of waiting, incrementing and looping, only a tad different.

    // Change 5 times                                                                                                                                                                                                                                                          
    for (int j = 0; j < 5;   j) {                                                                                                                                                                                                                                              
        printf("-->proc %d %d\n", getpid(), value);                                                                                                                                                                                                                            
        a->watch(value);                                                                                                                                                                                                                                                       
        value  = 2;                                                                                                                                                                                                                                                            
        a->write(value);                                                                                                                                                                                                                                                       
    }        

In the end, just unmap and close as before

    ::munmap(ptr, mapsize);                                                                                                                                                                                                                                                    
    ::close(fid);   

The whole code is here: https://godbolt.org/z/zK3WKWqj4

  • Related