public boolean isReady(SelectionKey key) {
    AsyncQueueEntry channelEntry = readQueue.getAsyncQueueEntry(key.channel());

    return channelEntry != null
        && (channelEntry.currentElement.get() != null
            || (channelEntry.queue != null && !channelEntry.queue.isEmpty()));
  }
  public Future<AsyncQueueReadUnit> 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<AsyncQueueReadUnit> future = new FutureImpl<AsyncQueueReadUnit>();

    SelectableChannel channel = (SelectableChannel) key.channel();
    AsyncQueueEntry channelEntry = readQueue.obtainAsyncQueueEntry(channel);

    // Update statistics
    channelEntry.totalElementsCount.incrementAndGet();

    AsyncQueueReadUnit record = new AsyncQueueReadUnit();

    final Queue<AsyncQueueReadUnit> queue = channelEntry.queue;
    final AtomicReference<AsyncQueueReadUnit> 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;
  }
 public void close() {
   readQueue.clear();
   // readQueue = null;
 }
 public void onClose(SelectableChannel channel) {
   readQueue.removeEntry(channel);
 }
  public void onRead(SelectionKey key) throws IOException {
    SelectableChannel channel = key.channel();

    AsyncQueueEntry channelEntry = readQueue.obtainAsyncQueueEntry(channel);

    final Queue<AsyncQueueReadUnit> queue = channelEntry.queue;
    final AtomicReference<AsyncQueueReadUnit> 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();
      }
    }
  }
 public AsyncQueueEntry getAsyncQueue(SelectionKey key) {
   return readQueue.getAsyncQueueEntry(key.channel());
 }