This is the mail archive of the kawa@sources.redhat.com mailing list for the Kawa project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Gargage collected top-level bindings?


Per Bothner wrote:
Dean Ferreyra wrote:

I've seen this behavior when initializing and using Kawa modules from Java directly, in threads that were created from Java but not with (future ...). When the thread that has initialized the module ends, some binding-related thread-local objects are garbage collected, the symbols are unbound, and from then on the module's global bindings give the UnboundLocationException exception.

Assuming that we're seeing the same problem, attached is my temporary kludge around it.


Note that I'm not sure this is a bug in Kawa, and I'm fairly sure this
temporary kludge is *not* The Right Thing.

Kawa "global" bindings are fluid and thread-local, so if you create a
binding in one thread, and the thread ends goes away, then so does the
binding.  If you have a static module, you might still be able to
reference the ThreadLocation from a different thread, but it will be
unbound.

Using the dynamic environment to access bidnings from a static module
initialized by a diffferent thread is going to be tricky, and it's
not obvious what the "right" behavior is.  If you provide some more
details perhaps we can figure out a solution.

I've attached a small (though a bit convoluted) test case for the problem I was seeing. Compiled and run with a version of Kawa from August, the test case runs without a hitch:


----------------------------------------
% make OLDPURE=1 default
rm -f *.class
CLASSPATH=.:~/kawa/cvs/kawa-pure-8-31-2004-build/kawa-1.7.90.jar:/usr/local/java/jre/lib/rt.jar jikes Launch.java
CLASSPATH=.:~/kawa/cvs/kawa-pure-8-31-2004-build/kawa-1.7.90.jar:/usr/local/java/jre/lib/rt.jar jikes Start.java
CLASSPATH=.:~/kawa/cvs/kawa-pure-8-31-2004-build/kawa-1.7.90.jar java kawa.repl --warn-undefined-variable -C ModA.scm
(compiling ModA.scm)
CLASSPATH=.:~/kawa/cvs/kawa-pure-8-31-2004-build/kawa-1.7.90.jar java Start
m: 1, Thread[Thread-0,5,main], hello-world
m: 1, Thread[Thread-2,5,main], hello-world
----------------------------------------


With the latest Kawa I get the UnboundLocationException exception:

----------------------------------------
% make NEW=1 default
rm -f *.class
CLASSPATH=.:~/kawa/cvs/kawa-pure-build/kawa-1.7.91.jar:/usr/local/java/jre/lib/rt.jar jikes Launch.java
CLASSPATH=.:~/kawa/cvs/kawa-pure-build/kawa-1.7.91.jar:/usr/local/java/jre/lib/rt.jar jikes Start.java
CLASSPATH=.:~/kawa/cvs/kawa-pure-build/kawa-1.7.91.jar java kawa.repl --warn-undefined-variable --module-static-run -C ModA.scm
(compiling ModA.scm)
CLASSPATH=.:~/kawa/cvs/kawa-pure-build/kawa-1.7.91.jar java Start
m: 1, Thread[Thread-0,5,main], hello-world
m: Error: gnu.mapping.UnboundLocationException: unbound location VAR (property (dynamic)), thread: Thread[Thread-2,5,main]
modAFn error: java.lang.reflect.InvocationTargetException
----------------------------------------


Our code base loads Java and Kawa modules at run time mostly based on settings found in configuration files. We have threads that start life outside of the Kawa environment (i.e., without using "future") that eventually call into Kawa modules; and we have code paths where some of these same modules are first initialized from "require" from within other Kawa modules.

Per, you suggested (in an off-list email) making sure all initialization happens in a parent thread and ensuring that other threads inherit from this parent thread. That make sense to me, though in our case that might be quite painful to enforce; I'll have to take a closer look.

Dean
--- /dev/null	Tue Oct  7 04:48:06 2003
+++ Launch.java	Wed Apr 13 15:48:35 2005
@@ -0,0 +1,45 @@
+import java.lang.reflect.*;
+
+public class Launch implements Runnable {
+    public static Launch instance;
+
+    public static void init() {
+        instance = new Launch();
+
+        // Initialize Kawa?
+        // kawa.standard.Scheme.registerEnvironment();
+
+        // Start thread
+        (new Thread(instance)).start();
+
+        // Garbage collect
+        try { Thread.sleep(2000); } catch (Throwable e) { }
+        System.gc();
+        
+        // Start thread
+        (new Thread(instance)).start();
+    }
+
+    public void run()
+    {
+        // New thread
+
+        // Initialize Kawa
+        kawa.standard.Scheme.registerEnvironment();
+
+        // Call Kawa module
+        modAFn();
+    }
+
+    public static void modAFn()
+    {
+        // Call into Kawa module
+        try {
+            Class modA = Class.forName("ModA");
+            Method fn = modA.getMethod("fn", null);
+            fn.invoke(null, null);
+        } catch (Throwable e) {
+            System.err.println("modAFn error: " + e);
+        }
+    }
+}
--- /dev/null	Tue Oct  7 04:48:06 2003
+++ Start.java	Wed Apr 13 15:11:10 2005
@@ -0,0 +1,8 @@
+import java.lang.reflect.*;
+
+public class Start {
+    public static void main(String[] strings) {
+        // Launch launcher
+        Launch.init();
+    }
+}
--- /dev/null	Tue Oct  7 04:48:06 2003
+++ ModA.scm	Wed Apr 13 15:16:13 2005
@@ -0,0 +1,19 @@
+(module-static #t)
+
+(define VAR 'hello-world)
+
+(define (fn)
+  (try-catch
+   (begin
+     (invoke (static-field <java.lang.System> 'err)
+             'println
+             (format "m: 1, ~A, ~A"
+                     (invoke-static <java.lang.Thread> 'currentThread)
+                     VAR)))
+   (e <java.lang.Throwable>
+      (invoke (static-field <java.lang.System> 'err)
+              'println
+              (format "m: Error: ~A, thread: ~A"
+                      e
+                      (invoke-static <java.lang.Thread> 'currentThread)))
+      (throw e))))

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]