Mercurial Hosting > luan
view src/luan/modules/ThreadLuan.java @ 1972:253f8a23e131
threading for swing
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 18 Jun 2025 18:54:38 -0600 |
parents | 31f006c64782 |
children |
line wrap: on
line source
package luan.modules; import java.io.Closeable; import java.util.Iterator; import java.util.Map; import java.util.HashMap; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; 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.ExecutionException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import goodjava.util.WeakCacheMap; import luan.Luan; import luan.LuanFunction; import luan.LuanTable; import luan.LuanException; import luan.LuanRuntimeException; import luan.LuanMutable; import luan.modules.logging.LuanLogger; import goodjava.logging.Logger; import goodjava.logging.LoggerFactory; public final class ThreadLuan { private static final Logger logger = LoggerFactory.getLogger(ThreadLuan.class); public static final String CLOSEABLES = "Luan.closeables"; public interface Closeables { public void addCloseable(Closeable c) throws LuanException; } private static final Executor exec = Executors.newCachedThreadPool(); public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private static Runnable runnable(final Luan luan,final LuanFunction fn) { return new Runnable() { public synchronized void run() { LuanLogger.startThreadLogging(luan); try { fn.call(luan); } catch(LuanException e) { e.printStackTrace(); } finally { LuanLogger.endThreadLogging(); } } }; } private static Luan newLuan(Luan luan,Boolean clean) { if( Boolean.TRUE.equals(clean) ) { Luan.Security security = luan.getSecurity(); luan = new Luan(); if( security != null ) Luan.setSecurity(luan,security); return luan; } else { return new Luan(luan); } } public static void run(Luan luan,LuanFunction fn,Boolean clean) throws LuanException { luan = newLuan(luan,clean); LuanMutable.makeImmutable(fn); exec.execute(runnable(luan,fn)); } public static LuanFunction thread_safe_function(final Luan luan,final LuanFunction fn) { final Thread thread = Thread.currentThread(); return new LuanFunction() { @Override public Object call(Luan luan2,Object[] args) throws LuanException { if( thread != Thread.currentThread() ) throw new LuanException("called function from another thread"); if( luan != luan2 ) throw new LuanException("called function from another luan"); return fn.call(luan,args); } }; } private static void cancel(ScheduledFuture sf,String src) { boolean b = sf.cancel(false); if( !sf.isCancelled() ) logger.error(src+" cancel="+b+" isCancelled="+sf.isCancelled()+" isDone="+sf.isDone()+" "+sf); } public static synchronized void schedule_closure(Luan luan,LuanFunction initFn,LuanTable options) throws LuanException { final Luan newLuan = new Luan(luan); LuanMutable.makeImmutable(initFn); LuanFunction fn = (LuanFunction)initFn.call(newLuan); scheduleFn(luan,newLuan,fn,options); } public static synchronized void schedule(Luan luan,LuanFunction fn,LuanTable options) throws LuanException { final Luan newLuan = new Luan(luan); LuanMutable.makeImmutable(fn); scheduleFn(luan,newLuan,fn,options); } private static synchronized void scheduleFn(Luan luan,final Luan newLuan,LuanFunction fn,LuanTable options) throws LuanException { options = new LuanTable(options); Number delay = Utils.removeNumber(options,"delay"); Number repeatingDelay = Utils.removeNumber(options,"repeating_delay"); Number repeatingRate = Utils.removeNumber(options,"repeating_rate"); String id = Utils.removeString(options,"id"); if( id != null ) logger.error("thread option 'id' is obsolete: "+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(options); final Runnable r = runnable(newLuan,fn); final ScheduledFuture sf; if( repeatingDelay != null ) { if( delay==null ) delay = repeatingDelay; sf = scheduler.scheduleWithFixedDelay(r,delay.longValue(),repeatingDelay.longValue(),TimeUnit.MILLISECONDS); } else if( repeatingRate != null ) { if( delay==null ) delay = repeatingRate; sf = scheduler.scheduleAtFixedRate(r,delay.longValue(),repeatingRate.longValue(),TimeUnit.MILLISECONDS); } else if( delay != null ) { sf = scheduler.schedule(r,delay.longValue(),TimeUnit.MILLISECONDS); } else { scheduler.schedule(r,0L,TimeUnit.MILLISECONDS); return; } Closeables cs = (Closeables)luan.registry.get(CLOSEABLES); if( cs != null ) { Closeable cl = new Closeable() { public void close() { cancel(sf,"close"); } protected void finalize() throws Throwable { cancel(sf,"gc"); // cancel on gc } }; cs.addCloseable(cl); } } public static void sleep(long millis) throws InterruptedException { Thread.sleep(millis); } public static final class CallableLuan { private long expires; private final Luan luan; private final LuanTable fns; CallableLuan(Luan luan,LuanFunction initFn) throws LuanException { this.luan = new Luan(luan); LuanMutable.makeImmutable(initFn); Object obj = initFn.call(luan); if( !(obj instanceof LuanTable) ) throw new LuanException("global_callable init_fn must return a table"); this.fns = (LuanTable)obj; } public synchronized Object call(String fnName,Object... args) throws LuanException { LuanMutable.makeImmutable(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); LuanMutable.makeImmutable(rtn); return rtn; } } private static void sweep(Map<String,CallableLuan> callableMap,long now) { for( Iterator<CallableLuan> iter = callableMap.values().iterator(); iter.hasNext(); ) { CallableLuan callable = iter.next(); if( callable.expires < now ) iter.remove(); } } public static CallableLuan globalCallable(Luan luan,final String name,LuanFunction initFn,long timeout) throws LuanException { Map rootRegistry = luan.rootRegistry; synchronized(rootRegistry) { Map<String,CallableLuan> callableMap = (Map<String,CallableLuan>)rootRegistry.get("Luan.callableMap"); if( callableMap == null ) { callableMap = new HashMap<String,CallableLuan>(); rootRegistry.put("Luan.callableMap",callableMap); } long now = System.currentTimeMillis(); CallableLuan callable = callableMap.get(name); if( callable == null ) { sweep(callableMap,now); callable = new CallableLuan(luan,initFn); callableMap.put(name,callable); } callable.expires = now + timeout; return callable; } } public static void removeGlobalCallable(Luan luan,String name) { Map rootRegistry = luan.rootRegistry; synchronized(rootRegistry) { Map<String,CallableLuan> callableMap = (Map<String,CallableLuan>)rootRegistry.get("Luan.callableMap"); if( callableMap != null ) callableMap.remove(name); } } public static Object runInLock(Luan luan,Lock lock,long timeout,LuanFunction fn,Object... args) throws LuanException, InterruptedException { if( !lock.tryLock(timeout,TimeUnit.MILLISECONDS) ) throw new LuanException("failed to acquire lock"); try { return fn.call(luan,args); } finally { lock.unlock(); } } private static final Map<String,Lock> locks = new WeakCacheMap<String,Lock>(); public static synchronized Lock getLock(String key) { Lock lock = locks.get(key); if( lock == null ) { lock = new ReentrantLock(); locks.put(key,lock); } return lock; } private static Callable<Object> callable(final Luan luan,final LuanFunction fn) { return new Callable<Object>() { public Object call() { LuanLogger.startThreadLogging(luan); try { return fn.call(luan); } catch(LuanException e) { //e.printStackTrace(); throw new LuanRuntimeException(e); } finally { LuanLogger.endThreadLogging(); } } }; } public static final class FutureLuan { public final Future<Object> future; private FutureLuan(Future<Object> future) { this.future = future; } public Object get() throws LuanException, InterruptedException { try { return future.get(); } catch(ExecutionException e) { Throwable cause = e.getCause(); if( cause instanceof LuanRuntimeException ) { throw (LuanException)cause.getCause(); } else { throw new LuanException(cause); } } } public boolean isDone() { return future.isDone(); } } public static FutureLuan newFuture(Luan luan,LuanFunction fn) { luan = new Luan(luan); LuanMutable.makeImmutable(fn); FutureTask<Object> ft = new FutureTask<Object>(callable(luan,fn)); new Thread(ft).start(); return new FutureLuan(ft); } }