gecko-dev/js
nboyd%atg.com fdeea4b93b Subject:
Re: Rhino1.5.R1: problems with multithreaded embedded application.
        Date:
             Mon, 03 Jul 2000 14:38:56 -0400
       From:
             Norris Boyd <nboyd@atg.com>
 Organization:
             Art Technology Group
         To:
             Fergus Gallagher <Fergus.Gallagher@orbisuk.com>
 Newsgroups:
             netscape.public.mozilla.jseng
  References:
             1




You found a bug in Rhino; I wonder if others have been running into the same thing.

The problem is with a class called LazilyLoadedCtor. I wrote this class to reduce the
time
required by initStandardObjects by only creating standard objects when the associated
constructors are first accessed. The problem is that this class was not threadsafe.
I've
made changes to that class, and to ScriptableObject as well. The design of dynamic
properties calling getters and setters (which LazilyLoadedCtor uses) didn't really
allow
any way for the getter/setter to replace itself without a thread hazard. I've now
extended
setters so that they can return a value which replaces the getter/setter to avoid this
problem.

Thanks for finding this problem. There have been a couple of other reports of problems
in
this area, so I hope this will fix them.

The patch follows.

--N

Index: LazilyLoadedCtor.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/org/mozilla/javascript/LazilyLoadedCtor.java,v
retrieving revision 1.1
diff -u -r1.1 LazilyLoadedCtor.java
--- LazilyLoadedCtor.java 2000/02/29 21:34:37 1.1
+++ LazilyLoadedCtor.java 2000/07/03 18:31:03
@@ -58,9 +58,12 @@
     }

     public Object getProperty(ScriptableObject obj) {
-        obj.delete(ctorName);
         try {
-            ScriptableObject.defineClass(obj, Class.forName(className));
+            synchronized (obj) {
+                if (!isReplaced)
+                    ScriptableObject.defineClass(obj, Class.forName(className));
+                isReplaced = true;
+            }
         }
         catch (ClassNotFoundException e) {
             throw WrappedException.wrapException(e);
@@ -83,11 +86,14 @@
         return obj.get(ctorName, obj);
     }

-    public void setProperty(ScriptableObject obj, Object val) {
-        obj.delete(ctorName);
-        obj.put(ctorName, obj, val);
+    public Object setProperty(ScriptableObject obj, Object val) {
+        synchronized (obj) {
+            isReplaced = true;
+            return val;
+        }
     }

     private String ctorName;
     private String className;
+    private boolean isReplaced;
 }
Index: ScriptableObject.java
===================================================================
RCS file: /cvsroot/mozilla/js/rhino/org/mozilla/javascript/ScriptableObject.java,v
retrieving revision 1.17
diff -u -r1.17 ScriptableObject.java
--- ScriptableObject.java 2000/03/13 17:12:36 1.17
+++ ScriptableObject.java 2000/07/03 18:31:04
@@ -246,11 +246,21 @@
                             break;
                         }
                     }
-                    getterSlot.setter.invoke(start, arg);
+                    Object v = getterSlot.setter.invoke(start, arg);
+                    if (getterSlot.setterReturnsValue) {
+                        slots[slotIndex].value = v;
+                        if (!(v instanceof Method))
+                            slots[slotIndex].flags = 0;
+                    }
                     return;
                 }
                 Object[] args = { this, actualArg };
-                getterSlot.setter.invoke(getterSlot.delegateTo, args);
+                Object v = getterSlot.setter.invoke(getterSlot.delegateTo, args);
+                if (getterSlot.setterReturnsValue) {
+                    slots[slotIndex].value = v;
+                    if (!(v instanceof Method))
+                        slots[slotIndex].flags = 0;
+                }
                 return;
             }
             catch (InvocationTargetException e) {
@@ -1183,6 +1193,7 @@
         slot.delegateTo = delegateTo;
         slot.getter = getter;
         slot.setter = setter;
+        slot.setterReturnsValue = setter != null && setter.getReturnType() !=
Void.TYPE;
         slot.value = null;
         slot.attributes = (short) attributes;
         slot.flags = flags;
@@ -1551,6 +1562,7 @@
     Object delegateTo;  // OPT: merge with "value"
     Method getter;
     Method setter;
+    boolean setterReturnsValue;
 }




Fergus Gallagher wrote:

> I am having problems getting my head around contexts and scopes and my
> multi-threaded application fall over.
>
> If I set "global=false" the following code used a per-thread
> initStandardObject() and this seems to work.  But when I set
> "global=true", the global "parentScope" is used and I get some wierd
> errors.
>
> If I change "__CODE__.slice(0,5)" to
> 1. "__CODE__" - works
> 2. "__CODE__.substring(0,5)" - fails
> 3. "__CODE__.toString()" - works
>
> Any help appreciated.
>
> Fergus
>
> ===== Test.java =========================================
> import java.io.*;
> import org.mozilla.javascript.*;
>
> public class Test implements Runnable {
>         private Script script;
>         private Scriptable parentScope;
>         private String __CODE__="ABCDEFGHIJK";
>         private boolean global = true;
>         private static Context globalContext = null;
>         public Test() throws Exception {
>                 String js= "java.lang.System.out.println(__CODE__.slice(0,5));";
>                 globalContext.setCompileFunctionsWithDynamicScope(false);
>                 parentScope = globalContext.initStandardObjects(null);
>                 StringReader sr = new StringReader(js);
>                 script = globalContext.compileReader(parentScope, sr, "(compiled)",
> 1,null);
>         }
>
>         public void run() {
>                 try {
>                         Context context = Context.enter();
>                         Scriptable threadScope;
>                         if (global) {
>                                 threadScope = context.newObject(parentScope);
>                                 threadScope.setPrototype(parentScope);
>                                 threadScope.setParentScope(null);
>                         } else {
>                                 threadScope = context.initStandardObjects(null);
>                         }
>                         threadScope.put("__CODE__",threadScope,__CODE__);
>                         script.exec(context,threadScope);
>                 }
>                 catch (Exception e) {
>                         System.err.println(e.getClass().getName()+":
"+e.getMessage());
>                 }
>                 finally {
>                         Context.exit();
>                 }
>         }
>
>         public static void main(String args[]) throws Exception {
>         globalContext = Context.enter();
>                 Test me = new Test();
>                 int count=10;
>                 Thread[] threads = new Thread[count];
>                 for (int i=0; i<count; i++) {
>                         Thread t = new Thread(me);
>                         threads[i] = t;
>                         t.start();
>                 }
>                 for (int i=0; i<count; i++) {
>                         threads[i].join();
>                 }
>                 Context.exit();
>         }
> }
>
> ==== OUTPUT ===============================================
> ABCDE
> ABCDE
> org.mozilla.javascript.EcmaError: undefined is not a function.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> org.mozilla.javascript.EvaluatorException: Constructor for "String" not
> found.
> ===========================================================
>
> The number and type of exceptions is highly variable from run to run -
> anywhere from 1-9 out of 10.
> The EcmaError in particular only happens occasionally.
>
> --
> Fergus Gallagher          Tel: +44 (20) 8 987 0717
> Orbis                     Fax: +44 (20) 8 742 2649
> The Swan Centre           email: Fergus.Gallagher@orbisuk.com
> Fishers Lane              Web: http://www.orbisuk.com
> London W4 1RX / UK
2000-07-03 18:40:35 +00:00
..
benchmarks Add benchmarks. 1999-06-04 18:44:17 +00:00
js2 Fix build bustage. 2000-06-29 18:40:58 +00:00
jsd NOT PART OF SEAMONKEY BUILD - removed misplaced semi-colon added by toshok on 6 Sep 1999 which had converted a conditional block into a non-conditional block and made the debugger completely non functional 2000-02-19 02:34:05 +00:00
jsdj updated license boilerplate to xPL 1.1, a=chofmann@netscape.com,r=endico@mozilla.org 1999-11-06 02:47:15 +00:00
jsj Deleting obsolete contents of jsj directory 1998-09-24 00:32:24 +00:00
macbuild Add nsIScriptError.idl. Build it here rather than back in xpfe/components/console. Checkin by McCabe, on rginda's mac. 2000-04-21 00:57:27 +00:00
ref The files in this directory are all obsolete. JavaScript development 1998-10-16 22:18:00 +00:00
rhino Subject: 2000-07-03 18:40:35 +00:00
semantics Simplified grammar without changing syntax 2000-05-13 00:18:56 +00:00
src updating readme. Not part of build 2000-07-02 19:37:57 +00:00
tests Fix missing $ bug when testing test_dir. 2000-06-03 19:20:03 +00:00
.cvsignore more anal tricks. 1999-06-26 05:01:40 +00:00
Makefile.in updated license boilerplate to xPL 1.1, a=chofmann@netscape.com,r=endico@mozilla.org 1999-11-06 03:40:37 +00:00
landbranch.pl Tweaks 1999-04-27 05:08:14 +00:00
makefile.win updated license boilerplate to xPL 1.1, a=chofmann@netscape.com,r=endico@mozilla.org 1999-11-06 03:40:37 +00:00