1/*
2 * Copyright (c) 2009-2010 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32package com.jme3.app;
33
34import java.util.concurrent.*;
35import java.util.concurrent.locks.Condition;
36import java.util.concurrent.locks.ReentrantLock;
37import java.util.logging.Level;
38import java.util.logging.Logger;
39
40/**
41 * <code>AppTask</code> is used in <code>AppTaskQueue</code> to manage tasks that have
42 * yet to be accomplished. The AppTask system is used to execute tasks either
43 * in the OpenGL/Render thread, or outside of it.
44 *
45 * @author Matthew D. Hicks, lazloh
46 */
47public class AppTask<V> implements Future<V> {
48    private static final Logger logger = Logger.getLogger(AppTask.class
49            .getName());
50
51    private final Callable<V> callable;
52
53    private V result;
54    private ExecutionException exception;
55    private boolean cancelled, finished;
56    private final ReentrantLock stateLock = new ReentrantLock();
57    private final Condition finishedCondition = stateLock.newCondition();
58
59    /**
60     * Create an <code>AppTask</code> that will execute the given
61     * {@link Callable}.
62     *
63     * @param callable The callable to be executed
64     */
65    public AppTask(Callable<V> callable) {
66        this.callable = callable;
67    }
68
69    public boolean cancel(boolean mayInterruptIfRunning) {
70        stateLock.lock();
71        try {
72            if (result != null) {
73                return false;
74            }
75            cancelled = true;
76
77            finishedCondition.signalAll();
78
79            return true;
80        } finally {
81            stateLock.unlock();
82        }
83    }
84
85    public V get() throws InterruptedException, ExecutionException {
86        stateLock.lock();
87        try {
88            while (!isDone()) {
89                finishedCondition.await();
90            }
91            if (exception != null) {
92                throw exception;
93            }
94            return result;
95        } finally {
96            stateLock.unlock();
97        }
98    }
99
100    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
101        stateLock.lock();
102        try {
103            if (!isDone()) {
104                finishedCondition.await(timeout, unit);
105            }
106            if (exception != null) {
107                throw exception;
108            }
109            if (result == null) {
110                throw new TimeoutException("Object not returned in time allocated.");
111            }
112            return result;
113        } finally {
114            stateLock.unlock();
115        }
116    }
117
118    public boolean isCancelled() {
119        stateLock.lock();
120        try {
121            return cancelled;
122        } finally {
123            stateLock.unlock();
124        }
125    }
126
127    public boolean isDone() {
128        stateLock.lock();
129        try {
130            return finished || cancelled || (exception != null);
131        } finally {
132            stateLock.unlock();
133        }
134    }
135
136    public Callable<V> getCallable() {
137        return callable;
138    }
139
140    public void invoke() {
141        try {
142            final V tmpResult = callable.call();
143
144            stateLock.lock();
145            try {
146                result = tmpResult;
147                finished = true;
148
149                finishedCondition.signalAll();
150            } finally {
151                stateLock.unlock();
152            }
153        } catch (Exception e) {
154            logger.logp(Level.SEVERE, this.getClass().toString(), "invoke()", "Exception", e);
155
156            stateLock.lock();
157            try {
158                exception = new ExecutionException(e);
159
160                finishedCondition.signalAll();
161            } finally {
162                stateLock.unlock();
163            }
164        }
165    }
166
167}