pecl-gearman memleak troubleshooting in PHP7 port
| From: | Will Gallego | Date: | Fri, 06 May 2016 20:35:37 +0000 |
| Subject: | pecl-gearman memleak troubleshooting in PHP7 port | ||
| Groups: | php.pecl.dev | ||
| Request: | Send a blank email to pecl-dev+get-13735@lists.php.net to get a copy of this message | ||
Hey All,
*tl;dr* - if you have an example of storing a callback that is a method
call and destroying it at the object's destruction, could you share?
Specifically one that works with a user defined object method call as the
callback.
I've been working on the port for pecl-gearman to PHP 7 for the last year
or so, which is in a relatively stable state now. There's one last memleak
I'm trying to iron out. I'll leave replication details at the bottom to
avoid clutter.
Part of the extension is a callback for processing jobs, saving the added
functions in an object that's included in a hash. This works for most
cases. The only issue I have is when setting an object method call as the
callback. My guess is I'm not destroying that zval correctly, but I'm a
little stumped to find a corresponding extension or php core code that
holds onto a callback and destroys later. Apologies for the poor searching
skills, I'm sure it exists, but couldn't find it through sleuthing around
lxr.php.net.
Anyways, my thinking is at the script's end, the WorkerWrapper (see gists
below) instance is cleaned up, it cleans up the GearmanWorker member of
said class, which then attempts to destroy the hash consisting of
gearman_worker_cb objects (each object having a zval called 'zcall'
referencing the callback). It destroys the zcall but is leaving some
lingering refcount on the WorkerWrapper object which is part of it, which
then fails to destroy the GearmanWorker member. Mumble mumble hand-wavy
talk here a bit.
I guess I'm lacking context on any special work involved in either storing
or destroying a callback when you pass in as a callback something like:
[$this, "method"]
This differs from 5.x and below as the callback was stored using
zend_object_store_get_object, so had to change how that could be referenced
and deallocated.
*Environment/testing notes:*
I've been developing on Rasmus' vagrant image (
https://atlas.hashicorp.com/rasmus/boxes/php7dev)
but it should be platform
agnostic. PHP version is PHP 7.1.0-dev, but any stable PHP 7 release should
reproduce. Running valgrind with the default suppressions file points at an
object created and with some sigtraps and running through gdb it looks like
it is indeed the GearmanWorker object created that is referenced in the
memory leak.
The memleak is present without doing the "work", as in processing the job.
Creating a GearmanWorker instance and adding a function where the callback
is an object method is sufficient. Passing in a string or an anonymous
function referenced otherwise doesn't produce the memleak.
Link to repo ("issue_19" is the latest branch I'm developing this fix,
though it's not for prod use yet):
https://github.com/wcgallego/pecl-gearman
Gist of a client (to queue up jobs for worker to process):
https://gist.github.com/wcgallego/ab88ae50a7a886ec79509b9c93fb2539
GIst of worker (where the memleak occurs, processing the above job):
https://gist.github.com/wcgallego/2c331fa9154c98fbf0a29da1153a4696
and if you're looking to compile and run locally, you'll need gearman
installed (https://launchpad.net/gearmand) with gearmand running
(/usr/local/sbin/gearmand -d).
I'm also a lurker in #php.pecl on Efnet to get in contact. Thanks for
reading!