ID:               49010
 User updated by:  benjamin at josefus dot net
 Reported By:      benjamin at josefus dot net
-Status:           Feedback
+Status:           Open
 Bug Type:         SPL related
 Operating System: Ubuntu 9.04, Kernel2.6.27
 PHP Version:      5.3SVN-2009-07-21 (snap)
 New Comment:

In fact, there ain't no single word defining that spl_object_hash()
should work in forked environments. For sure! 
But if an instance of class "Foo" is created BEFORE forking, shouldn't
the object's ID be the same, in both forked and parent processes? 

Therefore, I expected the unique ID to be created for an object on
contruct-Time. But this is not happening. It will be assigned at first
time calling spl_object_hash() for an object. This is quite unsafe in
distributed and concurrent enviroments. 


A simple functional example below:
---------------------------------
class SharedObject
{
    public function __construct($callSplObjectHash = false)
    {
        if ($callSplObjectHash) 
            spl_object_hash($this);
    }
}

abstract class Task
{
    protected $_pid;

    protected $_isChild;

    public function __construct()
    {   
        $this->_isChild = false;        
    }
        
    /**
     * Starts this Task running
     */
    public function start() 
    {
        // try to fork and acquire PID
        try {
            $pid = pcntl_fork();
            if ($pid === 0) {    // in chid process
                self::sleep(10); // sleep and release CPU after Fork
                $this->_isChild = true;                 
                $this->run();    // starts the logic routine
                exit();
            } else if ($pid){    // in parent process
                $this->_pid = $pid;
                $this->_isChild = false;
            } else {
                throw new Excpetion('Could not do pcntl_fork().
Negative Return Value');
            }
        } catch (Exception $e) {
            throw new Exception('Unable to Start Task. Cause: ' .
$e->getMessage());
        }
    }

    public static function sleep($millisecs)
    {
        usleep($millisecs * 1000);
    }

    abstract protected function run();
} 

class Producer extends Task
{
    /**
     * @var SharedObject
     */
    protected $o;
    
    public function __construct(SharedObject $o)
    {
        $this->o = $o;
    }

    protected function run()
    {
        echo 'Producer::run()  ' . spl_object_hash($this->o) .
PHP_EOL;
    }
}
        

class Worker extends Task
{
    /**
     * @var SharedObject
     */
    protected $o;
    
    public function __construct(SharedObject $o)
    {
        $this->o = $o;
    }
        
    protected function run()
    {
        echo 'Worker::run()    ' . spl_object_hash($this->o) .
PHP_EOL;
    }
} 

// without spl_object_hash() called in Constructor
$s = new SharedObject();
$p = new Producer($s);
$w = new Worker($s);
$w->start();
$p->start();

// with spl_object_hash() called in Constructor
$s = new SharedObject(true);
$p = new Producer($s);
$w = new Worker($s);
$w->start();
$p->start();


Previous Comments:
------------------------------------------------------------------------

[2009-07-21 22:13:04] j...@php.net

Where does it say that this function returns unique hash for objects
run 
from forked PHP instances? And if you claim it should, provide a short

but complete(!) reproducing script..

------------------------------------------------------------------------

[2009-07-21 21:12:13] benjamin at josefus dot net

This is the result with spl_object_hash() invoked first off (as it
should be):

not yet shared, initialize SHM 0000000016c4f93000000000506bf64a
Worker:   --> Wait!, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 0, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 0, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 1, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 1, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 2, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 2, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 3, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 3, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 4, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 4, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Wait!, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 5, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 5, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 6, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 6, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 7, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 7, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 8, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 8, ID: 0000000016c4f93000000000506bf64a
Producer: --> Producing 9, ID: 0000000016c4f93000000000506bf64a
Worker:   --> Working on 9, ID: 0000000016c4f93000000000506bf64a


This one happens when spl_object_hash() has not been invoked first 
off:

not yet shared, initialize SHM 0000000051f9a9a50000000056018292
not yet shared, initialize SHM 00000000187ccfd60000000067032181
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 0, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 1, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 2, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 3, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 4, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 5, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 6, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 7, ID: 0000000051f9a9a50000000056018292
Worker:   --> Wait!, ID: 00000000187ccfd60000000067032181
Producer: --> Producing 8, ID: 0000000051f9a9a50000000056018292
Producer: --> Producing 9, ID: 0000000051f9a9a50000000056018292)

------------------------------------------------------------------------

[2009-07-21 20:54:56] benjamin at josefus dot net

Description:
------------
I am working on a Task Management (java-like Threads) where each Task 
implements a pcntl_fork(), which works with shared Objects using IPC and
Semaphore to access concurrently and synchronized. 
The Synchronisation should happen by unique Key per Object and SHM
Segment (delivered by spl_object_hash). I am using sem_acquire(),
sem_release(), shm_attach(), shm_release(), shm_[put|get]_var() and so
on. 

When providing two concurrent Tasks with the same Object to be shared,
spl_object_hash delivers two different Hashes, when invoked in each
Task.

Interestingly, if i call spl_object_hash for the shared Object before
it is assigned to both "Producer" and "Worker" Task, the Hash IS unique,
as it should be!

In short: 
- new Object 
- fork -> spl_object_hash(Object) is NOT unique

- new Object 
- call spl_object_hash(Object) first off 
- fork -> spl_object_hash(Object) IS unique and correct

System Notes:
PHP 5.3.0 (cli) (built: Jul 21 2009 22:19:17)
Configure Command       './configure' '--enable-pcntl' '--enable-shmop'
'--enable-sysvmsg' '--enable-sysvsem' '--enable-sysvshm'
'--with-apxs2=/usr/bin/apxs2' '--disable-short-tags' '--with-openssl'
'--with-zlib' '--enable-bcmath' '--with-bz2=/bin/bzip2'
'--enable-calendar' '--with-curl' '--with-curlwrappers' '--enable-exif'
'--enable-ftp' '--with-gd' '--with-jpeg-dir=/usr/lib'
'--with-png-dir=/usr/lib' '--with-xpm-dir=/usr/lib' '--with-t1lib'
'--enable-gd-native-ttf' '--enable-gd-jis-conv' '--with-gettext'
'--with-imap' '--with-imap-ssl' '--with-ldap' '--with-ldap-sasl'
'--enable-mbstring' '--with-mcrypt' '--with-mhash'
'--with-mysql=mysqlnd' '--with-mysqli=mysqlnd' '--with-pdo-mysql'
'--with-pspell' '--with-readline' '--with-snmp' '--enable-soap'
'--enable-sockets' '--without-sqlite' '--enable-sqlite-utf8'
'--with-tidy' '--enable-wddx' '--with-xmlrpc' '--with-xsl'
'--enable-zip' '--with-pear' '--with-kerberos' '--with-pgsql'
'--with-pdo-pgsql' 

Reproduce code:
---------------
$shared = new MySharedObj();
spl_object_hash($shared); //toggle this line to for in/correct result 


/**
 * $shared is referenced member variable in each task
 */
$producer = new ProducerTask($shared); 
$worker = new WorkerTask($shared);      

$producer->start();
$worker->start(); //outputs different Hash for $shared provided by
spl_object_hash



------------------------------------------------------------------------


-- 
Edit this bug report at http://bugs.php.net/?id=49010&edit=1

Reply via email to