/** * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2014 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.html5player.client.threading; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map.Entry; import org.catrobat.html5player.client.CatrobatDebug; import com.google.gwt.core.client.Scheduler.RepeatingCommand; public class CatScheduler implements RepeatingCommand { public final static int SCHEDULE_DELAY = 1; //milliseconds private static CatScheduler instance = null; private LinkedHashMap<String, CatThread> threadMap; private boolean alive = true; private CatThread currentThread = null; private int currentThreadIndex = -1; //########################################################################## /** * */ private CatScheduler() { threadMap = new LinkedHashMap<String, CatThread>(); } /** * * @return */ public static CatScheduler get() { if(instance == null) { instance = new CatScheduler(); } return instance; } //########################################################################## @Override public boolean execute() { long start = System.currentTimeMillis(); if(currentThread == null && getThreadCount() == 0) return alive; ArrayList<CatThread> threads = new ArrayList<CatThread>(threadMap.values()); // if(currentThread == null || getThreadCount() == 1) { // currentThread = threads.get(0); // currentThreadIndex = 0; // } // else { // int newCurrentThreadIndex = (threads.indexOf(currentThread) + 1) % getThreadCount(); // currentThread = threads.get(newCurrentThreadIndex); // } currentThreadIndex = (currentThreadIndex + 1) % getThreadCount(); currentThread = threads.get(currentThreadIndex); if(currentThread.getStatus() == CatThread.READY) { currentThread.run(); CatrobatDebug.debug("currentThreadIndex: " + currentThreadIndex); CatrobatDebug.info("One thread execution took " + (System.currentTimeMillis() - start) + " ms, thread: " + currentThread.getName()); //return alive; } else if(currentThread.getStatus() == CatThread.SLEEPING) { CatrobatDebug.debug("currentThread is sleeping"); //return alive; } //else if: possibly take the next thread?? cleanUpThreads(); CatrobatDebug.debug("currentThreadIndex after cleanUp: " + currentThreadIndex); return alive; } //########################################################################## public ArrayList<CatThread> getThreads() { return (ArrayList<CatThread>) threadMap.values(); } /** * * @param thread */ public void schedule(CatThread thread) { if(!threadMap.containsKey(thread.getName())) { threadMap.put(thread.getName(), thread); } } //########################################################################## /** * */ public void killScheduler() { alive = false; killThreads(); } /** * */ public void killThreads() { //kill all threads for(CatThread thread : threadMap.values()) { thread.kill(); } cleanUpThreads(); } /** * */ private void cleanUpThreads() { Iterator<Entry<String, CatThread>> iterator = threadMap.entrySet().iterator(); int removedThreadsBeforeCurrentThread = 0; int maxCheckedIndex = 0; while(iterator.hasNext()) { Entry<String, CatThread> entry = (Entry<String, CatThread>)iterator.next(); if(entry.getValue().getStatus() == CatThread.DESTROY) { checkIfThreadToRemoveIsCurrentThread(entry.getValue()); if(maxCheckedIndex < currentThreadIndex) { removedThreadsBeforeCurrentThread++; } iterator.remove(); } maxCheckedIndex++; } // checkThreadCount(); CatrobatDebug.debug("currentThreadIndex: " + currentThreadIndex); setCurrentThreadIndexAfterRemoval(removedThreadsBeforeCurrentThread); CatrobatDebug.debug("currentThreadIndex after removal: " + currentThreadIndex); boolean debugYes = true; if(currentThread != null) debugYes = false; CatrobatDebug.debug("SCHEDULER: removed " + removedThreadsBeforeCurrentThread + " threads before currentThread, currentThread removed: " + debugYes); } /** * Sets the status of the thread to DESTROY if it is already added to the * scheduler */ public void killThread(String threadName) { if(threadMap.containsKey(threadName)) { threadMap.get(threadName).kill(); } } /** * checks if the thread to remove is the currentThread, * @param threadToRemove */ private void checkIfThreadToRemoveIsCurrentThread(CatThread threadToRemove) { if(currentThread != null && threadToRemove.equals(currentThread)) { currentThread = null; } } /** * checks if currentThread equals null (currentThread got removed from map) * and sets the currentThreadIndex according to the number of threads which * are scheduled. If the currentThread got not removed, the * currentThreadIndex is calculated of the currentThreadIndex minus the * number of threads which where removed before the currentThread * * @param removedThreadsBeforeCurrentThread */ private void setCurrentThreadIndexAfterRemoval(int removedThreadsBeforeCurrentThread) { //current thread got removed if(currentThread == null) { if((getThreadCount() == 0) || (getThreadCount() == 1)) { currentThreadIndex = -1; } else { currentThreadIndex -= (removedThreadsBeforeCurrentThread + 1); } } else { //current thread got not removed currentThreadIndex -= removedThreadsBeforeCurrentThread; } } //########################################################################## public LinkedHashMap<String, CatThread> getThreadMap() { return threadMap; } public void setThreadMap(LinkedHashMap<String, CatThread> threadMap) { this.threadMap = threadMap; } /** * */ public int getThreadCount() { return threadMap.size(); } /** * * @param threadName * @return */ public CatThread getThread(String threadName) { if(threadMap.containsKey(threadName)) { return threadMap.get(threadName); } return null; } /** * @return last thread which got executed, null if thread finished or no * threads were added to the scheduler */ public CatThread getCurrentThread() { return this.currentThread; } /** * @return index of the currentThread */ public int getCurrentThreadIndex() { return this.currentThreadIndex; } //########################################################################## /** * FOR UNIT TESTING */ public void clear() { threadMap.clear(); currentThread = null; currentThreadIndex = -1; } /** * FOR UNIT TESTING */ public void reviveScheduler() { alive = true; } //########################################################################## }