pmmp / ext-pmmpthread

Fork of https://github.com/krakjoe/pthreads with a revamped API and PHP 8.1+ support
Other
82 stars 15 forks source link

Closure() : [Closure() use (&$ref)] terminate the process #121

Closed Blackjack200 closed 1 year ago

Blackjack200 commented 1 year ago

Environment

PHP:

PHP 8.2.6 (cli) (built: Jun  4 2023 03:34:43) (ZTS Visual C++ 2019 x64)
Copyright (c) The PHP Group
Zend Engine v4.2.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.6, Copyright (c), by Zend Technologies 

Reproducing Code

<?php

use pmmp\thread\Thread;

class A extends Thread {
    public function __construct(
        private Closure $x
    ) {
    }

    public function run() : void {
        ($this->x)()();
    }
}

$t = (new A(static fn() => static function() : void {
    echo "HI 1\n";
}));
$t->start(Thread::INHERIT_ALL);
$t->join();

$t = (new A(static fn() => static function() use (&$u) : void {
    echo "HI 2\n";
}));
$t->start(Thread::INHERIT_COMMENTS);
$t->join();

Expected Output

HI 1

Fatal error: Uncaught pmmp\thread\NonThreadSafeValueError: Closures with local static variables or use-by-reference cannot be made thread-safe in 

or something like


Fatal error: Uncaught pmmp\thread\NonThreadSafeValueError: Closures with local static variables or use-by-reference cannot be made thread-safe in 

Actual Output

in PHPStorm:

D:\php\php.exe test.php
HI 1

Process finished with exit code -1073741819 (0xC0000005)
dktapps commented 1 year ago

$u is not defined anywhere

Blackjack200 commented 1 year ago

$u is not defined anywhere

Of course,

$u = 1;
$t = (new A(static fn() => static function() use (&$u) : void {
    echo "HI 2\n";
    var_dump($u);
    $u=10;
}));
$t->start(Thread::INHERIT_COMMENTS);
$t->join();
var_dump($u);

this outputs

HI 1
HI 2
int(1)
int(1)

&$u is a copy of $u

dktapps commented 1 year ago

Can you try disabling xdebug (remove the line zend_extension=xdebug.so from php.ini)?

dktapps commented 1 year ago

This happens because an IS_UNDEF zval appears in the static_variables table, which is quietly skipped by this foreach:

https://github.com/pmmp/ext-pmmpthread/blob/95bf23c9e309c5f945d7d8fa5c5b291b54365fa7/src/copy.c#L104

Technically speaking, it shouldn't actually make it this far in the first place (evidently we are missing nested closure thread-safety checks), but this bug will probably show up in other places too.