Wednesday, March 29, 2017

Upgrading from PThreads v2 to V3: What to Look out For

A fair amount has changed for the pthreads extension with the release of pthreads v3. This article aims to cover the necessary information for those who are looking to upgrade their applications from pthreads v2 to v3.

If you're unfamiliar with pthreads, check out my introduction to pthreads instead!

A big thank you to Joe Watkins for proofreading and helping to improve my article!

Abstract image of parallel tracks with superimposed numbers 2 and 3, indicating version change

Generic Changes

There have been a few general changes made in pthreads v3. The first, and perhaps most prominent, is that pthreads cannot be used in any environment other than the command line interface. It was never meant to be used in a web server environment (i.e. in an FCGI process) due to safety and scaling concerns, so the advice from pthreads v2 has now been enforced.

There have also been some changes to workers. Previously, there was a need to keep track of the work objects given to workers, otherwise if they were destroyed before having been executed by the worker thread, a segmentation fault would occur. This was well-known behavior and was demonstrated succinctly in the Multi-Threading in PHP with pthreads gist with the following snippet:

class W extends Worker {
    public function run(){}
}
class S extends Stackable {
    public function run(){}
}
/* 1 */
$w = new W();
/* 2 */
$j = array(
    new S(), new S(), new S()
);
/* 3 */
foreach ($j as $job)
    $w->stack($job);
/* 4 */
$j = array();
$w->start();
$w->shutdown();

This is no longer an issue because the workers themselves now track the stacked work objects.

Furthermore, there have been some changes around the meaning of method modifiers in pthreads v3. In pthreads v2, method modifiers had a special meaning in the context of Threaded objects. Specifically, protected methods had implicit synchronized access (enabling for them to be safely executed by multiple contexts), and private methods could only be executed by the context they were tied to. These differing semantics have now been removed due to reliability concerns.

For example, take the following snippet:

class ExampleThread extends Thread {
    public $value = 0;

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

    protected function exclusive()
    {
        for ($i = 0; $i < 10000; ++$i) {
            ++$this->value;
        }
    }
}

class Test extends ExampleThread {
    public function callExclusive()
    {
        $this->exclusive();
    }
};

$thread = new Test();
$thread->start();
$thread->callExclusive();
$thread->join();

var_dump($thread->value);

In pthreads v2, calling the ExampleThread::exclusive method from both the main context and the new thread context was safe. The value output at the end of the script would always be int(20000). But in pthreads v3, this value can be anything from 1 to 20000 due to race conditions between the two unsynchronized for loops.

In order to achieve the exact same behavior in pthreads v3, we must explicitly synchronize access using the built-in Threaded::synchronized method. This need only be applied to the body of the ExampleThread::exclusive method:

protected function exclusive()
{
    $this->synchronized(function () {
        for ($i = 0; $i < 10000; ++$i) {
            ++$this->value;
        }
    });
}

Continue reading %Upgrading from PThreads v2 to V3: What to Look out For%


by Thomas Punt via SitePoint

No comments:

Post a Comment