/**
* Copyright 2011-2012 Akiban Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.persistit;
import com.persistit.exception.PersistitException;
import com.persistit.util.Util;
/**
* Base class for the background threads that perform various IO tasks.
*
* @author peter
*
*/
abstract class IOTaskRunnable implements Runnable {
private final static long THREAD_DEATH_WAIT_INTERVAL = 5000;
protected final Persistit _persistit;
private volatile Thread _thread;
private boolean _stopped;
private boolean _notified;
private long _pollInterval;
private int _exceptionCount = 0;
private Exception _lastException;
protected IOTaskRunnable(final Persistit persistit) {
_persistit = persistit;
}
protected void start(final String name, final long pollInterval) {
_pollInterval = pollInterval;
_thread = new Thread(this, name);
_thread.start();
}
Thread getThread() {
return _thread;
}
synchronized void kick() {
if (!_notified) {
_notified = true;
notify();
}
}
public final synchronized long getPollInterval() {
return _pollInterval;
}
public final synchronized void setPollInterval(final long pollInterval) {
_pollInterval = pollInterval;
kick();
}
public final synchronized Exception getLastException() {
return _lastException;
}
public final synchronized int getExceptionCount() {
return _exceptionCount;
}
synchronized boolean lastException(final Exception exception) {
_exceptionCount++;
if (_lastException == null || exception == null || !_lastException.getClass().equals(exception.getClass())) {
_lastException = exception;
return true;
} else {
return false;
}
}
protected synchronized boolean isStopped() {
final Thread thread = _thread;
if (thread == null || !thread.isAlive()) {
return true;
}
return _stopped;
}
protected synchronized void setStopped() {
_stopped = true;
}
void join(final long millis) throws InterruptedException {
if (_thread != null) {
_thread.join(millis);
}
}
@SuppressWarnings("deprecation")
// Use only for tests.
protected void crash() {
final Thread thread = _thread;
for (int count = 0; (thread != null && thread.isAlive()); count++) {
if (count > 0) {
_persistit.getLogBase().crashRetry.log(count, _thread.getName());
}
thread.stop();
try {
thread.join(THREAD_DEATH_WAIT_INTERVAL);
} catch (final InterruptedException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void run() {
while (true) {
synchronized (this) {
_notified = false;
}
try {
/*
* Unit tests use a negative poll interval to prevent processing
* here
*/
if (getPollInterval() < 0) {
Util.spinSleep();
} else {
runTask();
}
} catch (final Exception e) {
if (lastException(e)) {
_persistit.getLogBase().exception.log(e);
}
}
final long lastCycleTime = System.currentTimeMillis();
synchronized (this) {
if (shouldStop()) {
_stopped = true;
kick();
break;
}
while (!shouldStop()) {
final long pollInterval = pollInterval();
if (_notified && pollInterval >= 0) {
break;
}
final long waitTime = pollInterval < 0 ? Persistit.SHORT_DELAY : lastCycleTime + pollInterval
- System.currentTimeMillis();
if (waitTime <= 0) {
break;
}
try {
wait(waitTime);
} catch (final InterruptedException ie) {
_persistit.getLogBase().exception.log(ie);
break;
}
}
}
}
try {
_persistit.closeSession();
} catch (final PersistitException e) {
_persistit.getLogBase().exception.log(e);
}
}
static void crash(final IOTaskRunnable task) {
if (task != null) {
task.crash();
}
}
protected long pollInterval() {
return getPollInterval();
}
protected abstract boolean shouldStop();
protected abstract void runTask() throws Exception;
}