/*
* Copyright (C) 2015 AChep@xda <artemchep@gmail.com>
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.achep.base.async;
import android.support.annotation.NonNull;
import com.achep.base.interfaces.IThreadFinishable;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import timber.log.Timber;
/**
* Created by Artem Chepurnoy on 17.04.2015.
*/
public abstract class TaskQueueThread<T> extends Thread implements IThreadFinishable {
private static final String TAG = "TaskQueueThread";
private final Queue<T> mQueue = new ConcurrentLinkedQueue<>();
private boolean mWaiting = false;
protected boolean mRunning = true;
@Override
public void finish() {
finish(false);
}
public void finish(boolean clearAllTasks) {
if (isAlive()) {
mRunning = false;
if (clearAllTasks) {
synchronized (this) {
mQueue.clear();
if (mWaiting) notifyAll();
}
}
while (true) {
try {
join();
break;
} catch (InterruptedException e) { /* pretty please! */ }
}
}
}
@Override
public void run() {
Timber.tag(TAG).d("Starting thread...");
super.run();
Queue<T> queue = new ConcurrentLinkedQueue<>();
while (mRunning) {
synchronized (this) {
if (mQueue.isEmpty())
try {
// Wait for a next #sendEvent(Event),
// where this thread will be unlocked.
mWaiting = true;
wait();
} catch (InterruptedException ignored) {
} finally {
mWaiting = false;
}
// Move all pending events to a local copy, so we don't need
// to block main queue.
while (!mQueue.isEmpty()) {
queue.add(mQueue.poll());
}
}
if (isLost()) {
mRunning = false;
break;
}
Iterator<T> iterator = queue.iterator();
while (iterator.hasNext()) {
T object = iterator.next();
// ~~
onHandleTask(object);
// ~~
iterator.remove();
}
}
Timber.tag(TAG).d("Stopping thread...");
}
public void sendTask(@NonNull T object) {
synchronized (this) {
mQueue.add(object);
// Release the thread lock if needed.
if (mWaiting) notifyAll();
}
}
public void clearTask(@NonNull T object) {
synchronized (this) {
mQueue.remove(object);
}
}
public void clearAllTasks() {
synchronized (this) {
mQueue.clear();
}
}
protected abstract void onHandleTask(T object);
protected abstract boolean isLost();
}