view src/luan/modules/ThreadLuan.java @ 1202:d3a3ca116e42

gc site instances
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 05 Mar 2018 19:30:51 -0700
parents 7ef40e1923b7
children 3ffc7c4a3b85
line wrap: on
line source

package luan.modules;

import java.io.Closeable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import luan.Luan;
import luan.LuanState;
import luan.LuanFunction;
import luan.LuanTable;
import luan.LuanException;
import luan.LuanCloner;


public final class ThreadLuan {
	private static final Executor exec = Executors.newCachedThreadPool();
	public static final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);

	public static void fork(LuanState luan,LuanFunction fn,Object... args) {
		LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
		final LuanState newLuan = (LuanState)cloner.clone(luan);
		final LuanFunction newFn = (LuanFunction)cloner.get(fn);
		final Object[] newArgs = cloner.clone(args);
		exec.execute(new Runnable(){public void run() {
			try {
				newFn.call(newLuan,newArgs);
			} catch(LuanException e) {
				e.printStackTrace();
			}
		}});
	}
/*
	public static LuanFunction synchronized_(final LuanState luan,final LuanFunction fn) throws LuanException {
		Utils.checkNotNull(fn);
		return new LuanFunction() {
			@Override public Object call(LuanState ingored,Object[] args) throws LuanException {
				synchronized(luan) {
					return fn.call(luan,args);
				}
			}
		};
	}
*/
	public static void schedule(LuanState luan,long delay,boolean repeat,LuanFunction fn,Object... args) {
		LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
		final LuanState newLuan = (LuanState)cloner.clone(luan);
		final LuanFunction newFn = (LuanFunction)cloner.get(fn);
		final Object[] newArgs = cloner.clone(args);
		Runnable r = new Runnable(){public void run() {
			try {
				newFn.call(newLuan,newArgs);
			} catch(LuanException e) {
				e.printStackTrace();
			}
		}};
		final ScheduledFuture sf;
		if( repeat ) {
			sf = scheduler.scheduleWithFixedDelay(r,delay,delay,TimeUnit.MILLISECONDS);
		} else {
			sf = scheduler.schedule(r,delay,TimeUnit.MILLISECONDS);
		}
		final Closeable c = new Closeable(){public void close(){
			boolean b = sf.cancel(false);
		}};
		newLuan.registry().put(luan,luan);  // prevent gc
		luan.registry().put(c,c);  // prevent gc
		luan.onClose(c);
	}

/*
	public static class GlobalMap {

		private static class Value {
			final long time = System.currentTimeMillis();
			final Object v;

			Value(Object v) {
				this.v = v;
			}
		}

		public long timeout = 60000L;  // one minute
		private Map<String,Value> map = new LinkedHashMap<String,Value>() {
			protected boolean removeEldestEntry(Map.Entry<String,Value> eldest) {
				return eldest.getValue().time < System.currentTimeMillis() - timeout;
			}
		};

		public synchronized Object get(String key) {
			Value val = map.get(key);
			return val==null ? null : val.v;
		}

		public synchronized Object put(String key,Object v) throws LuanException {
			Value val;
			if( v == null ) {
				val = map.remove(key);
			} else {
				if( !(v instanceof String || v instanceof Boolean || v instanceof Number) )
					throw new LuanException("can't assign type "+Luan.type(v)+" to Thread.global");
				val = map.put(key,new Value(v));
			}
			return val==null ? null : val.v;
		}
	}
*/

	public static void sleep(long millis) throws InterruptedException {
		Thread.sleep(millis);
	}


	private static boolean isPrimitive(Object v) {
		if( v instanceof Object[] ) {
			Object[] a = (Object[])v;
			for( Object obj : a ) {
				if( !isPrimitive(obj) )
					return false;
			}
			return true;
		} else {
			return v==null || v instanceof String || v instanceof Boolean || v instanceof Number;
		}
	}

	public static final class Callable {
		private long expires;
		private final LuanState luan = new LuanState();
		private final LuanTable fns;

		Callable(LuanState luan,LuanTable fns) {
			LuanCloner cloner = new LuanCloner(LuanCloner.Type.COMPLETE);
			this.fns = (LuanTable)cloner.get(fns);
		}

		public synchronized Object call(String fnName,Object... args) throws LuanException {
			if( !isPrimitive(args) )
				throw new LuanException("can't pass non-primitive type to global_callable "+Arrays.asList(args));
			Object f = fns.get(luan,fnName);
			if( f == null )
				throw new LuanException("function '"+fnName+"' not found in global_callable");
			if( !(f instanceof LuanFunction) )
				throw new LuanException("value of '"+fnName+"' not a function in global_callable");
			LuanFunction fn = (LuanFunction)f;
			Object rtn = fn.call(luan,args);
			if( !isPrimitive(rtn) )
				throw new LuanException("can't return non-primitive type from global_callable");
			return rtn;
		}
	}

	private static Map<String,Callable> callableMap = new HashMap<String,Callable>();

	private static void sweep() {
		long now = System.currentTimeMillis();
		for( Iterator<Callable> iter = callableMap.values().iterator(); iter.hasNext(); ) {
			Callable callable = iter.next();
			if( callable.expires < now )
				iter.remove();
		}
	}

	public static synchronized Callable globalCallable(LuanState luan,String name,long timeout,LuanTable fns) {
		Callable callable = callableMap.get(name);
		if( callable == null ) {
			sweep();
			callable = new Callable(luan,fns);
			callableMap.put(name,callable);
		}
		callable.expires = System.currentTimeMillis() + timeout;
		return callable;
	}

	public static synchronized void removeGlobalCallable(String name) {
		callableMap.remove(name);
	}

}