changeset 1401:ef1620aa99cb

fix gc issues
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 16 Sep 2019 22:51:41 -0400
parents 221eedb0f54e
children 27efb1fcbcb5
files conv.txt examples/blog/src/lib/Db.luan src/luan/Luan.java src/luan/lib/webserver/handlers/DomainHandler.java src/luan/modules/ThreadLuan.java src/luan/modules/http/LuanHandler.java src/luan/modules/http/tools/Luan_threads.luan
diffstat 7 files changed, 114 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/conv.txt	Fri Sep 13 05:05:51 2019 -0600
+++ b/conv.txt	Mon Sep 16 22:51:41 2019 -0400
@@ -1,3 +1,5 @@
+Thread.schedule
+
 Lucene.index
 stringify
 
--- a/examples/blog/src/lib/Db.luan	Fri Sep 13 05:05:51 2019 -0600
+++ b/examples/blog/src/lib/Db.luan	Mon Sep 16 22:51:41 2019 -0400
@@ -38,6 +38,6 @@
 Db.db = Db.new("site:/private/local/lucene")
 
 Db.db.restore_from_postgres()
-Thread.schedule( Db.db.check, { delay=0, repeating_delay=Time.period{minutes=1}, daemon = true } )
+Thread.schedule( Db.db.check, { delay=0, repeating_delay=Time.period{minutes=1}, id="blog-db-check" } )
 
 return Db
--- a/src/luan/Luan.java	Fri Sep 13 05:05:51 2019 -0600
+++ b/src/luan/Luan.java	Mon Sep 16 22:51:41 2019 -0400
@@ -1,7 +1,6 @@
 package luan;
 
 import java.lang.reflect.Array;
-import java.io.Closeable;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Map;
@@ -26,11 +25,6 @@
 	private Map registry;
 	private boolean isLocked = false;
 
-	public interface OnClose extends Closeable {
-		public void onClose(Closeable c);
-	}
-	public OnClose onClose;
-
 	public Luan() {
 		registry = new HashMap();
 	}
@@ -71,11 +65,6 @@
 		return registry;
 	}
 
-	public void onClose(Closeable c) {
-		if( onClose != null )
-			onClose.onClose(c);
-	}
-
 	public Object eval(String cmd,Object... args) throws LuanException {
 		return load(cmd,"eval").call(args);
 	}
--- a/src/luan/lib/webserver/handlers/DomainHandler.java	Fri Sep 13 05:05:51 2019 -0600
+++ b/src/luan/lib/webserver/handlers/DomainHandler.java	Mon Sep 16 22:51:41 2019 -0400
@@ -3,7 +3,7 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
+//import java.lang.ref.WeakReference;
 import java.lang.ref.SoftReference;
 import java.lang.ref.ReferenceQueue;
 import java.util.Map;
@@ -22,25 +22,6 @@
 		public Handler newHandler(String domain);
 	}
 
-	private static class Ref {
-		private final Handler handler;
-
-		private Ref(Handler handler) {
-			this.handler = handler;
-		}
-	}
-
-	private final ReferenceQueue<Ref> queue = new ReferenceQueue<Ref>();
-
-	private class MyReference extends WeakReference<Ref> {
-		private Handler handler;
-
-		private MyReference(Ref r) {
-			super(r,queue);
-			this.handler = r.handler;
-		}
-	}
-
 	private static void close(Handler handler) {
 		if( handler instanceof Closeable ) {
 			try {
@@ -51,18 +32,7 @@
 		}
 	}
 
-	private void sweep() {
-		while(true) {
-			MyReference ref = (MyReference)queue.poll();
-			if( ref == null )
-				return;
-			//logger.info("sweep");
-			close(ref.handler);
-			ref.handler = null;
-		}
-	}
-
-	private final Map<String,MyReference> map = new HashMap<String,MyReference>();
+	private final Map<String,Reference<Handler>> map = new HashMap<String,Reference<Handler>>();
 
 	private final Factory factory;
 
@@ -81,37 +51,31 @@
 	}
 
 	public Handler getHandler(String domain) {
-		Ref r = getRef(domain);
-		return r==null ? null : r.handler;
+		domain = domain.toLowerCase();
+		synchronized(map) {
+			Reference<Handler> ref = map.get(domain);
+			Handler handler = ref==null ? null : ref.get();
+			if( handler == null ) {
+				//if(ref!=null) logger.info("gc "+domain);
+				handler = factory.newHandler(domain);
+				if( handler == null )
+					return null;
+				map.put(domain,new SoftReference<Handler>(handler));
+			}
+			return handler;
+		}
 	}
 
 	public void removeHandler(String domain) {
+		logger.info("removeHandler "+domain);
 		domain = domain.toLowerCase();
 		synchronized(map) {
-			Reference<Ref> ref = map.remove(domain);
-			Ref r = ref==null ? null : ref.get();
-			if( r != null ) {
-				close(r.handler);
+			Reference<Handler> ref = map.remove(domain);
+			Handler handler = ref==null ? null : ref.get();
+			if( handler != null ) {
+				close(handler);
 			}
 		}
 	}
 
-	private Ref getRef(String domain) {
-		domain = domain.toLowerCase();
-		synchronized(map) {
-			Reference<Ref> ref = map.get(domain);
-			Ref r = ref==null ? null : ref.get();
-			if( r == null ) {
-				//if(ref!=null) logger.info("gc "+domain);
-				sweep();
-				Handler handler = factory.newHandler(domain);
-				if( handler == null )
-					return null;
-				r = new Ref(handler);
-				map.put(domain,new MyReference(r));
-			}
-			return r;
-		}
-	}
-
 }
--- a/src/luan/modules/ThreadLuan.java	Fri Sep 13 05:05:51 2019 -0600
+++ b/src/luan/modules/ThreadLuan.java	Mon Sep 16 22:51:41 2019 -0400
@@ -6,11 +6,14 @@
 import java.util.Map;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -21,9 +24,13 @@
 import luan.LuanException;
 import luan.LuanCloner;
 import luan.LuanCloneable;
+import luan.lib.logging.Logger;
+import luan.lib.logging.LoggerFactory;
 
 
 public final class ThreadLuan {
+	private static final Logger logger = LoggerFactory.getLogger(ThreadLuan.class);
+
 	private static final Executor exec = Executors.newCachedThreadPool();
 	public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
 
@@ -45,6 +52,8 @@
 		exec.execute(runnable(newFn));
 	}
 
+	private static Map<String,Reference<ScheduledFuture>> scheduleds = new ConcurrentHashMap<String,Reference<ScheduledFuture>>();
+
 	public static void schedule(LuanFunction fn,LuanTable options)
 		throws LuanException
 	{
@@ -52,12 +61,19 @@
 		Number delay = Utils.removeNumber(map,"delay");
 		Number repeatingDelay = Utils.removeNumber(map,"repeating_delay");
 		Number repeatingRate = Utils.removeNumber(map,"repeating_rate");
-		boolean daemon = Boolean.TRUE.equals(Utils.removeBoolean(map,"daemon"));
-		final boolean runOnClose = Boolean.TRUE.equals(Utils.removeBoolean(map,"run_on_close"));
+		String id = Utils.removeString(map,"id");
 		if( repeatingDelay!=null && repeatingRate!=null )
 			throw new LuanException("can't define both repeating_delay and repeating_rate");
 		boolean repeating = repeatingDelay!=null || repeatingRate!=null;
 		Utils.checkEmpty(map);
+		if( id != null ) {
+			Reference<ScheduledFuture> ref = scheduleds.remove(id);
+			if( ref != null ) {
+				ScheduledFuture sf = ref.get();
+				if( sf != null )
+					sf.cancel(false);
+			}
+		}
 		Luan luan = fn.luan();
 		LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
 		final Luan newLuan = (Luan)cloner.clone(luan);
@@ -74,26 +90,18 @@
 			sf = scheduler.scheduleWithFixedDelay(r,delay.longValue(),repeatingRate.longValue(),TimeUnit.MILLISECONDS);
 		} else if( delay != null ) {
 			sf = scheduler.schedule(r,delay.longValue(),TimeUnit.MILLISECONDS);
-		} else if( runOnClose ) {
-			Closeable c = new Closeable(){public void close(){
-				scheduler.schedule(r,0L,TimeUnit.MILLISECONDS);
-			}};
-			luan.registry().put(c,c);  // prevent gc
-			luan.onClose(c);
-			return;
 		} else {
 			scheduler.schedule(r,0L,TimeUnit.MILLISECONDS);
 			return;
 		}
-		Closeable c = new Closeable(){public void close(){
-			boolean b = sf.cancel(false);
-			if( runOnClose )
-				scheduler.schedule(r,0L,TimeUnit.MILLISECONDS);
-		}};
-		if( !daemon )
-			newLuan.registry().put(luan,luan);  // prevent gc
-		luan.registry().put(c,c);  // prevent gc
-		luan.onClose(c);
+		Object c = new Object() {
+			protected void finalize() throws Throwable {
+				sf.cancel(false);
+			}
+		};
+		luan.registry().put(c,c);  // cancel on gc
+		if( id != null )
+			scheduleds.put(id,new WeakReference<ScheduledFuture>(sf));
 	}
 
 /*
--- a/src/luan/modules/http/LuanHandler.java	Fri Sep 13 05:05:51 2019 -0600
+++ b/src/luan/modules/http/LuanHandler.java	Mon Sep 16 22:51:41 2019 -0400
@@ -10,6 +10,9 @@
 import java.net.BindException;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import luan.lib.logging.Logger;
@@ -31,25 +34,28 @@
 import luan.modules.logging.LuanLogger;
 
 
-public final class LuanHandler implements Handler, Luan.OnClose {
+public final class LuanHandler implements Handler, Closeable {
 	private static final Logger sysLogger = LoggerFactory.getLogger(LuanHandler.class);
 
+	private static final Set<LuanHandler> dontGc = Collections.newSetFromMap(new ConcurrentHashMap<LuanHandler,Boolean>());
+
 	private final Luan luanInit;
 	private final String domain;
 	private final Logger luanLogger;
 	private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
-	private final List<Reference<Closeable>> onClose = new ArrayList<Reference<Closeable>>();
 	private volatile Luan currentLuan;
 	private volatile boolean isDisabled = false;
 
 	private static final Method resetLuanMethod;
 	private static final Method evalInRootMethod;
 	private static final Method disableLuanMethod;
+	private static final Method dontGcMethod;
 	static {
 		try {
-			resetLuanMethod = LuanHandler.class.getMethod( "reset_luan" );
-			evalInRootMethod = LuanHandler.class.getMethod( "eval_in_root", String.class );
-			disableLuanMethod = LuanHandler.class.getMethod( "disable_luan" );
+			resetLuanMethod = LuanHandler.Fns.class.getMethod( "reset_luan" );
+			evalInRootMethod = LuanHandler.Fns.class.getMethod( "eval_in_root", String.class );
+			disableLuanMethod = LuanHandler.Fns.class.getMethod( "disable_luan" );
+			dontGcMethod = LuanHandler.Fns.class.getMethod( "dont_gc" );
 		} catch(NoSuchMethodException e) {
 			throw new RuntimeException(e);
 		}
@@ -60,20 +66,22 @@
 		this.domain = domain;
 		this.luanLogger = luanInit.getLogger(LuanHandler.class);
 		try {
+			Fns fns = new Fns(this);
 			LuanTable Http = (LuanTable)luanInit.require("luan:http/Http.luan");
 			if( Http.get("reset_luan") == null )
-				Http.put( "reset_luan", new LuanJavaFunction(luanInit,resetLuanMethod,this) );
-			Http.put( "eval_in_root", new LuanJavaFunction(luanInit,evalInRootMethod,this) );
-			Http.put( "disable_luan", new LuanJavaFunction(luanInit,disableLuanMethod,this) );
+				Http.put( "reset_luan", new LuanJavaFunction(luanInit,resetLuanMethod,fns) );
+			Http.put( "eval_in_root", new LuanJavaFunction(luanInit,evalInRootMethod,fns) );
+			Http.put( "disable_luan", new LuanJavaFunction(luanInit,disableLuanMethod,fns) );
+			Http.put( "dont_gc", new LuanJavaFunction(luanInit,dontGcMethod,fns) );
 		} catch(LuanException e) {
 			throw new RuntimeException(e);
 		}
+		sysLogger.info("new "+domain);
 		currentLuan = newLuan();
-		sysLogger.info("new "+domain);
 	}
 
 	protected void finalize() throws Throwable {
-		sysLogger.info("gc  "+domain);
+		sysLogger.info("gc "+domain);
 	}
 
 	private Luan newLuan() {
@@ -82,7 +90,6 @@
 			LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
 			luan = (Luan)cloner.clone(luanInit);
 		}
-		luan.onClose = this;
 		try {
 			PackageLuan.load(luan,"site:/init.luan");
 		} catch(LuanException e) {
@@ -126,26 +133,9 @@
 		}
 	}
 
-	public void onClose(Closeable c) {
-		synchronized(onClose) {
-			onClose.add(new WeakReference<Closeable>(c));
-		}
-	}
-
 	public void close() {
-		synchronized(onClose) {
-			for( Reference<Closeable> ref : onClose ) {
-				Closeable c = ref.get();
-				if( c != null ) {
-					try {
-						c.close();
-					} catch(IOException e) {
-						luanLogger.error(c.toString(),e);
-					}
-				}
-			}
-			onClose.clear();
-		}
+		Object obj = dontGc.remove(this);
+		//sysLogger.info("close "+domain+" "+(obj!=null));
 	}
 
 	public Object call_rpc(String fnName,Object... args) throws LuanException {
@@ -168,7 +158,15 @@
 		}
 	}
 
-	public void reset_luan() {
+	public static void start(Server server) throws Exception {
+		try {
+			server.start();
+		} catch(BindException e) {
+			throw new LuanException(e.toString());
+		}
+	}
+
+	private void reset_luan() {
 		new Thread() {public void run(){
 			rwLock.writeLock().lock();
 			try {
@@ -180,32 +178,57 @@
 		}}.start();
 	}
 
-	public void disable_luan() {
+	private void disable_luan() {
 		isDisabled = true;
 	}
 
-	public void eval_in_root(String text) throws LuanException {
+	private void eval_in_root(String text) throws LuanException {
 		Luan luan;
 		synchronized(luanInit) {
 			LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
 			luan = (Luan)cloner.clone(currentLuan);
 		}
 		luan.load(text,"<eval_in_root>",null).call();
-		currentLuan.onClose = null;
-		luan.onClose = this;
 		currentLuan = luan;
 	}
 
-	public static void start(Server server) throws Exception {
-		try {
-			server.start();
-		} catch(BindException e) {
-			throw new LuanException(e.toString());
+	private void dont_gc() {
+		dontGc.add(this);
+		//sysLogger.info("dont_gc "+domain);
+	}
+
+	public static final class Fns {
+		private final Reference<LuanHandler> ref;
+
+		private Fns(LuanHandler lh) {
+			this.ref = new WeakReference<LuanHandler>(lh);
+		}
+
+		private LuanHandler lh() throws LuanException {
+			LuanHandler lh = ref.get();
+			if( lh == null )
+				throw new LuanException("HTTP handler has been garbage collected");
+			return lh;
+		}
+
+		public void reset_luan() throws LuanException {
+			lh().reset_luan();
+		}
+
+		public void disable_luan() throws LuanException {
+			lh().disable_luan();
+		}
+
+		public void eval_in_root(String text) throws LuanException {
+			lh().eval_in_root(text);
+		}
+
+		public void dont_gc() throws LuanException {
+			lh().dont_gc();
 		}
 	}
 
 
-
 	// from HttpServicer
 
 	private Response service(Request request,boolean notFound)
@@ -289,9 +312,7 @@
 		for( String mod : mods ) {
 			if( loaded.rawGet(mod) == null ) {
 				LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
-				currentLuan.onClose = null;
 				currentLuan = (Luan)cloner.clone(currentLuan);
-				currentLuan.onClose = this;
 				break;
 			}
 		}
--- a/src/luan/modules/http/tools/Luan_threads.luan	Fri Sep 13 05:05:51 2019 -0600
+++ b/src/luan/modules/http/tools/Luan_threads.luan	Mon Sep 16 22:51:41 2019 -0400
@@ -42,7 +42,7 @@
 				<ul>
 				<%
 				for i, el in Luan.ipairs(luan_trace) do
-					local line = LuanException.toString(el);
+					local line = LuanException.toLuanString(el);
 					%><li><%=line%></li><%
 				end
 				%>