[patch] Session RSHUTDOWN, shutdown_destructors() issues

From: Date: Wed, 16 Mar 2011 04:40:01 +0000
Subject: [patch] Session RSHUTDOWN, shutdown_destructors() issues
Groups: php.internals 
Request: Send a blank email to internals+get-51709@lists.php.net to get a copy of this message
This is a followup to http://bugs.php.net/bug.php?id=54157 . Johannes said I should post here. In the course of my MediaWiki development work, I hit a strange issue involving session_set_save_handler(). PHP's built-in session handler is pretty much useless when you have more than one server serving a given site, so session_set_save_handler() is essential. MediaWiki has a feature allowing it to save sessions via its memcached client. A suitable client object is put in $wgMemc during setup, which is used when the session is closed. A minor change to the way this variable was initialised caused it to disappear from the global symbol table before the session save handler was called. This turned out to be due to a broken algorithm in shutdown_destructors() in zend_execute_API.c. The algorithm basically does this: 1. Delete all objects from the global symbol table which have a zval reference count of 1 2. If any objects were deleted, go to step 1. Obviously the intention is to first delete the references, and then to delete the objects which were referred to. It doesn't work that way: if you have a reference, both symbol table entries point to the same zval, so they both have a reference count of 2, so the algorithm skips both and leaves them intact for RSHUTDOWN. My $wgMemc initialisation change caused the reference count to drop from 2 to 1, so the variable was deleted. Fixing this function to correctly delete all objects from the global symbol table would be easy enough (against 5.3): http://tstarling.com/stuff/shutdown_destructors-sanity.patch But please don't apply that, because it would break all released versions of MediaWiki. We now come to the next strange thing about this code: why is it attempting to delete all objects from the global symbol table anyway? There are plenty of other ways to store objects: function static variables, class static variables, etc. Deleting them from $GLOBALS doesn't stop them from being accessed. And besides, we know that there's no problem with accessing an object after __destruct() has been called on it, because that was the whole point of splitting out shutdown_destructors() in the first place, as a comment in shutdown_executor() explains: /* Removed because this can not be safely done, e.g. in this situation: Object 1 creates object 2 Object 3 holds reference to object 2. Now when 1 and 2 are destroyed, 3 can still access 2 in its destructor, with very problematic results */ /* zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); */ Just removing the whole loop would suit me, as a 5.3.x stopgap measure: http://tstarling.com/stuff/no-global-deletion.patch But that still leaves the problem that the session save handler is called in the strange and scary world of half-shut-down PHP. We don't know what RSHUTDOWN functions have been called before the session RSHUTDOWN, so we don't know what userspace code will work and what won't. A workaround is easy enough: http://www.mediawiki.org/wiki/Special:Code/MediaWiki/83147 but I thought I would be a good open source citizen and come up with a proper solution, against trunk. It is attached and at: http://tstarling.com/stuff/session-pre-deactivate.patch The idea is to introduce a pre-RSHUTDOWN hook, allowing modules to call user code in a relatively sane environment. It's implemented in a way that is closely analogous to the post-deactivate hook. I had trouble testing it because trunk was so broken that it wouldn't run MediaWiki, but the patch appears to work for simple CLI test cases. -- Tim Starling

Attachment: [text/x-diff] session-pre-deactivate.patch
« previous php.internals (#51709) next »