Consider the following small test case where $y is first made to be a
reference to $x. And next, we assign a literal to $y.
 
Example #1:
<?php
 
$x = 5;
$y = &$x;

$y = null;  <---
 
When we come to the third assignment statement above we try to increase
(followed quickly by a decrease) of the "refcount" on the right hand
side ZVAL (the null zval) in the following piece of code in
zend_assign_to_variable():
 
 
 } else if (PZVAL_IS_REF(variable_ptr)) {
  if (variable_ptr!=value) {
   zend_uint refcount = variable_ptr->refcount;
   zval garbage;
 
   if (type!=IS_TMP_VAR) {
    value->refcount++;                 <----- incrementing the refcount
of RHS zval
   }
   garbage = *variable_ptr;
   *variable_ptr = *value;
   variable_ptr->refcount = refcount;
   variable_ptr->is_ref = 1;
   if (type!=IS_TMP_VAR) {
    zendi_zval_copy_ctor(*variable_ptr);
    value->refcount--;                   <------ and then decrementing
it back...
   }
   zendi_zval_dtor(garbage);
  }
 }
 
I am trying to understand the rationale for value->refcount++ and
value->refcount-- above. Why do we need that?
 
With PHP running in apache+APC environment, the "null" literal is an
IS_CONST operand in the op_array in APC (shared memory). And
incrementing/decrementing a ref count on the shared memory area without
a mutex can cause unexpected problems due to race conditions (and
eventually lead to memory corruptions etc.)
 
Recall that zend_opcode.c:pass_two() sets "is_ref" on all IS_CONST
operands to 1, as a way to allow op_arrays to be sharable across
processes. And indeed, in the simpler case of an assignment such as in
the following example (#2), zend_assign_to_variable() notices that the
RHS has "is_ref" set and goes on to make a copy (rather than
incrementing the RHS's refcount).
 
Example #2:
 
<?php
$a = 5;
 
But in the case the LHS is itself an "is_ref" (like in the example #1)
it doesn't seem to make a similar check on the RHS (to see if it is safe
to modify the refcount on the RHS). Why?
 
 
regards,
Kannan

Reply via email to