php / pecl-system-sync

Synchronization objects
http://pecl.php.net/package/sync
MIT License
16 stars 11 forks source link

[Request feature] Delete shared memory #4

Closed ziaratban closed 2 years ago

ziaratban commented 2 years ago

Hi

How do I clear a shared memory object?

$mem = new SyncSharedMemory("AppReportName", 1024);
$mem->First(); //true
$mem->Clear(); //Clear from memory
$mem->First(); //Reallocate memory like first line & return false
$mem->Write(); // Everything ok
cubiclesoft commented 2 years ago

Shared memory is simply a chunk of physical RAM that gets mapped into the process space by the OS. In your example, you would have a grand total of 1024 bytes of shared memory. To "clear" the memory, you would simply move to the beginning and write up to 1024 bytes of whatever you define as "clear." You should surround shared memory accesses with at least one synchronization primitive(s) like a mutex to avoid race conditions. What you do with shared memory is up to you.

Been a while since I looked but I'm pretty sure physical RAM is guaranteed to be reserved by the kernel (only if space is available) and won't use swap space for shared memory segments. For that reason, system level shared memory objects should be used sparsely by applications in general (not just PHP) and kept fairly small. Also, since system memory is allocated/reserved/mapped in specific sizes, you might as well round up the value to the nearest page size of the system. Modern OSes allocate pages in chunks of 4KB or 8KB. So even if you only need 50 byes, you'll still get a full 4KB page. But as I said, it's been a while since I looked at that.

ziaratban commented 2 years ago

Thanks for the explanation. But I think I do not choose a good word(clear) for my goal. My goal is shmop_delete.

cubiclesoft commented 2 years ago

There's no need for that. When all object instances go out of scope, the shared memory is freed. Example code:

<?php
    echo "Part 1.\n";
    $obj = new SyncSharedMemory("test", 4096);
    var_dump($obj->First());
    var_dump($obj->Write("blahblah"));
    var_dump($obj->Read());
    echo "\n";

    echo "Part 2.\n";
    $obj2 = new SyncSharedMemory("test", 4096);
    var_dump($obj2->First());
    var_dump($obj2->Read());
    echo "\n";

    unset($obj);

    echo "Part 3.\n";
    $obj = new SyncSharedMemory("test", 4096);
    var_dump($obj->First());
    var_dump($obj->Read());
    echo "\n";

    unset($obj);
    unset($obj2);

    echo "Part 4.\n";
    $obj = new SyncSharedMemory("test", 4096);
    var_dump($obj->First());
    var_dump($obj->Read());
?>

Produces:

Part 1.
bool(true)
int(8)
string(4096) "blahblah"

Part 2.
bool(false)
string(4096) "blahblah"

Part 3.
bool(false)
string(4096) "blahblah"

Part 4.
bool(true)
string(4096) ""
ziaratban commented 2 years ago

There's no need for that. When all object instances go out of scope, the shared memory is freed.

Also at the end of the process? or do I have to call unset() manually?

Okay, but how does it work in multiple processes?

            -------------------------------Lifecycle-----------------------------
Process 1 : |-------New('test')-------------|
Process 2 :    |---------New('test')-----------|
Process 3 :                                   |---------New('test')--------------|

Is there still data in Process 3? In my mind is:

cubiclesoft commented 2 years ago

Also at the end of the process? or do I have to call unset() manually?

When the refcount to the shared memory object across all processes drops to zero, the shared memory object is reset/removed.

The use of the unset() keyword removes a reference in PHP and will generally call a destructor on a class instance when the last reference to the object is removed. It was just a way to demonstrate the refcounting aspect.

<?php
    class TestClass
    {
        public function __construct()
        {
            echo "constructor.\n";
        }

        public function __destruct()
        {
            echo "destructor.\n";
        }
    }

    $obj = new TestClass();

    echo "Before unset.\n";
    unset($obj);
    echo "After unset.\n";
?>

Output is:

constructor.
Before unset.
destructor.
After unset.

Shared memory contents continue to be available and First() will return false as long as there is one process holding onto an instance of the object. The point of named objects is to use them across processes.

Whenever I want to test out a theory that's timing based, I use echo and sleep() statements in a quick little bit of code. I'll fire up a few terminal sessions and run one process in one terminal and then start another process in a second terminal and maybe another process in a third terminal. sleep() statements allow sufficient time to start the other processes and also allow for watching the timed operations happen at a snail's pace instead of going so fast that there's no way to understand what is happening.

ziaratban commented 2 years ago

The use of the unset() keyword removes a reference in PHP and will generally call a destructor on a class instance when the last reference to the object is removed. It was just a way to demonstrate the refcounting aspect.

I think there is a logical bug here. PHP remove all objects at the end of the script. So php automatically calls the __destruct() method. So if the sync project uses this method to remove the shared memory block, the data will practically never remain in the shared memory.

And in fact I can not use it as a cache !

Am I guessing right?

cubiclesoft commented 2 years ago

Yes. And it's consistent behavior with Windows. Shared memory is for conducting IPC between two currently running processes, not long-term storage.

For a RAM cache, on many Linux distros there is /run which is mounted as tmpfs. That's basically a RAM drive. More generic alternatives are to use some sort of shared object caching system like Memcached.

ziaratban commented 2 years ago

Hmmm, I think this behavior is present in Linux as well.

The shared memory created by shm_open is persistent. It stays in the system until explicitly removed by a process. This has a drawback that if the process crashes and fails to clean up shared memory it will stay until system shutdown. https://en.wikipedia.org/wiki/Shared_memory

Shared memory is for conducting IPC between two currently running processes, not long-term storage.

Why ? Can you explain more?

https://stackoverflow.com/questions/25823097/unix-socket-vs-shared-memory-message-which-is-faster https://stackoverflow.com/questions/2101671/unix-domain-sockets-vs-shared-memory-mapped-file

Yes. And it's consistent behavior with Windows

I tested this behavior in shmop between two process and it worked when two processes exit .

ziaratban commented 2 years ago

Please also visit https://github.com/goldsborough/ipc-bench

cubiclesoft commented 2 years ago

Hmmm, I think this behavior is present in Linux as well.

The shared memory created by shm_open is persistent. It stays in the system until explicitly removed by a process. This has a drawback that if the process crashes and fails to clean up shared memory it will stay until system shutdown. https://en.wikipedia.org/wiki/Shared_memory

Shared memory is for conducting IPC between two currently running processes, not long-term storage.

Why ? Can you explain more?

Computer science. But your own quote above summarizes why you definitely want shared memory to go away when all processes using it exit: "This has a drawback that if the process crashes and fails to clean up shared memory it will stay until system shutdown." That's a pretty serious drawback. Stuff crashes all the time and usually unexpectedly. Leaving things in unknown states is why synchronization objects, shared memory, and other system resources should be managed in the kernel, not in user space. shm_open() is a fundamentally flawed function by design. PECL sync provides a minor bit of protection in that even if the script being executed by PHP "crashes," PHP itself isn't likely to crash and will likely allow PECL sync to clean up any objects being used in a relatively safe manner. There are still ways to create broken objects and leave behind a mess on the system even with this extension such as forcefully killing PHP (e.g. kill).

https://stackoverflow.com/questions/25823097/unix-socket-vs-shared-memory-message-which-is-faster https://stackoverflow.com/questions/2101671/unix-domain-sockets-vs-shared-memory-mapped-file

Yes. And it's consistent behavior with Windows

I tested this behavior in shmop between two process and it worked when two processes exit .

shmop uses POSIX shared memory functions on Linux. Shared memory on Windows, however, is non-persistent. When the last handle closes on Windows, the associated shared memory is freed by the kernel:

https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory

If you aren't seeing that behavior on Windows, then you still have a handle open to the shared memory somewhere. For consistency between all supported OSes, PECL sync frees shared memory when the last process using it closes its final handle. Windows, of course, does that automatically in the kernel. PHP userland code should work identically everywhere. I personally dislike when there are significant discrepancies in an implementation in PHP core rather than having expended a little extra effort to have feature parity across all major platforms.

For long-term, high-performance RAM-oriented storage, use a mounted RAM drive. If the concern is sheer performance, then writing C/C++ code is a better solution. Micro-optimization inside an interpreted language like PHP isn't a particularly useful exercise.