1

Using PHP threads, I've met a few times issues with "Serialization of Closure" fatal error. However I don't understand how it happens : I don't pass closure or any kind of callable to another thread, sometimes when I take of the code outside the project and run it alone it works, but fails into the project.

So I tried to make a script fail by serialization of closure, to understand how it actually works.

This code uses closures and actually works :

<?php

class MyWorker extends Worker {

    protected $kernel;
    protected $container;

    public function __construct($closure)
    {
        echo "Worker::__construct()";
        echo PHP_EOL;
        echo $closure();
        echo PHP_EOL;
    }

    public function run()
    {
        echo "Worker::run()";
        echo PHP_EOL;
    }
}

class Toto {

    protected $c;

    public function __construct()
    {
        $this->c = function() {
            return "CLOSURE";
        };
    }

    public function go()
    {
        $c = $this->c;
        $pool = new Pool(4, MyWorker::class, [function(){
            return "WORKER_CLOSURE";
        }]);
        $pool->submit(new class ($c) extends Threaded {

            public function __construct($c)
            {
                $this->c = $c;
            }

            public function run()
            {
                $c = $this->c;
                echo $c();
                echo "Threaded::run()";
                echo PHP_EOL;
            }
        });
    }
}

$toto = new Toto;
$toto->go();

When running alone. But inside a Symfony command, the following code simply fails. I don't understand why therefore I cannot fix it :

<?php

use Threaded;
use Thread;

class Foo
{    
    public function run()
    {

        $t = new Thread;
        $t->start();
        $t->join();
    }
}

So I thought maybe it's because this code executed in a closure (I don't know but maybe), so I tried this :

$c = function() {

    $t = new class extends Thread {

        public function run()
        {
            echo "yo";
        }
    };

    $t->start();
    $t->join();
};

$c();

And it works. So passing closure to a thread works, building, starting and joining a thread from inside a closure also works, I don't know where to look now ...

I've look at this too : Storing closures in thread fails, but the code is outdated and it now ( PHP 7, pthreads 3.x ) works. So here I am asking for help to understand what is actually the problem with Closure...

Community
  • 1
  • 1
JesusTheHun
  • 1,217
  • 1
  • 10
  • 19

1 Answers1

0

The latest versions of pthreads do allow serialization of Closure objects; When used as a member property of a Threaded object, pthreads creates a copy of the function that the Closure is closing over to execute in other contexts.

Zend still does not allow serialization of Closure objects:

class Member {

    public function __construct(Closure $closure) {
        $this->closure = $closure;
    }

    public $closure;
}

class Fail extends Thread {

    public function __construct(Member $member) {
        $this->member = $member;
    }

    public function run() {
        ($this->member->closure)();
    }

    private $member;
}

$member = new Member(function(){
    echo "can only work if Member is Threaded !\n";
});

$fail = new Fail($member);

$fail->start() && $fail->join();

The code above will not work as it is, because Member is serialized when set as a member of the Threaded object, this results in pthreads trying to serialize a Closure.

Declaring Member thus:

class Member extends Threaded

Will cause it to behave as expected.

I'm not familiar enough with Symfony to tell you where to look to solve the problem, but this is certainly the cause of it; A Closure was set as a member property of an object that pthreads subsequently tries to serialize.

Joe Watkins
  • 17,032
  • 5
  • 41
  • 62
  • So Zend doesn't allow serialization of Closure but pthreads deals with it if wrapped into a Threaded object ?. In the second sample of code in my post, nothing is passed to the thread, so why could it fail ? Is some scope involved in it ? Like embed into the creation of the thread ? – JesusTheHun Feb 21 '16 at 17:22