4

When such situation occurs?

If your are using shared memory and semaphores for interpocess locking (with pcntl extension) you should care about semaphore and shared memory segment life circle. For example, you writing backgroud worker application and use master and some child (forked) process for job processing. Using shared memory and semaphores good idea for IPC between them. And RAII like class wrapper around shm_xxx and sem_xxx php functions look`s like good idea too.

Example

class Semaphore
{
     private $file;

     private $sem;

     public function __construct()
     {
        $this->file = tempnam(sys_get_temp_dir(), 's');
        $semKey = ftok($this->file, 'a');

        $this->sem = sem_get($semKey, 1); //auto_release = 1 by default
     }

     public function __destruct()
     {
         if (is_resource($this->sem) {
            sem_remove($this->sem);
         }
     }

     ....
}

Not the good choise - after fork we have one instanse in parent and one in child process. And destructor in any of them destroy the semaphore.

Why important

Most of linux systems has limit about semaphore of shared memory count. If you have application which should create and remove many shared memory segfments of semaphores you can`t wait while it be automatically released on process shutdown.

Question

Using с you can use shmctl with IPC_RMID - it marks the segment for removal. The actual removal itself occurs when the last process currently attached to the segment has properly detached it. Of course, if no processes are currently attached to the segment, the removal seems immediate. It works like simple referenc counter. But php do not implements shmctl.

The other strategy - destroy semaphore only in destructor of master process:

class Semaphore
{
     ... 
     private $pid;

     public function __construct()
     {
        $this->pid = getmypid();
        ...
     }

     public function __destruct()
     {
         if (is_resource($this->sem) && $this->pid === getmypid()) {
            sem_remove($this->sem);
         }
     }
     ....
}

So, the questions is

  1. If any way to use IPC_RMID in php?
  2. What strategy should be used in such cases? Destroy in master process only? Other cases?
Nick Bondarenko
  • 6,211
  • 4
  • 35
  • 56

1 Answers1

1

I checked the current PHP source code and IPC_RMID is not used. However, PHP uses semop() and with it, the SEM_UNDO flag, in case auto_release (see PHP sem_get() manual) is set. But be aware that this works on a per process level. So in case you are using PHP as Apache module, or FCGI or FPM, it might not work as expected. It should work nicely for CLI, though.

For your cleanup, it depends on whether the "master" terminates last or not.

If you do not know, you can implement reference counting yourself.

class Semaphore
{
    static private $m_referenceCount = 0;

    public function __construct()
    {
        ++self::$m_referenceCount;
        // aquire semaphore
    }

    public function __destruct()
    {
        if (--self::$m_referenceCount <= 0) {
            // clean up
        }
    }
}

But be aware that the destructor is NOT executed in some circuumstances.

Community
  • 1
  • 1
Shi
  • 4,178
  • 1
  • 26
  • 31
  • Yes, my question in more than other actual for CLI case. The self-made reference count has one critical issue - you should be sure about reference incr/decr is atomic operation. So you should have some interprocess lock to achieve this. Yes? – Nick Bondarenko Jun 10 '14 at 08:43
  • Incrementing or decrementing an integer (at native size) is atomic for sure! What's likely not atomic is the decrement AND compare operation. But even without this being atomic, it can still work reliably in certain cases: If you create all your instances first, and then remove them one after another at any time, without creating new ones, you will clean up. Maybe twice or more, but at least once. – Shi Jun 15 '14 at 08:04
  • Shi, i mean that atomic interlocking should be made in some separated CLI processes - master and one slave in simple case. The PHP shared memory realization do not have any atomic operations. So you should make locking themself. The basic realization for that locking is shared memory + semaphore. But how to be shure about that locking object life circle? – Nick Bondarenko Jun 15 '14 at 13:46