3

This question follows this answer : Why not all threads are completed?

I've tried to implement Joe Watkins's exemple into a couple of classes :

SynchronizedWork :

<?php

class SynchronizedWork extends Threaded {

    /**
     * @var int Dummy work ID
     */
    protected $id;
    /**
     * @var $resultBag Volatile Result container passed around threads
     */
    protected $resultBag;

    /**
     * @var bool Should this work be collected for garbage ?
     */
    protected $garbage = 0;

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

    public function getId()
    {
        return $this->id;
    }

    public function setResultBag(Volatile $volatile)
    {
        $this->resultBag = $volatile;
        return $this;
    }

    /**
     * @return $this Set this work ready to be collected for garbage
     */
    public function setGarbage()
    {
        $this->garbage = 1;
        return $this;
    }

    /**
     * @return bool Is this work ready to be collected for garbage ?
     */
    public function isGarbage() : bool
    {
        return $this->garbage === 1;
    }

    public function run()
    {
        $this->send(range(0, 10, $this->getId()));
    }

    public function send($result)
    {
        $this->resultBag->synchronized(function(Volatile $results, $workResult){
            $results[$this->getId()] = $workResult;
            $results->notify();
        }, $this->resultBag, $result);
    }
}

SynchronizedPool :

class SynchronizedPool extends Pool {

    /**
     * @var int Pool's lifetime submitted works count
     */
    protected $submitted = 0;
    /**
     * @var int Pool's lifetime collected works count
     */
    protected $collected = 0;

    protected $resultBag;

    public function __construct($size, $clazz = Worker::class, $ctor = array())
    {
        $this->resultBag = new Volatile;
        parent::__construct($size, $clazz, $ctor);
    }

    /**
     * @param Threaded $threaded Submit the work to the pool and will be dispatched among Pool's Workers
     * @return int|void
     */
    public function submit(Threaded $threaded)
    {
        ++$this->submitted;
        $threaded->setResultBag($this->resultBag);
        parent::submit($threaded);
    }

    public function getResultBag()
    {
        return $this->resultBag;
    }

    /**
     * @return int Return the number of work submitted since the Pool has been created
     */
    public function submitted()
    {
        return $this->submitted;
    }

    /**
     * @return array Return the list of workers in the stack. Collected workers are no longer in this stack.
     */
    public function getWorks() : array
    {
        return $this->workers;
    }

    /**
     * @return int Return the number of work collected for garbage since the Pool has been created
     */
    public function collected()
    {
        return $this->collected;
    }

    public function processResult(Closure $closure)
    {
        $found = 0;
        $resultBag = $this->getResultBag();

        do {
            $workResult = $this->getResultBag()->synchronized(function() use(&$found, $resultBag) {
                while (!count($resultBag)) {
                    $resultBag->wait();
                }

                $found++;
                return $resultBag->shift();
            });

            $closure($workResult);

        } while ($found < $this->submitted());
    }
}

And the execution :

$pool = new SynchronizedPool(3);

$pool->submit(new SynchronizedWork(1));
$pool->submit(new SynchronizedWork(2));
$pool->submit(new SynchronizedWork(3));

$pool->processResult(function($workResult) {
    var_dump($workResult);
});

while($pool->collect()) continue;

$pool->shutdown();

It appears to fail with :

    PHP Fatal error:  Uncaught RuntimeException: pthreads detected an attempt to connect to an object which has already been destroyed in /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php:137
Stack trace:
#0 /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php(137): Threaded->shift()
#1 [internal function]: SynchronizedPool->{closure}()
#2 /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php(138): Threaded->synchronized(Object(Closure))
#3 /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php(154): SynchronizedPool->processResult(Object(Closure))
#4 {main}
  thrown in /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php on line 137

I've also tried to call processResult before submit, thinking maybe the ref are destroyed right after the end (even if I didn't call collect), but in this case, it just wait forever.

Why does it say resultBag has been destroyed ? The ref is still hold in Pool isn't it ? What am I doing/understanding wrong ?

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

1 Answers1

2

After crawling pthreads's github & some SO questions, I've find the issue, and debunked the next one :

In the send method, I have to cast the thread's result to an actual array to prevent it to be coerce into a Volatile which will be destroyed by the end of the method (if I understand correctly). Which leads to :

In SynchronizedWork class.

public function send($result)
{
    $this->resultBag->synchronized(function(Volatile $resultBag, $workResult){
        $resultBag[$this->getId()] = (array) $workResult;
        $resultBag->notify();
    }, $this->resultBag, $result);
}

And then, set the thread ready for garbage collection otherwise the pool collect will wait forever.

public function run()
{
    $holdenRef = range(0, 10, $this->getId());
    $this->send($holdenRef);
    $this->setGarbage();
}
JesusTheHun
  • 1,217
  • 1
  • 10
  • 19
  • It was one year ago so I don't really remember. But I did say "After crawling pthreads's github & some SO questions" so I guess I read a bit of source code and some pthreads issues related to switching thread context. Now I use a tiny lib I coded https://github.com/JesusTheHun/GreenMT if it can help you somehow ... – JesusTheHun Nov 29 '16 at 15:56
  • Read the main answer here : http://stackoverflow.com/questions/14796674/a-php-pthreads-thread-class-cant-use-array,especially the first PHP7 example, you see that any array becoming part of a thread-family object, becomes a Volatile, and therefore will not survive to its attached context. – JesusTheHun Nov 29 '16 at 16:01