# HG changeset patch # User Jaroslav Tulach # Date 1383422992 -3600 # Node ID 32e050a07754c0610d88fbb28716039ea00a6714 # Parent 161772460817d364604ddf7a526ee82ed0f37f42 Implementing java.util.Timer via window.setTimeout(...) diff -r 161772460817 -r 32e050a07754 rt/emul/compact/src/main/java/java/util/Timer.java --- a/rt/emul/compact/src/main/java/java/util/Timer.java Sat Nov 02 16:43:48 2013 +0100 +++ b/rt/emul/compact/src/main/java/java/util/Timer.java Sat Nov 02 21:09:52 2013 +0100 @@ -26,6 +26,7 @@ package java.util; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; +import org.apidesign.bck2brwsr.core.JavaScriptBody; /** * A facility for threads to schedule tasks for future execution in a @@ -111,7 +112,7 @@ protected void finalize() throws Throwable { synchronized(queue) { thread.newTasksMayBeScheduled = false; - queue.notify(); // In case queue is empty. + thread.notifyQueue(1); // In case queue is empty. } } }; @@ -156,8 +157,6 @@ * @since 1.5 */ public Timer(String name) { - thread.setName(name); - thread.start(); } /** @@ -171,9 +170,6 @@ * @since 1.5 */ public Timer(String name, boolean isDaemon) { - thread.setName(name); - thread.setDaemon(isDaemon); - thread.start(); } /** @@ -407,7 +403,7 @@ queue.add(task); if (queue.getMin() == task) - queue.notify(); + thread.notifyQueue(1); } } @@ -429,10 +425,10 @@ synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.clear(); - queue.notify(); // In case queue was already empty. + thread.notifyQueue(1); // In case queue was already empty. } } - + /** * Removes all cancelled tasks from this timer's task queue. Calling * this method has no effect on the behavior of the timer, but @@ -478,7 +474,7 @@ * reschedules repeating tasks, and removes cancelled tasks and spent * non-repeating tasks from the queue. */ -class TimerThread extends Thread { +class TimerThread implements Runnable { /** * This flag is set to false by the reaper to inform us that there * are no more live references to our Timer object. Once this flag @@ -500,30 +496,41 @@ this.queue = queue; } + void notifyQueue(int delay) { + if (delay < 1) { + delay = 1; + } + setTimeout(delay, this); + } + + @JavaScriptBody(args = { "delay", "r" }, body = "window.setTimeout(function() { r.run__V(); }, delay);") + private static native void setTimeout(int delay, Runnable r); + public void run() { - try { - mainLoop(); - } finally { - // Someone killed this Thread, behave as if Timer cancelled - synchronized(queue) { - newTasksMayBeScheduled = false; - queue.clear(); // Eliminate obsolete references - } - } + mainLoop(1); +// try { +// mainLoop(0); +// } finally { +// // Someone killed this Thread, behave as if Timer cancelled +// synchronized(queue) { +// newTasksMayBeScheduled = false; +// queue.clear(); // Eliminate obsolete references +// } +// } } /** * The main timer loop. (See class comment.) */ - private void mainLoop() { - while (true) { + private void mainLoop(int inc) { + for (int i = 0; i < 1; i += inc) { try { TimerTask task; boolean taskFired; synchronized(queue) { // Wait for queue to become non-empty while (queue.isEmpty() && newTasksMayBeScheduled) - queue.wait(); + break; if (queue.isEmpty()) break; // Queue is empty and will forever remain; die @@ -548,12 +555,16 @@ } } } - if (!taskFired) // Task hasn't yet fired; wait - queue.wait(executionTime - currentTime); + if (!taskFired) { + // Task hasn't yet fired; wait + notifyQueue((int)(executionTime - currentTime)); + return; + } } if (taskFired) // Task fired; run it, holding no locks task.run(); - } catch(InterruptedException e) { + } catch(Exception e) { + e.printStackTrace(); } } } diff -r 161772460817 -r 32e050a07754 rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/TimerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/TimerTest.java Sat Nov 02 21:09:52 2013 +0100 @@ -0,0 +1,81 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.util.Timer; +import java.util.TimerTask; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class TimerTest { + int miss; + int exec; + + public TimerTest() { + } + + @BrwsrTest public void scheduleTick() throws Exception { + Timer t = new Timer("MyTest"); + class TT extends TimerTask { + @Override + public void run() { + exec++; + } + } + TT task = new TT(); + t.schedule(task, 15); + + if (exec == 0) { + miss++; + throw new InterruptedException(); + } + + assert exec == 1 : "One exec: " + exec; + assert miss == 1 : "One miss: " + miss; + } + + @BrwsrTest public void repeatedTicks() throws Exception { + Timer t = new Timer("MyTest"); + class TT extends TimerTask { + @Override + public void run() { + exec++; + } + } + TT task = new TT(); + t.scheduleAtFixedRate(task, 15, 10); + + if (exec != 2) { + miss++; + throw new InterruptedException(); + } + + assert exec == 2 : "Two execs: " + exec; + assert miss == 2 : "Two misses: " + miss; + } + + @Factory public static Object[] create() { + return VMTest.create(TimerTest.class); + } + +}