Revision: 4765 http://sourceforge.net/p/vexi/code/4765 Author: mkpg2 Date: 2015-02-25 20:11:16 +0000 (Wed, 25 Feb 2015) Log Message: ----------- Background thread callback/syncCall. - allows pausing when call java script to be evaluated from another thread (previously used mechanism was not pausable since it used runInCurrent). - test for syncCall Rename methods findXxx -> expectXxx. Better convention.
Modified Paths: -------------- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VML.java branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VMLBuilder.java 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/Fountain.java branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSFunction.java branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java 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 branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/parse/Function.java branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java branches/vexi3/org.vexi-library.js/src/test/java/test/js/TestJS.java Added Paths: ----------- branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/ branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/AssertionUtil.java branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/Scripting.java branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/TestJSThreading.java branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/script.js Removed Paths: ------------- branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSDynProxy.java Modified: branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VML.java =================================================================== --- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VML.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VML.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -198,7 +198,7 @@ void apply(JS[] thisHolder, JS[] args, PerInstantiationScope idContextParent, PerInstantiationScope idContext) throws JSExn { - Main.SCHEDULER.findCurrentInterpreter().enterNonJSCall(this); + Main.SCHEDULER.expectCurrentInterpreter().enterNonJSCall(this); try { // REMARK - the preapplies may not have been resolved yet, // the resolved template is not necessarily the same object. @@ -301,7 +301,7 @@ JSU.cloneWithNewGlobalScope((Function)script, pis).apply(null, args!=null?args:EMPTY_JS_ARRAY); } } finally { - Main.SCHEDULER.findCurrentInterpreter().exitNonJSCall(); + Main.SCHEDULER.expectCurrentInterpreter().exitNonJSCall(); } } Modified: branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VMLBuilder.java =================================================================== --- branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VMLBuilder.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/core/VMLBuilder.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -216,14 +216,14 @@ Function staticScript = parseScript(static_.content, static_.contentStart, sourceName()); if (t != null) { static_.template = t; - Main.SCHEDULER.findCurrentInterpreter().enterNonJSCall( static_); + Main.SCHEDULER.expectCurrentInterpreter().enterNonJSCall( static_); try { if (staticScript != null) { JS staticScope = static_.new StaticScope(); JSU.cloneWithNewGlobalScope(staticScript, staticScope).apply(null, callempty); } } finally { - Main.SCHEDULER.findCurrentInterpreter().exitNonJSCall(); + Main.SCHEDULER.expectCurrentInterpreter().exitNonJSCall(); } } else { Log.warn(LOG_TYPE, "'" + static_.sourceName + ".t' does not declare a template"); 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 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-core.main/src/main/java/org/vexi/graphics/Picture.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -90,7 +90,7 @@ } static public Picture load(JS[] args) throws JSExn { - final Scheduler sched = Scheduler.findCurrent(); + final Scheduler sched = Scheduler.expectCurrent(); final Thread callback = sched.pauseJSThread("load picture"); Picture p = Picture.load(args[0], new Callable(){ public Object run(Object o) throws JSExn { 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 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-core.main/src/main/jpp/org/vexi/core/Vexi.jpp 2015-02-25 20:11:16 UTC (rev 4765) @@ -654,7 +654,7 @@ // sanity check so we are not passing nonsensical arguments to the scheduler throw new JSExn("Tried to put non-function value of type '"+(value==null?"null":value.type())+"' to vexi.thread"); } - Main.SCHEDULER.runInNew(value, null); + Main.SCHEDULER.runInNew(value); return; case "debug": Main.debug = JSU.toBoolean(value); Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Fountain.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -155,7 +155,7 @@ super.addTrap(key, f); // used for call backs when input stream is read, we assume // the traps added are callbacks - if(scheduler==null) scheduler = Scheduler.findCurrent(); + if(scheduler==null) scheduler = Scheduler.expectCurrent(); } // Perform any caching (Fountain.Multi calls this on its @@ -172,7 +172,7 @@ public JS callMethod(JS this_, JS method, JS[] args) throws JSExn { if("info".equals(JSU.toString(method))){ try { - final Scheduler sched = Scheduler.findCurrent(); + final Scheduler sched = Scheduler.expectCurrent(); return sched.backgroundCall("get fountain info", new Callable<Object, JS>() { public JS run(Object A) throws Exception { return getInfo(); @@ -417,7 +417,7 @@ static final int[] ARGTYPES_2fountains = new int[]{JSU.FOUNTAIN,JSU.FOUNTAIN}; static public void pipe(final JS[] args) throws JSExn{ JSU.checkArgs(args, ARGTYPES_2fountains); - final Scheduler sched = Scheduler.findCurrent(); + final Scheduler sched = Scheduler.expectCurrent(); sched.backgroundCall("pipe streams", new Callable<Object, JS>() { public JS run(Object A) throws Exception { try { Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Interpreter.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -119,7 +119,7 @@ // FIXME: split this stuff out into a Script instance control object // so it's possible to make JS either single or multi threaded. /** this is the only synchronization point we need in order to be threadsafe */ - public synchronized Object run(Object o) throws JSExn { + public synchronized JS run(Object o) throws JSExn { if (f == null) throw new RuntimeException("function already finished"); if (scope == null) throw new RuntimeException("scope is null"); @@ -136,7 +136,11 @@ } public void pause(String forwhat) throws JSExn { - if (pausecount == -1 || f == null) throw new JSExn("Cannot "+forwhat+" in foreground thread"); + if (pausecount == -1 || f == null) { + String msg = "Cannot "+forwhat+" in thread '"+thread+"'"; + if(old!=null) msg+=" (nested execution)"; + throw new JSExn(msg); + } pausecount++; switch(f.op[pc]) { case Tokens.RETURN: case ByteCodes.PUT: get = false; break; @@ -149,9 +153,7 @@ return f == null || pc < 0 || pc >= f.size ? -1 : f.line[pc]; } - String getSourceName() { - return f == null ? null : f.sourceName; - } + String getSourceName() { return f == null ? null : f.sourceName; } String getWhere(){ return getSourceName() + ":" + getLine(); } private JSExn je(String s) { return new JSExn(getWhere() + " " + s); } Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSFunction.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSFunction.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSFunction.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -49,7 +49,7 @@ public JS apply(JS this_, JS[] args) throws JSExn{ // UNIDEAL .. we are prevented from yielding after apply is called // (until it returns). - return Scheduler.findCurrent().runInCurrent(this, this_, args); + return Scheduler.expectCurrent().runInCurrent(this, this_, args); } public String[] getFormalArgs() { return f.formalArgs; } Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/JSU.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -248,7 +248,7 @@ static public JS stackframe(int fromtop){ - Interpreter cx = Scheduler.findCurrent().findCurrentInterpreter(); + Interpreter cx = Scheduler.expectCurrent().expectCurrentInterpreter(); // HACKish String sfLine = cx.stack.stackframe(fromtop); if(sfLine==null){ 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 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Scheduler.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -4,6 +4,8 @@ package org.ibex.js; +import java.util.concurrent.CountDownLatch; + import org.ibex.js.JS.Trap; import org.ibex.util.Basket; import org.ibex.util.Callable; @@ -14,7 +16,7 @@ /** Implements cooperative multitasking */ public class Scheduler { static ThreadLocal threadlocal = new ThreadLocal(); - static public Scheduler findCurrent() { + static public Scheduler expectCurrent() { Scheduler r = (Scheduler)threadlocal.get(); if (r==null) { throw new Error("No current scheduler!"); @@ -108,7 +110,7 @@ current = (Callable)runnable.remove(true); synchronized (this) { //Log.debug(Scheduler.class, "performing " + current); - current.run(null); + current.run(null); } renderAll(); /*} catch (Stop e){ @@ -162,8 +164,9 @@ jsthread = t; } - public void runInNew(JS function, JS[] args) throws JSExn { - add(new Thread(this, function, true, args)); + public void runInNew(JS function) throws JSExn { runInNew(function, null, null); } + public void runInNew(JS function, JS[] args, Callable<Object, JS> callback) throws JSExn { + add(new Thread(this, "background", function, args, true, callback)); } @@ -172,11 +175,12 @@ if (!(t.function() instanceof JSFunction)) { return t.function(); } + JSFunction trapf = (JSFunction)t.function(); // REMARK - isFirst <-> old == null? boolean isFirst = jsthread==null; if (isFirst) { - setJSThread(new Thread(this, t.function(), false, null)); + setJSThread(new Thread(this, "current/trap", trapf, null)); } Interpreter old = jsthread.currentInterpreter; jsthread.currentInterpreter = new Interpreter(jsthread, t, null, false, null); @@ -210,7 +214,7 @@ /** Execute write traps, part 1 */ public JS runBeforePut(Trap t, JS val, JS trapname) throws JSExn { if (jsthread==null) { - setJSThread(new Thread(this, t.function(), false, null)); + setJSThread(new Thread(this, "current/trap/wpause", t.function(), null)); } Interpreter I = new Interpreter(jsthread, t, val, true, trapname); // REMARK - this thread is unpausable, so setting this static variable @@ -266,7 +270,7 @@ /** Execute read traps, part 1 */ public JS runBeforeGet(Trap t, JS trapname) throws JSExn { if (jsthread==null) { - setJSThread(new Thread(this, t.function(), false, null)); + setJSThread(new Thread(this, "current/trap/rpause", t.function(), null)); } Interpreter I = new Interpreter(jsthread, t, null, true, trapname); // REMARK - this thread is unpausable, so setting this static variable @@ -304,7 +308,7 @@ boolean isFirst = jsthread==null; // FEATURE - reuse the thread object if (isFirst) { - setJSThread(new Thread(this, function, false, args)); + setJSThread(new Thread(this, "current", function, args)); } Interpreter old = jsthread.currentInterpreter; // Always false. Restarting paused nested Interpreters not supported. @@ -319,6 +323,7 @@ } } } + /** Creates a thread that doesn't immediately execute JS (necessary for non-JS backtracing) */ @@ -327,7 +332,7 @@ throw new JSExn("Something a miss"); } // FEATURE - reuse the thread object - setJSThread(new Thread(this, null, false, null)); + setJSThread(new Thread(this, "non-js", null, null)); // REMARK - This interpreter is just used for its stack for // recording non-JS call entries (Box.apply) for backtracing later jsthread.currentInterpreter = new Interpreter(jsthread); @@ -348,7 +353,7 @@ /** Gets the value for a given key, triggering any read traps for the key. */ static public JS getAndTriggerTrapsNoScheduler(JS obj, JS key) throws JSExn { - return Scheduler.findCurrent().getAndTriggerTraps(obj,key); + return Scheduler.expectCurrent().getAndTriggerTraps(obj,key); } /** Simulates a put to the given key, triggering any write traps for the key @@ -393,6 +398,12 @@ return value; } + /** Make a background call from within a currently executing JS thread. It will halt execution + * and allow other threads to execute, and when the action has been completed it will reschedule + * execution of the halted thread. + * + * @return always null + */ public JS backgroundCall(final String what, final Callable<Object,JS> callable) throws JSExn{ final Callable callback = pauseJSThread(what); new java.lang.Thread() { @@ -409,6 +420,30 @@ return null; // doesn't matter since we are paused } + public JS syncCall(JS function, JS[] args) throws JSExn, InterruptedException{ + final CountDownLatch latch0 = new CountDownLatch(1); + final Object[] retArr = new Object[1]; + runInNew(function, args, new Callable<Object, JS>() { + public JS run(Object ret) throws Exception { + retArr[0] = ret; + latch0.countDown(); + return null; + } + }); + + latch0.await(); + Object ret = retArr[0]; + if(ret==null || ret instanceof JS){ + return (JS)ret; + }else if(ret instanceof JSExn){ + throw (JSExn)ret; + }else{ + throw new JSExn((Throwable)ret); + } + } + + + public void scheduleJustTriggerTraps(final JS.Obj obj, final JS key, final JS value) { add(new Callable() { public Object run(Object o) throws Exception { @@ -428,8 +463,8 @@ }); } - final private Basket.Array sleeperThreads = new Basket.Array(); - final WakeupThread scheduleWakeUp = new WakeupThread(); + static final private Basket.Array sleeperThreads = new Basket.Array(); + static final WakeupThread scheduleWakeUp = new WakeupThread(); /** cause the current (background only) thread to sleep for i ms */ public void sleep(final int i) throws JSExn { @@ -438,27 +473,29 @@ if (i<0) { add(callback); } else { - scheduleWakeUp.insert(i, callback); + scheduleWakeUp.insert(this, i, callback); } } /** encapsulates a sleeper thread and its desired wakeup time in milliseconds * @author Charles Goodwin */ static final private class SleeperCallback { + final Scheduler scheduler; final long wakeupms; final Callable callback; - public SleeperCallback(int afterms, Callable callback) { - wakeupms = afterms + System.currentTimeMillis(); + public SleeperCallback(Scheduler scheduler, int afterms, Callable callback) { + this.scheduler = scheduler; + this.wakeupms = afterms + System.currentTimeMillis(); this.callback = callback; } } /** an internal mechanism for scheduling the wake up of sleeper threads * @author Charles Goodwin */ - final class WakeupThread extends java.lang.Thread { + static final class WakeupThread extends java.lang.Thread { public WakeupThread() { super("WakeupThread"); start(); } - public void insert(int i, Callable callback) { - SleeperCallback t = new SleeperCallback(i, callback); + public void insert(Scheduler scheduler, int i, Callable callback) { + SleeperCallback t = new SleeperCallback(scheduler, i, callback); synchronized (this) { // store sleeper threads in order of wake up time for (int j=sleeperThreads.size(); j>=0; j--) { @@ -495,7 +532,7 @@ if (t.wakeupms - System.currentTimeMillis() <= 0) { // nudge nudge wakey wakey sleeperThreads.remove(0); - add(t.callback); + t.scheduler.add(t.callback); } } } @@ -520,7 +557,7 @@ } return jsthread.currentInterpreter; } - public Interpreter findCurrentInterpreter() { + public Interpreter expectCurrentInterpreter() { if (jsthread==null) { throw new RuntimeException("No current js thread"); } 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 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/Thread.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -17,26 +17,29 @@ */ public class Thread implements Callable { - //static int id_counter = 0; //int id = id_counter++; - Interpreter currentInterpreter; - private JSFunction f; // Root function (id's thread) - private JS[] args; // Args passed to root function - private boolean pauseable; // Is thread pausable or not. + final String type; + final private JSFunction f; // Root function (id's thread) + final private JS[] args; // Args passed to root function + final boolean pauseable; // Is thread pausable or not. final Scheduler faction; + final Callable<Object,JS> callback; // callback run when thread completes + Interpreter currentInterpreter; + + /** Constructor */ - Thread(Scheduler faction, JS f, boolean pauseable, JS[] args) throws JSExn { - //if (f == null) { - // throw new JSExn("attempted to create a null thread"); - //} + Thread(Scheduler faction, String name, JS f, JS[] args) { this(faction, name, f, args, false, null); } + Thread(Scheduler faction, String type, JS f, JS[] args, boolean pauseable, Callable<Object,JS> callback) { + this.type = type; this.faction = faction; this.f = (JSFunction)f; this.pauseable = pauseable; this.args = args==null?Constants.EMPTY_JS_ARRAY:args; faction.jsthreads.addElement(this); + this.callback = callback; //Log.info("thread created "+threadCount+" "+id +" "+ this); } @@ -51,19 +54,40 @@ } /** Execute JS code in the background. Method executed by scheduler. + * @throws Exception * @throws JSExn */ - public Object run(Object o) throws JSExn { + public Object run(Object o) throws Exception { faction.setJSThread(this); if (currentInterpreter==null) { //First time this thread has been run (i.e. not paused or yielded yet) currentInterpreter = new Interpreter(this, f, this.pauseable, args); } try { - Object ret = this.currentInterpreter.run(o); - return ret; + JS ret = this.currentInterpreter.run(o); + if(callback!=null && this.currentInterpreter.pausecount<=0) { + callback.run(ret); + } + // if a result is desired then a callback must be used + return null; + } catch(JSExn e){ + if(callback!=null){ + callback.run(e); + assert(this.currentInterpreter.pausecount<=0); + return null; + }else{ + throw e; + } + } catch(Throwable e){ + // must call the callback as it could be a waiting thread + if(callback!=null){ + callback.run(e); + } + if(e instanceof Exception) throw (Exception)e; + if(e instanceof Error) throw (Error)e; + throw new Error(e); } finally { if (this.currentInterpreter.pausecount<=0) { - destroy(); + destroy(); } else { faction.setJSThread(null); } @@ -72,9 +96,9 @@ public String description() { if (f!=null) { - return f.definedAt(); + return type+", "+f.definedAt(); } - return "thread, f==null"; + return type; } public String toString() { Modified: branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/parse/Function.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/parse/Function.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/main/java/org/ibex/js/parse/Function.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -58,7 +58,7 @@ return this; } - public String definedAt(){return sourceName + ":" + firstLine;}; + public String definedAt(){return sourceName + ":" + firstLine;} // Debugging ////////////////////////////////////////////////////////////////////// // public String toString() { Deleted: branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSDynProxy.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSDynProxy.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/JSDynProxy.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -1,66 +0,0 @@ -package org.ibex.js; - - - -public class JSDynProxy extends JS.Obj { - /** fetches a write trap for property 'name' */ - private Trap wtrap(JS name) { - Trap t = getTrap(name); - return t==null?null:t.findWrite(); - } - - /** fetches a read trap for property 'name' */ - private Trap rtrap(JS name) { - Trap t = getTrap(name); - return t==null?null:t.findRead(); - } - - - public JS get(JS key) throws JSExn { - Trap rangeTrap = rtrap(SC_); - JSExn rangeTrapException = null; - JS value = null; - try { - if (rangeTrap != null) { - value = Scheduler.findCurrent().runBeforeGet(rangeTrap, key); - key = Scheduler.findCurrent().cascadedTo; - // if null value returned, avoiding innermost cascade - if (key == null) return value; - } - value = super.get(key); - } catch (JSExn e) { - rangeTrapException = e; - throw e; - } finally { - if (rangeTrap != null) { - // value in: cascaded back to the lowermost read trap - // value out: returned from the outer most read trap - value = Scheduler.findCurrent().runAfterGet(value,rangeTrapException); - } - } - return value; - } - - - public void put(JS key, JS value) throws JSExn { - Trap rangeTrap = wtrap(SC_); - JSExn rangeTrapException = null; - try { - if (rangeTrap != null) { - value = Scheduler.findCurrent().runBeforePut(rangeTrap, value, key); - key = Scheduler.findCurrent().cascadedTo; - // returned from trap without cascading (cleaned up in finally clause) - if (key==null) return; - } - super.put(key,value); - } catch (JSExn e) { - rangeTrapException = e; - throw e; - } finally { - if (rangeTrap != null) { - Scheduler.findCurrent().runAfterPut(rangeTrapException); - } - } - } - -} Modified: branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/poke/java/org/ibex/js/RunJS.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -120,7 +120,7 @@ JS export = new JS.Obj(); JSFunction f = prepareRun(fileName, export); SCHEDULER = new Scheduler(LOG, true); - SCHEDULER.runInNew(f, null); + SCHEDULER.runInNew(f); Exception e = (Exception) SCHEDULER.run(); if(e!=null) throw e; @@ -346,7 +346,7 @@ public void put(JS jskey, JS val) throws JSExn { String key = JSU.toString(jskey); if("thread".equals(key)) { - SCHEDULER.runInNew(val,null); + SCHEDULER.runInNew(val); return; } /* @@ -386,7 +386,7 @@ } if("line".equals(methName)) { // TODO reimplement this - Interpreter I = SCHEDULER.findCurrentInterpreter(); + Interpreter I = SCHEDULER.expectCurrentInterpreter(); return JSU.N(I.f.line[I.pc]); } if("import".equals(methName)){ @@ -400,11 +400,12 @@ return new JSRegexp(args[0], args[1]); } if("pause".equals(methName)){ - final Callable callback = SCHEDULER.pauseJSThread("pause"); - new java.lang.Thread() { public void run() { - System.out.println("in pause"); - SCHEDULER.schedule(callback, null); - }}.start(); + SCHEDULER.backgroundCall("pause", new Callable<Object, JS>() { + public JS run(Object A) throws Exception { + System.out.println("in pause"); + return null; + } + }); } if( "xmlrpc".equals(methName)){ return new XMLRPC(LOG, JSU.toString(args[0]), ""); Modified: branches/vexi3/org.vexi-library.js/src/test/java/test/js/TestJS.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/test/js/TestJS.java 2015-02-16 09:55:52 UTC (rev 4764) +++ branches/vexi3/org.vexi-library.js/src/test/java/test/js/TestJS.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -12,6 +12,7 @@ import test.js.exec.TestExec; import test.js.parse.TestParse; +import test.js.threading.TestJSThreading; public class TestJS{ @@ -25,6 +26,7 @@ suite.addTest(TestExec.suite()); suite.addTest(JUnitUtil.suiteJava(TestParse.class)); suite.addTest(TestClasses.suite()); + suite.addTest(JUnitUtil.suiteJava(TestJSThreading.class)); return suite; } } Added: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/AssertionUtil.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/AssertionUtil.java (rev 0) +++ branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/AssertionUtil.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -0,0 +1,99 @@ +package test.js.threading; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +public class AssertionUtil { + static boolean shorterrors = true; + + static public String join(String... ss){ return join(Arrays.asList(ss)); } + + static public String join(Iterable<String> ss){ + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String s: ss){ + if(first) first = false; + else sb.append(","); + sb.append(s); + } + return sb.toString(); + } + + static public String fromList(Iterable l){ + if(!(l instanceof SortedSet)){ + Collection l1 = new TreeSet(); + for(Object o: l){ + l1.add(""+o); + } + l = l1; + } + return fromList((Collection)l); + } + + static public String fromList(Collection l0){ + // REMARK + // The dilema here is e.g. importing code where 100s of lines may have mismatches + // of 1000s of items. Really we don't want to create such long stack traces in these cases. + int max = shorterrors?50:1000; + Collection l; + if(!(l0 instanceof SortedSet)){ + Collection l1 = new TreeSet(); + int i=0; + for(Object o: l0){ + l1.add(""+o); + i++; + if(i>max) break; + } + l = l1; + }else{ + l = l0; + } + + StringBuilder r = new StringBuilder(); + int i=0; + for(Object o: l){ + if(i<max){ + r.append("\n "+o); + }else{ + r.append("\n [first "+max+" out of "+l0.size()+" shown]"); + } + i++; + } + if(r.length()==0){ + r.append("\n [no choices]"); + } + + return r.toString(); + } + + static private AssertionError mismatch(Set<String> expectedKeys, Set<String> actualKeys, Set<String> optionalKeys) { + Set<String> missingKeys = new HashSet(expectedKeys); + missingKeys.removeAll(actualKeys); + missingKeys.removeAll(optionalKeys); + Set<String> unexpectedKeys = new HashSet(actualKeys); + unexpectedKeys.removeAll(expectedKeys); + unexpectedKeys.removeAll(optionalKeys); + + String s = "Incompatible props sent."; + if(missingKeys.size()>0) s+= "\nMissing properties: " + fromList(missingKeys); + if(unexpectedKeys.size()>0) { + s+= "\nUnexpected properties: " + fromList(unexpectedKeys); + s+= "\nOptional properties: " + fromList(optionalKeys); + } + throw new AssertionError(s); + } + + static public void assertExpected(Set<String> expectedKeys, Set<String> actualKeys, Set<String> optionalKeys) { + if(!actualKeys.containsAll(expectedKeys)) + throw mismatch(expectedKeys, actualKeys, optionalKeys); + int c=0; + for(String s: optionalKeys){ + if(actualKeys.contains(s)) c++; + } + if(actualKeys.size()!=expectedKeys.size()+c) throw mismatch(expectedKeys, actualKeys, optionalKeys); + } +} Property changes on: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/AssertionUtil.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/Scripting.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/Scripting.java (rev 0) +++ branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/Scripting.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -0,0 +1,94 @@ +package test.js.threading; + +import java.io.Reader; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.ibex.js.ExecParser; +import org.ibex.js.JS; +import org.ibex.js.JSExn; +import org.ibex.js.JSFunction; +import org.ibex.js.JSON; +import org.ibex.js.JSU; +import org.ibex.js.Scheduler; +import org.ibex.js.parse.Function; +import org.ibex.js.standard.Global; +import org.ibex.util.Callable; +import org.ibex.util.DefaultLog; + + +public class Scripting{ + + + final public Set<String> logged = new LinkedHashSet(); + private JSFunction method; + + final JS LOG = new JS.Immutable(){ + public void put(JS key, JS value) throws JSExn { + logged.add(JSU.toString(key)); + }; + }; + final JS TESTOBJ = new JS.Immutable(){ + @Override public void put(JS keyJS, JS val) throws JSExn { + String key = JSU.toString(keyJS); + if("method".equals(key)){ + method = (JSFunction)val; + }else{ + super.put(keyJS, val); + } + } + }; + final JS GLOBAL = new Global() { + @Override public JS get(JS arg) throws JSExn { + String key = JSU.toString(arg); + if("pause".equals(key)) return METHOD; + if("print".equals(key)) return METHOD; + if("LOG".equals(key)) return LOG; + if("TESTOBJ".equals(key)) return TESTOBJ; + return super.get(arg); + } + @Override public JS callMethod(JS this_, JS method, JS[] args) throws JSExn { + String key = JSU.toString(method); + if("print".equals(key)){ + System.err.println(JSON.marshal(args[0])); + return null; + }else if("pause".equals(key)){ + SCHEDULER.backgroundCall("pause", new Callable<Object, JS>() { + public JS run(Object A) throws Exception { + System.out.println("in pause"); + return null; + } + }); + return null; + } + return super.callMethod(this_, method, args); + } + }; + + + final Scheduler SCHEDULER; + public Scripting() { + SCHEDULER = new Scheduler(DefaultLog.logger, true); + SCHEDULER.incForceActive(); + new java.lang.Thread(new Runnable(){ + public void run() { + SCHEDULER.run(); + System.err.println("Exiting..."); + } + },"Scripting").start(); + } + + + public void run(final String name, final Reader reader) throws Exception{ + final Function f = ExecParser.parse(name, 0, reader); + final JSFunction run = JSU.cloneWithNewGlobalScope(f, GLOBAL); + SCHEDULER.syncCall(run, null); + } + + public JS callMethod(boolean throwexn) throws Exception { + if(method==null){ + throw new Exception("method not initialised"); + } + return SCHEDULER.syncCall(method, new JS[]{JSU.B(throwexn)}); + } +} Property changes on: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/Scripting.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/TestJSThreading.java =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/TestJSThreading.java (rev 0) +++ branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/TestJSThreading.java 2015-02-25 20:11:16 UTC (rev 4765) @@ -0,0 +1,69 @@ +package test.js.threading; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.ibex.js.Fountain; +import org.ibex.js.JSExn; +import org.ibex.js.JSTestUtil; +import org.ibex.js.JSU; + + +public class TestJSThreading extends TestCase{ + + static public void main(String[] args) throws Exception { + TestJSThreading test = new TestJSThreading(); + test.setUp(); + test.testSyncCall(); + } + + final Fountain jspath; + private Scripting scripting; + + public TestJSThreading(){ + jspath = JSTestUtil.getResourceFountain(TestJSThreading.class, ".js"); + } + + + + void assertLogged(String expect){ + Assert.assertEquals(expect, AssertionUtil.join(scripting.logged)); + } + + @Override protected void setUp() throws Exception { + if(scripting==null){ + scripting = new Scripting(); + InputStream is = ((Fountain)jspath.get(JSU.S("script.js"))).getInputStream(true); + Reader reader = new InputStreamReader(is); + scripting.run("script.js", reader); + } + scripting.logged.clear(); + } + + void printLogged(){ + System.err.println("Logged Actions:-"); + for(String k: scripting.logged){ + System.err.println(" "+k); + } + } + + + public void testSyncCall() throws Exception{ + scripting.callMethod(false); + assertLogged("method1,method2,method3"); + + try{ + scripting.callMethod(true); + fail("Expected exn"); + }catch(JSExn e){ + assertEquals("JSExn: exn",e.getMessage()); + assertLogged("method1,method2,method3"); + } + + printLogged(); + } +} Property changes on: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/TestJSThreading.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/script.js =================================================================== --- branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/script.js (rev 0) +++ branches/vexi3/org.vexi-library.js/src/test/java/test/js/threading/script.js 2015-02-25 20:11:16 UTC (rev 4765) @@ -0,0 +1,17 @@ + + +TESTOBJ.method = function(throwexn){ + LOG.method1 = true; + pause(); + LOG.method2 = true; + try{ + if(throwexn){ + throw "exn"; + }else{ + return "done"; + } + }finally{ + LOG.method3 = true; + } +}; +print("setup"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ Dive into the World of Parallel Programming The Go Parallel Website, sponsored by Intel and developed in partnership with Slashdot Media, is your hub for all things parallel software development, from weekly thought leadership blogs to news, videos, case studies, tutorials and more. Take a look and join the conversation now. http://goparallel.sourceforge.net/ _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn