]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java
Fixed memory leaks of SCL module listening systems
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / module / repository / ModuleRepository.java
index b851810e375a3f306a6224f7c2d8aa2691780c5e..7170b992b41d87be51e0e615d4723985bb03478e 100644 (file)
@@ -4,7 +4,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
@@ -24,6 +23,7 @@ import org.simantics.scl.compiler.errors.Success;
 import org.simantics.scl.compiler.module.ImportDeclaration;
 import org.simantics.scl.compiler.module.Module;
 import org.simantics.scl.compiler.module.options.ModuleCompilationOptionsAdvisor;
+import org.simantics.scl.compiler.module.repository.UpdateListener.Observable;
 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
 import org.simantics.scl.compiler.runtime.RuntimeEnvironmentImpl;
 import org.simantics.scl.compiler.runtime.RuntimeModule;
@@ -67,9 +67,9 @@ public class ModuleRepository {
         PENDING_MODULES.get().remove(moduleName);
     }
     
-    private class ModuleEntry implements UpdateListener {
+    private class ModuleEntry extends UpdateListener implements Observable {
         final String moduleName;
-        WeakHashMap<UpdateListener,Object> listeners = new WeakHashMap<UpdateListener,Object>();
+        THashSet<UpdateListener> listeners = new THashSet<UpdateListener>();
         
         ModuleSource source;
         Failable<Module> compilationResult;
@@ -80,14 +80,20 @@ public class ModuleRepository {
         }
         
         synchronized void addListener(UpdateListener listener) {
-            if(listener != null)
-                listeners.put(listener, null);
+            if(listener == null || listeners == null)
+                return;
+            listeners.add(listener);
+            listener.addObservable(this);
+        }
+
+        public synchronized void removeListener(UpdateListener listener) {
+            if (listeners == null)
+                return;
+            listeners.remove(listener);
         }
         
         @Override
         public void notifyAboutUpdate() {
-            if (listeners == null)
-                return;
             ArrayList<UpdateListener> externalListeners = new ArrayList<UpdateListener>();
             notifyAboutUpdate(externalListeners);
             for(UpdateListener listener : externalListeners)
@@ -95,18 +101,26 @@ public class ModuleRepository {
         }
 
         synchronized void notifyAboutUpdate(ArrayList<UpdateListener> externalListeners) {
+            stopListening();
+            if (listeners == null)
+                return;
             if(moduleCache.get(moduleName) == this) {
                 moduleCache.remove(moduleName);
                 if(SCLCompilerConfiguration.TRACE_MODULE_UPDATE) {
                     System.out.println("Invalidate " + moduleName);
-                    for(UpdateListener l : listeners.keySet())
+                    for(UpdateListener l : listeners)
                         System.out.println("    " + l);
                 }
-                for(UpdateListener l : listeners.keySet())
+                THashSet<UpdateListener> listenersCopy = listeners;
+                listeners = null;
+                for(UpdateListener l : listenersCopy)
+                    l.stopListening();
+                for(UpdateListener l : listenersCopy)
                     if(l instanceof ModuleEntry)
                         ((ModuleEntry)l).notifyAboutUpdate(externalListeners);
-                    else
+                    else {
                         externalListeners.add(l);
+                    }
             }
         }
 
@@ -176,10 +190,11 @@ public class ModuleRepository {
             return runtimeModule;
         }
 
-        public void dispose() {
+        public synchronized void dispose() {
             if (listeners != null)
                 listeners.clear();
             listeners = null;
+            stopListening();
             source = null;
             compilationResult = null;
             if (runtimeModule != null) {
@@ -219,7 +234,7 @@ public class ModuleRepository {
     public Failable<RuntimeModule> getRuntimeModule(String moduleName) {
         return getRuntimeModule(moduleName, null);
     }
-    
+
     private ModuleEntry getModuleEntry(String moduleName, UpdateListener listener) {
         /* It is deliberate that the following code does not try to prevent
          * simultaneous compilation of the same module. This is because in
@@ -244,13 +259,16 @@ public class ModuleRepository {
         THashMap<String, ModuleEntry> result = new THashMap<String, ModuleEntry>();
         Collection<ImportFailure> failures = null;
         
+        THashSet<String> originalImports = new THashSet<String>(); 
         ArrayList<ImportDeclaration> stack = new ArrayList<ImportDeclaration>(imports.length);
-        for(ImportDeclaration import_ : imports)
+        for(ImportDeclaration import_ : imports) {
             stack.add(import_);
+            originalImports.add(import_.moduleName);
+        }
         while(!stack.isEmpty()) {
             ImportDeclaration import_ = stack.remove(stack.size()-1);
             if(!result.containsKey(import_.moduleName)) {
-                ModuleEntry entry = getModuleEntry(import_.moduleName, listener);
+                ModuleEntry entry = getModuleEntry(import_.moduleName, originalImports.contains(import_.moduleName) ? listener : null);
                 Failable<Module> compilationResult = entry.compilationResult;
                 if(compilationResult.didSucceed()) {
                     result.put(import_.moduleName, entry);