/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.util;
import com.codename1.io.Util;
import com.codename1.ui.Display;
import java.util.ArrayList;
/**
* An easy API for working with threads similar to call serially/and wait that allows us to
* create a thread and dispatch tasks to it.
*
* @author Shai Almog
*/
public class EasyThread {
private Thread t;
private boolean running = true;
private ArrayList<Object> queue = new ArrayList<Object>();
private static final Object LOCK = new Object();
private EasyThread(String name) {
t = Display.getInstance().startThread(new Runnable() {
public void run() {
Object current = null;
Object resultCallback = null;
while(running) {
synchronized(LOCK) {
if(queue.size() > 0) {
current = queue.get(0);
if(current instanceof RunnableWithResult) {
resultCallback = queue.get(1);
queue.remove(0);
}
queue.remove(0);
} else {
Util.wait(LOCK);
}
}
if(current != null) {
if(current instanceof Runnable) {
((Runnable)current).run();
} else {
((RunnableWithResult)current).run((SuccessCallback)resultCallback);
}
}
}
}
}, name);
t.start();
}
/**
* Starts a new thread
* @param name the display name for the thread
* @return a new thread instance
*/
public static EasyThread start(String name) {
return new EasyThread(name);
}
/**
* Runs the given object asynchronously on the thread and returns the result object
* @param r runs this method
* @param t object is passed to the success callback
*/
public <T> void run(RunnableWithResult<T> r, SuccessCallback<T> t) {
synchronized(LOCK) {
queue.add(r);
queue.add(t);
LOCK.notify();
}
}
/**
* Runs the given runnable on the thread, the method returns immediately
* @param r the runnable
*/
public void run(Runnable r) {
synchronized(LOCK) {
queue.add(r);
LOCK.notify();
}
}
/**
* Runs the given runnable on the thread and blocks until it completes, returns the value object
*
* @param r the runnable with result that will execute on the thread
* @return value returned by r
*/
public <T> T run(final RunnableWithResultSync<T> r) {
// we need the flag and can't use the result object. Since null would be a valid value for the result
// we would have a hard time of detecting the case of the code completing before the wait call
final boolean[] flag = new boolean[1];
final Object[] result = new Object[1];
final SuccessCallback<T> sc = new SuccessCallback<T>() {
public void onSucess(T value) {
synchronized(flag) {
result[0] = value;
flag[0] = true;
flag.notify();
}
}
};
RunnableWithResult<T> rr = new RunnableWithResult<T>() {
public void run(SuccessCallback<T> onSuccess) {
sc.onSucess(r.run());
}
};
synchronized(LOCK) {
queue.add(rr);
queue.add(sc);
LOCK.notify();
}
Display.getInstance().invokeAndBlock(new Runnable() {
public void run() {
synchronized(flag) {
if(!flag[0]) {
Util.wait(flag);
}
}
}
});
return (T)result[0];
}
/**
* Invokes the given runnable on the thread and waits for its execution to complete
* @param r the runnable
*/
public void runAndWait(final Runnable r) {
final boolean[] flag = new boolean[1];
synchronized(LOCK) {
queue.add(new Runnable() {
public void run() {
r.run();
synchronized(flag) {
flag[0] = true;
flag.notify();
}
}
});
LOCK.notify();
}
Display.getInstance().invokeAndBlock(new Runnable() {
public void run() {
synchronized(flag) {
if(!flag[0]) {
Util.wait(flag);
}
}
}
});
}
/**
* Stops the thread once the current task completes
*/
public void kill() {
synchronized(LOCK) {
running = false;
LOCK.notify();
}
}
}