I've used APC + custom session handler for ages without any problems. And couldn't this be fixed without such nasty hooks..?
--Jani On 02/19/2010 05:54 PM, Christian Seiler wrote:
Hello, [CC to pecl-dev since this is APC-related.] I have been investigating a problem occuring when using APC in combination with userspace session save handlers. This has been reported multiple times both in the APC and PHP bugtrackers, bugs related to this issue include (but are most likely not limited to): http://bugs.php.net/bug.php?id=48787 http://bugs.php.net/bug.php?id=48686 http://pecl.php.net/bugs/bug.php?id=16721 http://pecl.php.net/bugs/bug.php?id=16745 Using a debugger I have found the source of the problem. In my eyes, this is a design problem in PHP's session extension. Rationale: Currently, the session module calls php_session_flush() in its RSHUTDOWN function. This makes sure that the session handler is always called at the very end of execution. However, APC uses its RSHUTDOWN function to remove all class entries from the class table, since they are only shallow copies of memory structures of its cache and the Zend engine should not destroy the class entries by itself. The problem now occurs when the session save handler uses non-internal classes in some way. Since APC, as a dynamic extension, is always loaded AFTER the session module (always a static extension), and the module de-initialization is in reverse order, APC will unload all internal classes *before* the session save handler is executed. So when it is finally the turn of the the session save handler, the classes will no longer be present in the class table, causing the problems described above. Note that APC is not the only extension for which there is a problem with regards to de-initialization. The Spidermonkey extension destroys the current Javascript execution context in the RSHUTDOWN function, so if should any save handler use the Spidermonkey extension, it will not work. In theory, even the date extension could cause minor problems (since the default timezone is reset in RSHUTDOWN), but it gets away since it is always loaded *before* session and thus de-inititialized afterwards. I consider this to be a design problem in PHP's session extension. It tries to use the PHP engine to execute PHP code (the custom session save handler, possibly also the serialization handlers of objects etc.) during a phase where at least some other extension that may be used in that code are already de-initialized. The only reason why it got unnoticed so long is that the RSHUTDOWN code of most - but not all - extensions doesn't really have any really nasty side-effects on its use. It would not surprise me at all, however, if there were memory leaks and other strange this occurring due to this problem. My immediate fix for this problem is to make php_request_shutdown() execute the session save handler just after the userspace shutdown functions but before the RSHUTDOWN functions of the other modules. I have attached short patches to this mail for PHP 5.2, 5.3 and 6.0 that accomplish this. It's not very elegant but it is the least-invasive fix I could come up with. Not existing tests are broken in either of the three branches. Unfortunately, I was not able to create a unit test for this using core PHP alone, since all of the core extensions hide the problem. I wanted to get some feedback first. If nobody objects, however, I'll apply these patches to PHP 5.3 and PHP 6 tomorrow. As for PHP 5.2: I was extremely busy the last two months so I didn't really follow the list. Is it okay if I apply it? I do think this patch should go into the last release of PHP 5.2 (which will be used by quite a few people for a while) since this bug prevents people from using a custom session save handler in combination with APC. On a side note: For PHP6 we may think of adding an additional hook for modules that is executed at the very beginning of the shutdown phase for precisely this purpose. Regards, Christian
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php