Home > database >  how to call destructor only once when fork
how to call destructor only once when fork

Time:07-15

I wrote MmapHandler that provides a shared memory among processes using mmap and at destruction, it unmaps the shared memory. I want munmap to be called once by the parent process. However, this unfortunately gets called by all child processes:

MmapHandler.h:

template<typename T>
class MmapHandler{
protected:
  size_t m_size;
  T* m_ptr;
public:
  MmapHandler(size_t);
  ~MmapHandler();
}

MmapHandler.cpp:

template<typename T>
MmapHandler<T>::MmapHandler(size_t size): m_size(size), m_ptr(MAP_FAILED)
{
  m_ptr = static_cast<T*>(mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  if(m_ptr == MAP_FAILED){
    throw std::exception();
  }
}

template<typename T>
MmapHandler<T>::~MmapHandler()
{
  if(m_ptr != MAP_FAILED)
  {
    munmap(static_cast<void*>(m_ptr), m_size);
  }
}

main.cpp:

int num_procs = 3;
pid_t pids[num_procs];
MmapHandler sharedMemory(4096);
for(int i=0; i<num_procs; i  )
{
  if((pids[i] = fork()) < 0){
    perror("fork");
  }
  else if (pids[i] == 0){

    // do something with sharedMemory
    // ...
    // 

    return 0;
  }
}

while(wait(NULL) > 0);
// all children done!
return 0;

If one of the children process exits (e.g. exception), then it destroys the sharedmemory (e.g. munmap). Is there a way I can disable this on my child processes?

I can pass an argument such that only a certain pid can unmap the shared memory, but is there a more elegant C way of doing this?

I thought about =delete on destructor for child processes only using macros but this obviously won't work...

I also thought about creating a wrapper MmapHandler class that would =delete the destructor, and all the other MmapHandler will be moved to the wrapper handler, but I'm not sure if this'll work.

CodePudding user response:

Why pass an argument, with anyone's pid, somewhere? There's no need to do that.

m_ptr = static_cast<T*>(mmap(NULL, size, PROT_READ, MAP_SHARED, -1, 0);

Here's where you're mmaping the memory segment. How about calling getpid() right here. This gives you your own pid, that you can save somewhere.

T* m_ptr;

It seems logical that the pid_t would also be saved, right next to the m_ptr.

munmap(static_cast<void*>(m_ptr), m_size);

And this is where the shared memory segment is getting unmapped. It should now be obvious that calling getpid() here lets you check if it's the same pid that mapped it, and unmap it only in that case, and do nothing if it's some other process.

Now, nothing outside of this class needs to do anything, and The Right Thing Will Happen.

CodePudding user response:

Store a reference counter in your shared memory; and have the last process free the memory. The counter must be syncronized between processes, so an interprocess counting semaphore may serve the trick.

Iff you can confirm that the grand parent is the last man standing or want to grant it the privilege of ownership of shared memory, you can store the PID of that process inside the constructor, and decide to delete in destructor - based on a comparison to current PID.

Using fork after using C specific code is often not considered good practice though. system, spawn or other utilities such as boost.interprocess might give better results.

Regards, FM.

  • Related