Revision: 4687
          http://sourceforge.net/p/vexi/code/4687
Author:   mkpg2
Date:     2014-04-18 00:35:32 +0000 (Fri, 18 Apr 2014)
Log Message:
-----------
Fix. vexi.js.loadImage (was not restarting interpreter correctly).
Fix. Image Caching. Cache key included fountain hash, and so was reloading url 
images each time.
SoftReferenceCache. Use to store images, will periodically purge keys to avoid 
unbounded growth when dealing with arbitrary numbers of images (e.g. photo 
albums).

Modified Paths:
--------------
    
branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/Picture.java
    branches/vexi3/org.vexi-core.main/src/main/jpp/org/vexi/core/Vexi.jpp
    branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java
    branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Thread.java
    trunk/org.vexi-library.util/meta/module.xml

Added Paths:
-----------
    
trunk/org.vexi-library.util/src/main/java/org/vexi/util/SoftReferenceCache.java

Modified: 
branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/Picture.java
===================================================================
--- 
branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/Picture.java  
    2014-04-17 18:22:57 UTC (rev 4686)
+++ 
branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/Picture.java  
    2014-04-18 00:35:32 UTC (rev 4687)
@@ -7,17 +7,19 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PushbackInputStream;
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
 
+import org.ibex.js.Fountain;
 import org.ibex.js.JS;
 import org.ibex.js.JSExn;
 import org.ibex.js.JSU;
+import org.ibex.js.Scheduler;
+import org.ibex.js.Thread;
 import org.ibex.util.Callable;
 import org.ibex.util.Vec;
 import org.vexi.core.Blessing;
 import org.vexi.core.Main;
 import org.vexi.plat.Platform;
+import org.vexi.util.SoftReferenceCache;
 
 /** 
  *    The in-memory representation of a PNG or GIF image. It is
@@ -30,38 +32,8 @@
  */
 public class Picture {
 
-    public Picture() { this.stream = null; }
-    public Picture(JS r) { this.stream = r; }
-    private static HashMap cache = new HashMap(100);
-
-    public JS stream = null;                       ///< the stream we were 
loaded from
-    public int width = -1;                         ///< the width of the image
-    public int height = -1;                        ///< the height of the image
-    public int[] data = null;                      ///< argb samples
-    public boolean isLoaded = false;               ///< true iff the image is 
fully loaded
-    // FIXME: this is a hack to disguise isLoaded somehow being false when 
callbacks are run
-    public JSExn loadFailed = null;                ///< not null iff the image 
loading failed
-
-    private Vec loadedCallbacks;                   ///< list of callbacks 
interested in this Picture
-    
-    /** invoked when an image is fully loaded; subclasses can use this to 
initialize platform-specific constructs */
-    protected synchronized void loaded() {
-        if (loadedCallbacks == null) {
-            return;
-        }
-        
-        synchronized (worker) {
-            if (loadedPictures != null) {
-                loadedPictures.addElement(this);
-                return;
-            }
-
-            loadedPictures = new Vec();
-            loadedPictures.addElement(this);
-            Main.SCHEDULER.add(worker);
-        }
-    }
-    
+       static final SoftReferenceCache<String,Picture> cache = new 
SoftReferenceCache();
+         
     /*
      *  The 'worker' Callable invokes the other Callables; needed
      *  to be done this way because if using a lot of images, there
@@ -69,8 +41,8 @@
      *  individually, and this can lead to 'step by step' building
      *  of the UI - not really ideal.
      */
-    private static Vec loadedPictures;
-    private static Callable worker = new Callable() {
+    static private Vec loadedPictures;
+    static private Callable worker = new Callable() {
         public Object run(Object o) throws Exception {
             synchronized (worker) {
                 for (int j=0; loadedPictures.size()>j; j++) {
@@ -88,9 +60,16 @@
             }
         }
     };
+    
+    static String cacheKey(JS stream){
+       if(stream instanceof Fountain){
+               return ((Fountain)stream).canonical();
+       }
+       return stream.coerceToString();
+    }
 
     /** turns a stream into a Picture.Source and passes it to the callback */
-    public static Picture load(JS stream, Callable callback) {
+    static public Picture load(JS stream, Callable callback) {
         // REMARK - as things stand we cannot get the underlying stream object 
as 
         // due to the way blessings work (they discover their suffix) we can be
         // opening many inputstreams on the same file, if an image is used in 
many
@@ -99,19 +78,65 @@
         // We construct a key here. This should be unique for different 
streams and blessings.
         // The construction of the key could be improved upon, if we can make 
sure
         // that it is unique/underlying resource then that would be ideal.
-        String key = stream.coerceToString();
-        SoftReference ref = (SoftReference)cache.get(key);
-        Picture ret = ref==null ? null : (Picture)ref.get();
-        // FIXME: is this a small memory leak? JS streams will not be GC'd.
+        String key = cacheKey(stream);
+        Picture ret = cache.get(key);
         if (ret==null) {
-            cache.put(key, new SoftReference(ret = 
Platform.createPicture(stream)));
+            cache.cache(key, ret = Platform.createPicture(stream));    
         }
         // can return ret here outside of sync block for caller to assign as 
callback
         // is executed in the same interpreter as the caller, synchronously.
         ret.load(callback);
         return ret;
     }
+       
+    static public Picture load(JS[] args) throws JSExn {
+       final Scheduler sched = Scheduler.findCurrent();
+       final Thread callback = sched.pauseJSThread();
+       Picture p = Picture.load(args[0], new Callable(){
+               public Object run(Object o) throws JSExn {
+                       Picture p = (Picture)o;
+                       sched.schedule(callback, p.loadFailed);
+                       return null;
+               }
+       });
+       if(p.isLoaded){
+               sched.schedule(callback, null);
+       }
+       return null;
+    }
+       
+    public Picture() { this.stream = null; }
+    public Picture(JS r) { this.stream = r; }
+
+    public JS stream = null;                       ///< the stream we were 
loaded from
+    public int width = -1;                         ///< the width of the image
+    public int height = -1;                        ///< the height of the image
+    public int[] data = null;                      ///< argb samples
+    public boolean isLoaded = false;               ///< true iff the image is 
fully loaded
+    // FIXME: this is a hack to disguise isLoaded somehow being false when 
callbacks are run
+    public JSExn loadFailed = null;                ///< not null iff the image 
loading failed
+
+    private Vec loadedCallbacks;                   ///< list of callbacks 
interested in this Picture
     
+    /** invoked when an image is fully loaded; subclasses can use this to 
initialize platform-specific constructs */
+    protected synchronized void loaded() {
+        if (loadedCallbacks == null) {
+            return;
+        }
+        
+        synchronized (worker) {
+            if (loadedPictures != null) {
+                loadedPictures.addElement(this);
+                return;
+            }
+
+            loadedPictures = new Vec();
+            loadedPictures.addElement(this);
+            Main.SCHEDULER.add(worker);
+        }
+    }
+  
+    
     /** turns a stream into a Picture.Source and once loaded invokes the
      *  given notification callback (which is added to the Scheduler)
      *  NOTE: the callback is not invoked if picture is already loaded */

Modified: branches/vexi3/org.vexi-core.main/src/main/jpp/org/vexi/core/Vexi.jpp
===================================================================
--- branches/vexi3/org.vexi-core.main/src/main/jpp/org/vexi/core/Vexi.jpp       
2014-04-17 18:22:57 UTC (rev 4686)
+++ branches/vexi3/org.vexi-core.main/src/main/jpp/org/vexi/core/Vexi.jpp       
2014-04-18 00:35:32 UTC (rev 4687)
@@ -7,6 +7,7 @@
 
 import org.ibex.crypto.MD5;
 import org.ibex.js.*;
+import org.ibex.js.Thread;
 import org.ibex.js.JSU.*;
 import org.vexi.js.*;
 import org.ibex.util.Cache;
@@ -787,18 +788,8 @@
                     ret.put(JSU.S("right"), JSU.N(s.rightInset));
                     return ret;
                 case "ui.loadImage":
-                       final Scheduler sched = Scheduler.findCurrent();
-                       final Callable callback = sched.pauseJSThread();
-                       Picture.load(args[0], new Callable(){
-                               public Object run(Object o) throws Exception {
-                                       Picture p = (Picture)o;
-                                       // if loadFailed then interpreter will 
handle the exception
-                                       callback.run(p.loadFailed); 
-                                       return null;
-                               }
-                       });
-                       return null; // doesn't matter since we paused
-                       
+                       Picture.load(args);
+                       return null;                    
                 case "unclone": return args[0].unclone();
                 //#end
                 break;

Modified: 
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 
2014-04-17 18:22:57 UTC (rev 4686)
+++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 
2014-04-18 00:35:32 UTC (rev 4687)
@@ -503,7 +503,7 @@
     // we do not deal with potential parent interpreters when we reschedule.
     // A JSExn complaining about the foreground thread will be thrown, but this
     // will not be accurate. (e.g. load template from a background thread)
-    public Callable pauseJSThread() throws JSExn {
+    public Thread pauseJSThread() throws JSExn {
         jsthread.currentInterpreter.pause();
         return jsthread;
     }

Modified: 
branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Thread.java
===================================================================
--- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Thread.java    
2014-04-17 18:22:57 UTC (rev 4686)
+++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Thread.java    
2014-04-18 00:35:32 UTC (rev 4687)
@@ -50,8 +50,9 @@
                //Log.info("thread destroyed "+threadCount+"     "+id +"     "+ 
this);
        }
        
-       /** Execute JS code in the background. Method executed by scheduler.  */
-       public Object run(Object o) throws Exception {
+       /** Execute JS code in the background. Method executed by scheduler.  
+        * @throws JSExn */
+       public Object run(Object o) throws JSExn {
                faction.setJSThread(this);
                if (currentInterpreter==null) {
                        //First time this thread has been run (i.e. not paused 
or yielded yet)

Modified: trunk/org.vexi-library.util/meta/module.xml
===================================================================
--- trunk/org.vexi-library.util/meta/module.xml 2014-04-17 18:22:57 UTC (rev 
4686)
+++ trunk/org.vexi-library.util/meta/module.xml 2014-04-18 00:35:32 UTC (rev 
4687)
@@ -3,7 +3,7 @@
        <artifact name="java.zip" />
     
     <dependencies>
-               <system  name="java.jre"        tag="1.4"/>
+               <system  name="java.jre"        tag="1.6"/>
            <!-- Test dependencies -->
            <dependency source="local"   org="org.vexi"            
name="library.testing"                                      scope="test"/>
            <dependency source="ibiblio" org="junit"               name="junit" 
                                 tag="3.8.1"   scope="test"/>

Added: 
trunk/org.vexi-library.util/src/main/java/org/vexi/util/SoftReferenceCache.java
===================================================================
--- 
trunk/org.vexi-library.util/src/main/java/org/vexi/util/SoftReferenceCache.java 
                            (rev 0)
+++ 
trunk/org.vexi-library.util/src/main/java/org/vexi/util/SoftReferenceCache.java 
    2014-04-18 00:35:32 UTC (rev 4687)
@@ -0,0 +1,34 @@
+package org.vexi.util;
+
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class SoftReferenceCache<K,V> {
+       final private Map<K, SoftReference<V>> m = new HashMap<K, 
SoftReference<V>>();
+       int counter = 0;
+       synchronized public V get(K key){
+               SoftReference<V> r = m.get(key);
+               if(r==null) return null;
+               return r.get();
+       }
+       
+       synchronized public void cache(K key, V value){
+               counter ++;
+               if(counter>100){
+                       counter = 0;            
+                       
+                   Iterator<Entry<K, SoftReference<V>>> it = 
m.entrySet().iterator();
+                   while (it.hasNext()) {
+                       Entry<K, SoftReference<V>> entry = it.next();
+                       if(entry.getValue().get()==null){
+                               it.remove();
+                       }
+                   }
+               }
+               m.put(key, new SoftReference<V>(value));
+       }
+       
+}


Property changes on: 
trunk/org.vexi-library.util/src/main/java/org/vexi/util/SoftReferenceCache.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Learn Graph Databases - Download FREE O'Reilly Book
"Graph Databases" is the definitive new guide to graph databases and their
applications. Written by three acclaimed leaders in the field,
this first edition is now available. Download your free book today!
http://p.sf.net/sfu/NeoTech
_______________________________________________
Vexi-svn mailing list
Vexi-svn@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/vexi-svn

Reply via email to