/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.store.statement.xa;
// Java 2 standard packages
import java.io.*;
import java.util.LinkedList;
// Third party packages
import org.apache.log4j.Logger;
final class TripleWriteThread extends Thread {
/**
* Logger.
*/
private final static Logger logger = Logger.getLogger(TripleWriteThread.class);
private static final int BUFFER_SIZE = 50000;
private static final int QUEUE_MAX_BUFFERS = 10;
/** The current phase of the TripleAVLFile. */
private TripleAVLFile.Phase phase;
private File file;
private long[][] buffer = null;
private int index = 0;
/** The queue of triples to add to the TripleAVLFile. */
private LinkedList<long[][]> queue = new LinkedList<long[][]>();
private boolean threadRunning = true;
private boolean processing = false;
private Throwable t = null;
TripleWriteThread(File file) {
super("TripleWriteThread: " + file);
this.file = file;
setDaemon(true);
start();
}
synchronized void setPhase(TripleAVLFile.Phase phase) {
if (buffer != null || processing || !queue.isEmpty()) {
throw new IllegalStateException(
"Attempt to change phase while still processing. " + getName()
);
}
checkForException(); // This should never detect an exception.
this.phase = phase;
}
void addTriple(long[] triple) {
if (buffer == null) {
buffer = new long[BUFFER_SIZE][];
index = 0;
}
buffer[index++] = triple;
if (index == buffer.length) {
synchronized (this) {
checkForException();
// Make sure the Queue doesn't exceed its maximum size.
if (queue.size() == QUEUE_MAX_BUFFERS) {
if (logger.isInfoEnabled()) {
logger.info("Triple write queue full for file: " + file + " Waiting.");
}
// Wait for the Queue to be less than full.
do {
try {
wait();
} catch (InterruptedException ie) {
throw new RuntimeException("Exception in " + getName(), ie);
}
} while (queue.size() == QUEUE_MAX_BUFFERS);
}
// Put the buffer in the queue.
if (queue.isEmpty()) {
// Transition from empty to not empty.
notifyAll();
}
queue.addLast(buffer);
}
buffer = null;
}
}
public void run() {
try {
for (;;) {
long[][] buffer;
synchronized (this) {
if (queue.isEmpty()) {
// Queue is empty so processing is complete.
processing = false;
notifyAll();
// Wait for more triples to process.
do {
wait();
} while (queue.isEmpty());
}
if (queue.size() == QUEUE_MAX_BUFFERS) {
// Transition from full to not full.
notifyAll();
}
// Get the next triple to process.
buffer = (long[][])queue.removeFirst();
processing = true;
}
try {
phase.syncAddTriples(buffer);
} catch (Throwable t) {
reportException(t);
} finally {
buffer = null;
}
}
} catch (InterruptedException ie) {
// Allow the thread to terminate silently.
} catch (Throwable t) {
logger.error("Unhandled exception in " + getName(), t);
}
synchronized (this) {
threadRunning = false;
notifyAll();
}
}
private synchronized void reportException(Throwable t) {
this.t = t;
logger.error("Exception in TripleWriteThread", t);
queue.clear();
processing = false;
notifyAll();
}
private void checkForException() {
if (this.t != null) {
Throwable t = this.t;
this.t = null;
throw new RuntimeException("Exception in " + getName(), t);
}
checkThreadRunning();
}
private void checkThreadRunning() {
if (!threadRunning)
throw new RuntimeException("Thread not running: " + getName());
}
synchronized void abort() {
buffer = null;
queue.clear();
checkThreadRunning();
try {
while (processing) {
wait();
checkThreadRunning();
}
} catch (InterruptedException ex) {
throw new RuntimeException("Interrupted", ex);
}
if (t != null) {
// Log any exception that occurred in the TripleWriteThread.
logger.warn("Exception during abort of " + getName(), t);
t = null;
}
}
synchronized void drain() {
checkForException();
if (buffer != null) {
if (index > 0) {
long[][] newBuffer = new long[index][];
System.arraycopy(buffer, 0, newBuffer, 0, index);
if (queue.isEmpty()) notifyAll();
queue.addLast(newBuffer);
}
buffer = null;
}
try {
while (processing || !queue.isEmpty()) {
wait();
checkForException();
}
} catch (InterruptedException ex) {
throw new RuntimeException("Interrupted", ex);
}
}
synchronized void close() {
try {
abort();
} catch (Throwable t) {
// Log and ignore.
logger.warn("Exception while shutting down " + getName(), t);
}
phase = null;
// Request the thread to exit.
interrupt();
}
}