com.sun.grizzly.async.AbstractAsyncQueueReader Maven / Gradle / Ivy
/*
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*/
package com.sun.grizzly.async;
import com.sun.grizzly.Controller;
import com.sun.grizzly.SelectorHandler;
import com.sun.grizzly.async.AsyncQueue.AsyncQueueEntry;
import com.sun.grizzly.util.FutureImpl;
import com.sun.grizzly.util.LinkedTransferQueue;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
/**
*
* @author oleksiys
*/
public abstract class AbstractAsyncQueueReader implements AsyncQueueReader {
protected SelectorHandler selectorHandler;
private AsyncQueue readQueue;
public AbstractAsyncQueueReader(SelectorHandler selectorHandler) {
this.selectorHandler = selectorHandler;
readQueue = new AsyncQueue();
}
/**
* {@inheritDoc}
*/
public Future read(SelectionKey key, ByteBuffer buffer,
AsyncReadCallbackHandler callbackHandler) throws IOException {
return read(key, buffer, callbackHandler, null);
}
/**
* {@inheritDoc}
*/
public Future read(SelectionKey key, ByteBuffer buffer,
AsyncReadCallbackHandler callbackHandler,
AsyncReadCondition condition) throws IOException {
return read(key, buffer, callbackHandler, condition, null);
}
/**
* {@inheritDoc}
*/
public Future read(SelectionKey key, ByteBuffer buffer,
AsyncReadCallbackHandler callbackHandler,
AsyncReadCondition condition,
AsyncQueueDataProcessor readPostProcessor) throws IOException {
if (key == null) {
throw new IOException("SelectionKey is null! " +
"Probably key was cancelled or connection was closed?");
}
FutureImpl future =
new FutureImpl();
SelectableChannel channel = (SelectableChannel) key.channel();
AsyncQueueEntry channelEntry =
readQueue.obtainAsyncQueueEntry(channel);
// Update statistics
channelEntry.totalElementsCount.incrementAndGet();
AsyncQueueReadUnit record = new AsyncQueueReadUnit();
LinkedTransferQueue queue = channelEntry.queue;
AtomicReference currentElement = channelEntry.currentElement;
ReentrantLock lock = channelEntry.queuedActionLock;
final int holdState = lock.getHoldCount();
// If AsyncQueue is empty - try to read ByteBuffer here
try {
OperationResult dstResult = channelEntry.tmpResult;
boolean isDirectReadCompleted = false;
if (currentElement.get() == null && // Weak comparison for null
lock.tryLock()) {
// Strong comparison for null, because we're in locked region
if (currentElement.compareAndSet(null, record)) {
// Do direct reading
do {
dstResult = doRead((ReadableByteChannel) channel, buffer,
readPostProcessor, dstResult);
channelEntry.processedDataSize.addAndGet(dstResult.bytesProcessed);
// If some data was read - we need to check "condition"
// Check is performed for each message separately, not like for TCP
if (dstResult.address != null &&
(!buffer.hasRemaining() || (condition != null &&
condition.checkAsyncReadCompleted(key,
dstResult.address, buffer)))) {
isDirectReadCompleted = true;
break;
}
} while (dstResult.address != null);
} else {
lock.unlock();
}
}
if (!isDirectReadCompleted && buffer.hasRemaining()) {
// Update statistics
channelEntry.queuedElementsCount.incrementAndGet();
record.set(buffer, callbackHandler, condition,
readPostProcessor, future);
boolean isRegisterForReading = false;
// add new element to the queue, if it's not current
if (currentElement.get() != record) {
queue.offer(record); // add to queue
if (!lock.isLocked()) {
isRegisterForReading = true;
}
} else { // if element was read direct (not fully read)
isRegisterForReading = true;
lock.unlock();
}
if (isRegisterForReading) {
registerForReading(key);
}
} else { // If there are no bytes available for reading
boolean isReregister = false;
// Update statistics
channelEntry.processedElementsCount.incrementAndGet();
// If buffer was read directly - set next queue element as current
if (lock.isHeldByCurrentThread()) {
AsyncQueueReadUnit nextRecord = queue.poll();
if (nextRecord != null) { // if there is something in queue
currentElement.set(nextRecord);
lock.unlock();
isReregister = true;
} else { // if nothing in queue
currentElement.set(null);
lock.unlock(); // unlock
if (queue.peek() != null) { // check one more time
isReregister = true;
}
}
}
// Notify callback handler
record.set(buffer, callbackHandler, condition,
readPostProcessor, future);
future.setResult(record);
if (callbackHandler != null) {
callbackHandler.onReadCompleted(key,
dstResult.address, record);
}
if (isReregister) {
registerForReading(key);
}
}
} catch(Exception e) {
if (record.callbackHandler != null) {
record.callbackHandler.onException(e, key,
buffer, queue);
}
onClose(channel);
if (e instanceof IOException) {
throw (IOException) e;
}
throw new IOException(e.getMessage());
} finally {
if (lock.isHeldByCurrentThread() && holdState < lock.getHoldCount()) {
lock.unlock();
}
}
return future;
}
/**
* {@inheritDoc}
*/
public boolean isReady(SelectionKey key) {
AsyncQueueEntry channelEntry =
readQueue.getAsyncQueueEntry(key.channel());
return channelEntry != null && (channelEntry.currentElement.get() != null ||
(channelEntry.queue != null && !channelEntry.queue.isEmpty()));
}
/**
* {@inheritDoc}
*/
public AsyncQueueEntry getAsyncQueue(SelectionKey key) {
return readQueue.getAsyncQueueEntry(key.channel());
}
/**
* {@inheritDoc}
*/
public void onRead(SelectionKey key) throws IOException {
SelectableChannel channel = key.channel();
AsyncQueueEntry channelEntry =
readQueue.obtainAsyncQueueEntry(channel);
LinkedTransferQueue queue = channelEntry.queue;
AtomicReference currentElement = channelEntry.currentElement;
ReentrantLock lock = channelEntry.queuedActionLock;
if (currentElement.get() == null) {
AsyncQueueReadUnit nextRecord = queue.peek();
if (nextRecord != null && lock.tryLock()) {
if (!queue.isEmpty() &&
currentElement.compareAndSet(null, nextRecord)) {
queue.remove();
}
} else {
return;
}
} else if (!lock.tryLock()) {
return;
}
try {
OperationResult dstResult = channelEntry.tmpResult;
while (currentElement.get() != null) {
AsyncQueueReadUnit queueRecord = currentElement.get();
ByteBuffer byteBuffer = queueRecord.byteBuffer;
AsyncQueueDataProcessor readPostProcessor = queueRecord.readPostProcessor;
try {
doRead((ReadableByteChannel) channel, byteBuffer, readPostProcessor, dstResult);
channelEntry.processedDataSize.addAndGet(dstResult.bytesProcessed);
} catch (Exception e) {
if (queueRecord.callbackHandler != null) {
queueRecord.callbackHandler.onException(e, key,
byteBuffer, queue);
} else {
Controller.logger().log(Level.SEVERE,
"Exception occured when executing " +
"asynchronous queue reading", e);
}
onClose(channel);
}
// check if buffer was completely read
AsyncReadCondition condition = queueRecord.condition;
if (!byteBuffer.hasRemaining() || (condition != null &&
condition.checkAsyncReadCompleted(key, dstResult.address, byteBuffer))) {
currentElement.set(queue.poll());
((FutureImpl) queueRecord.future).setResult(queueRecord);
// Update statistics
channelEntry.processedElementsCount.incrementAndGet();
if (queueRecord.callbackHandler != null) {
queueRecord.callbackHandler.onReadCompleted(
key, dstResult.address, queueRecord);
}
// If last element in queue is null - we have to be careful
if (currentElement.get() == null) {
lock.unlock();
AsyncQueueReadUnit nextRecord = queue.peek();
if (nextRecord != null && lock.tryLock()) {
if (!queue.isEmpty() &&
currentElement.compareAndSet(null, nextRecord)) {
queue.remove();
}
continue;
} else {
break;
}
}
} else { // if there is still some data in current buffer
lock.unlock();
registerForReading(key);
break;
}
}
} finally {
if (lock.isHeldByCurrentThread()) {
channelEntry.queuedActionLock.unlock();
}
}
}
/**
* {@inheritDoc}
*/
public void onClose(SelectableChannel channel) {
readQueue.removeEntry(channel);
}
/**
* {@inheritDoc}
*/
public void close() {
readQueue.clear();
readQueue = null;
}
protected abstract OperationResult doRead(ReadableByteChannel channel,
ByteBuffer byteBuffer, AsyncQueueDataProcessor readPostProcessor,
OperationResult dstResult) throws IOException;
private void registerForReading(SelectionKey key) {
selectorHandler.register(key, SelectionKey.OP_READ);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy