Mantis - Quercus
Viewing Issue Advanced Details
2252 major always 12-13-07 13:57 12-18-07 13:23
koreth  
ferg  
normal  
closed 3.1.4  
fixed  
none    
none 3.1.5  
0002252: PHP file edits not always detected
Attached is a shell script that will create a bunch of PHP files. Load test.php in Quercus. Reload it a few times over the course of several seconds.

Then go and edit the "print" statement in one of the files pretty far down the include chain, e.g., 89.php.

When you reload test.php again, most of the time you will not see the change you just made reflected in the output. *Sometimes* you will, so you might need to repeat the test a few times, but most of the time the change goes undetected.

If, however, you edit test.php, the change there will immediately be noticed. And if you restart Resin, the changes to the include files will take effect.

"Don't check for changes to PHP files" is definitely a mode we will want for a production environment for performance reasons, but in a development environment it's a pain to have to restart Resin every time I edit a library .php file.
This happens both on OS X and Linux. Running latest Resin code from svn as of December 13 and pro.jar from the 2007/12/05 snapshot.
 mk.sh [^] (371 bytes) 12-13-07 13:57

Notes
(0002573)
koreth   
12-14-07 19:54   
Found the problem. In Env.importPage(), the code checks to see if it has a cached copy of the definition state from the page. But the definition state is cumulative, not per-page -- it includes the definition state for all the pages that have already been included at that point. So if you have an include chain like

file1 -> file2 -> file3

and file2 is modified, the definition state for file3 will include the *old* functions for file2, and it'll get used in preference to the version that was just reconstructed from file3.

The crux of the bug seems to be that the definition state CRC doesn't change when the contents of one of the functions changes. It assumes that a function name uniquely identifies a function, when in fact it's really the name plus the code. Since Quercus allocates a new object with a different ID when it does a fresh parse of a function, the object ID can be used as a proxy for the contents. Here's a quick patch that seems to fix the problem; it's not exactly elegant but might be adequate.

(Edited to call hashCode() instead of toString())

--- a/modules/quercus/src/com/caucho/quercus/env/DefinitionState.java
+++ b/modules/quercus/src/com/caucho/quercus/env/DefinitionState.java
@@ -284,7 +284,7 @@ public final class DefinitionState {
 
     copyOnWrite();
     _funMap.put(name, fun);
- _crc = Crc64.generate(_crc, name);
+ _crc = Crc64.generate(_crc, name + fun.hashCode());
 
     if (_lowerFunMap != null)
       _lowerFunMap.put(name.toLowerCase(), fun);
@@ -315,7 +315,7 @@ public final class DefinitionState {
 
     copyOnWrite();
     _funMap.put(name, fun);
- _crc = Crc64.generate(_crc, name);
+ _crc = Crc64.generate(_crc, name + fun.hashCode());
 
     if (_lowerFunMap != null)
       _lowerFunMap.put(lowerName, fun);
@@ -330,7 +330,7 @@ public final class DefinitionState {
   {
     copyOnWrite();
     _classDefMap.put(name, cl);
- _crc = Crc64.generate(_crc, name);
+ _crc = Crc64.generate(_crc, name + cl.hashCode());
 
     if (_lowerClassDefMap != null)
       _lowerClassDefMap.put(name.toLowerCase(), cl);

(0002581)
koreth   
12-17-07 10:22   
That fix isn't quite right; it seems to cause memory usage to increase over time, most likely because the obsolete definition states aren't being discarded. But even though the fix is inadequate, I think I've identified the underlying problem correctly in the above note.
(0002588)
ferg   
12-18-07 13:23   
php/0b36