Search code examples
phpshared-memoryphp-extension

php: delete shared memory on windows


This code:

shmop_delete();
shmop_close();

doesn't delete shared memory. An experiment:

$shmid = @shmop_open(1234, 'a', 0, 0);
var_dump($shmid);

yields

bool(false)

of course. But

$shmid = shmop_open(5678, 'c', 0644, 10);
...
shmop_delete($shmid);
shmop_close($shmid);
...
$shmid = @shmop_open(5678, 'a', 0, 0);
var_dump($shmid);

yields

int(157)

Why not deleted yet? How can I delete shared memory? I'm running apache on windows 7.


Solution

  • SHM is not natively available in Windows, so PHP tries to emulate it in its "thread safe resource manager" (TSRM) by using Windows File Mappings internally, which is an ugly hack (/TSRM/tsrm_win32.c).

    Therefore, the shmop extension uses the TSRM for SHM on Windows systems, too. shmop_delete() uses shmctl() with the IPC_RMID command to mark the memory segment destroyed, however IPC_RMID is implemented in the following way in the emulation:

        switch (cmd) {
                [...]
                case IPC_RMID:
                        if (shm->descriptor->shm_nattch < 1) {
                                shm->descriptor->shm_perm.key = -1;
                        }
                        return 0;
    

    where shm_nattch is the number of processes the segment is attached to (or at least what number the TSRM believes it to be). By setting shm_perm.keyto -1, subsequent access by shmget() is blocked until the Windows File Mapping is destroyed. However, when this code is called from shmop_delete(), there's always at least the PHP process itself attached to the memory segment, so it effectively does nothing at all. The segment is only detached after you call shmop_close()

    So your answer is: Without fixing PHP, on Windows, you can't delete shared memory.

    You can blame it on either on the SHM emulation in the TSRM, which is not posixly correct, or on the shmop extension for blindly using it.

    You can try to remove the if and set shm_perm.key to -1 unconditionally and recompile PHP. It can only break the shmop extension itself, the sysvshm extension or probably other extensions not distributed with PHP.

    Feel free to report that to the PHP bugtracker at http://bugs.php.net/ and have it fixed by someone more familiar with PHP internals.

    In the meantime, maybe http://www.php.net/w32api could help - you could use CreateFileMapping & friends from the Win32-API in a more direct manner with it. However, I have never tested it and in PECL it says it is not maintained , so be careful. It is also of course not portable.

    You could also try to wrap the shmop_* stuff into your own library and put your own deleted-flag in the beginning of the memory segment - the TSRM does something similar internally after all. But then you could run into a related bug: I think I remember someone reporting that he couldn't create a segment with shmop_open() that was bigger than the last segment created using the same key.