All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.v5.amp.queue.QueueRingResizing Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine 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.
 *
 * Baratine 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.amp.queue;

import java.util.concurrent.TimeUnit;

import com.caucho.v5.amp.deliver.Deliver;
import com.caucho.v5.amp.deliver.MessageDeliver;
import com.caucho.v5.amp.deliver.Outbox;
import com.caucho.v5.amp.deliver.QueueRing;
import com.caucho.v5.amp.deliver.WorkerDeliver;
import com.caucho.v5.amp.spi.ShutdownModeAmp;
import com.caucho.v5.amp.thread.ThreadPool;
import com.caucho.v5.util.L10N;

/**
 * Value queue with atomic reference.
 */
public final class QueueRingResizing
  extends QueueRingBase
{
  private static final L10N L = new L10N(QueueRingResizing.class);
  
  private final int _minCapacity;
  private final int _maxCapacity;
  
  private int _capacity;
  
  private final CounterBuilder _counterBuilder;
  private final RingBlocker _baseBlocker;

  private volatile QueueRingFixed _readQueue;
  
  private final Object _resizeLock = new Object();
  private volatile QueueRingFixed _writeQueue;
  
  public QueueRingResizing(int minCapacity, int maxCapacity)
  {
    this(minCapacity, 
         maxCapacity,
         CounterBuilder.create(1));
  }
  
  public QueueRingResizing(int minCapacity, 
                           int maxCapacity,
                           CounterBuilder counterBuilder)
  {
    this(minCapacity, 
         maxCapacity,
         counterBuilder,
         new RingBlockerBasic());
  }
    
  public QueueRingResizing(int minCapacity,
                                   int maxCapacity,
                                   CounterBuilder counterBuilder,
                                   RingBlocker blocker)
  {
    if (Integer.bitCount(minCapacity) != 1 || minCapacity < 2) {
      throw new IllegalArgumentException(L.l("Invalid min capacity {0}",
                                             Long.toHexString(minCapacity)));
    }
    
    if (Integer.bitCount(maxCapacity) != 1 || maxCapacity < 2) {
      throw new IllegalArgumentException(L.l("Invalid max capacity {0}",
                                             Long.toHexString(maxCapacity)));
    }
    
    if (maxCapacity < minCapacity) {
      throw new IllegalArgumentException(L.l("Invalid min {0} and max {1} capacity",
                                             minCapacity,
                                             maxCapacity));
    }

    if (blocker == null) {
      throw new NullPointerException(L.l("RingBlocker is required"));
    }

    _minCapacity = minCapacity;
    _maxCapacity = maxCapacity;
    
    _capacity = _minCapacity;
    
    _baseBlocker = blocker;
    
    _counterBuilder = counterBuilder;

    _writeQueue = createQueue(_capacity, 0);
    _readQueue = _writeQueue;
  }
  
  public static > 
  QueueRing create(int min, int max)
  {
    return create(min, max,
                  CounterBuilder.create(1),
                  new RingBlockerBasic());
  }
    
  public static > 
  QueueRing create(int min, 
                        int max,
                        CounterBuilder counterBuilder,
                        RingBlocker blocker)
  {
    if (min == max) {
      return new QueueRingFixed(max, counterBuilder, 0, blocker);
    }
    else {
      return new QueueRingResizing(min, max, counterBuilder, blocker);
    }
  }
  
  /*
  @Override
  public int getOfferReserve()
  {
    return _readQueue.getOfferReserve();
  }
  */
  
  /*
  @Override
  public WorkerDeliverLifecycle worker()
  {
    return _baseBlocker;
  }
  */
  
  @Override
  public void wake()
  {
    _baseBlocker.wake();
  }

  @Override
  public boolean isEmpty()
  {
    return _writeQueue.isEmpty();
  }
  
  @Override
  public int size()
  {
    // bfs/1017 - worker start
    //return _writeQueue.size();
    
    /*
    QueueRing writeQueue = _writeQueue;
    QueueRing readQueue = _readQueue;
    
    if (writeQueue == readQueue) {
      return readQueue.size();
    }
    else {
      return writeQueue.size() + readQueue.size();
    }*/
    
    return _readQueue.size();
  }
  
  public long head()
  {
    return _readQueue.head();
  }
  
  @Override
  public final boolean offer(M value, long timeout, TimeUnit unit)
  {
    QueueRingFixed writeQueue;
    do {
      writeQueue = _writeQueue;
      
      if (writeQueue.offer(value, timeout, unit)) {
        return true;
      }
    } while (writeQueue.isWriteClosed());
    
    return false;
  }
  
  @Override
  public final M peek()
  {
    QueueRingFixed queue = _readQueue;
    
    M value = queue.peek();
    
    if (value != null) {
      return value;
    }
    
    if (pollResize(queue)) {
      value = _readQueue.peek();
    }
    
    return value;
  }
 
  @Override
  public final M poll(long timeout, TimeUnit unit)
  {
    QueueRingFixed readQueue = _readQueue;

    M value = readQueue.poll(timeout, unit);
    
    while (value == null && pollResize(readQueue)) {
      readQueue = _readQueue;
      
      value = readQueue.poll(timeout, unit);
    }
    
    return value;
  }
  
  @Override
  public void deliver(Deliver deliver, Outbox outbox)
    throws Exception
  {
    QueueRingFixed readQueue;
    
    do {
      readQueue = _readQueue;
      
      readQueue.deliver(deliver, outbox);
    } while (pollResize(readQueue));
  }
  
  @Override
  public void deliver(Deliver processor,
                      Outbox outbox,
                      int headIndex,
                      int tailIndex,
                      WorkerDeliver nextWorker,
                      boolean isTail)
    throws Exception
  {
    QueueRingFixed readQueue;
    
    // the loop is required on resize because the wake assumes the deliver
    // will consume at least one item if it was available at the time of the
    // wake.
      
    do {
      readQueue = _readQueue;
      
      readQueue.deliver(processor,
                        outbox,
                        headIndex, 
                        tailIndex, 
                        nextWorker, 
                        isTail);
    } while (isTail && pollResize(readQueue));
  }

  /*
  @Override
  public void deliverMulti(DeliverOutbox processor,
                           Outbox outbox,
                           int headIndex,
                           int tailIndex,
                           WorkerOutbox tailWorker)
    throws Exception
  {
    QueueRing readQueue;
    
    readQueue = _readQueue;

    readQueue.deliverMulti(processor,
                           outbox,
                           headIndex,
                           tailIndex,
                           tailWorker);
  }
  
  @Override
  public void deliverMultiTail(DeliverOutbox processor,
                               Outbox outbox,
                               int headIndex,
                               int tailIndex,
                               WorkerOutbox tailWorker)
    throws Exception
  {
    QueueRing readQueue;
    
    do {
      readQueue = _readQueue;

      readQueue.deliverMultiTail(processor,
                                 outbox,
                                 headIndex,
                                 tailIndex,
                                 tailWorker);
    } while (pollResize(readQueue));
  }
  */
  @Override
  public final int counterGroupSize()
  {
    return _readQueue.counterGroupSize();
  }
  
  private QueueRingFixed createQueue(int capacity,
                                   long initialIndex)
  {
    if (_maxCapacity <= capacity) {
      return new QueueRingFixed(_maxCapacity, 
                              _counterBuilder,
                              initialIndex,
                              _baseBlocker);
    }
    else {
      ResizingRingBlocker resizingBlocker
        = new ResizingRingBlocker(capacity, initialIndex, _writeQueue);
      
      return resizingBlocker.getQueue();
    }
  }
  
  private boolean pollResize(QueueRingFixed readQueue)
  {
    synchronized (_resizeLock) {
      if (readQueue == _readQueue && readQueue != _writeQueue) {
        if (readQueue.isEmpty()) {
          _readQueue = _writeQueue;
          
          _baseBlocker.offerWake();
          _baseBlocker.pollWake();
          
          return true;
        }
      }
    }

    return false;
  }
  
  private void copyQueue(M item, QueueRingFixed queue)
  {
    long timeout = 10;
    
    offer(item, timeout, TimeUnit.SECONDS);
    
    while ((item = queue.poll()) != null) {
      offer(item, timeout, TimeUnit.SECONDS);
    }
  }
  
  public final void shutdown(ShutdownModeAmp mode)
  {
    _baseBlocker.shutdown(mode);
  }
  
  private class ResizingRingBlocker implements RingBlocker {
    private final QueueRingFixed _queue;
    
    ResizingRingBlocker(int capacity,
                        long initialIndex,
                        QueueRingFixed prevQueue)
    {
      _queue = new QueueRingFixed(capacity, 
                                _counterBuilder, initialIndex, 
                                     this);
    }
    
    QueueRingFixed getQueue()
    {
      return _queue;
    }
    
    @Override
    public final long nextOfferSequence()
    {
      return 0;
    }
    
    @Override
    public final boolean offerWait(long offerSequence,
                                   long timeout,
                                   TimeUnit unit)
    {
      synchronized (_resizeLock) {
        QueueRingFixed queue = _queue;

        if (queue.isWriteClosed()) {
          // queue was already resized
          return false;
        }
        else if (queue.size() + 1 < queue.getCapacity()) {
          // space available
          return true;
        }
        else if (queue.getCapacity() < _maxCapacity) {
          queue.closeWrite();

          QueueRingFixed nextQueue = createQueue(4 * queue.getCapacity(),
                                               queue.getHeadAlloc());

          if (queue != _readQueue) {
            // if unshifted, copy the items
            M item;

            while ((item = queue.poll()) != null) {
              // System.out.println("COPY: " + item);

              if (! nextQueue.offer(item)) {
                M firstItem = item;
                
                ThreadPool.current().schedule(()->copyQueue(firstItem, queue));
                break;
              }
            }
          }

          _writeQueue = nextQueue;

          return false;
        }
        else {
          return false;
        }
      }
    }

    @Override
    public final void offerWake()
    {
    }

    @Override
    public final boolean wake()
    {
      return true;
    }

    @Override
    public final void wakeAll()
    {
      wake();
    }
    
    @Override
    public final long nextPollSequence()
    {
      return _baseBlocker.nextPollSequence();
    }

    @Override
    public final boolean pollWait(long sequence,
                                  long timeout, 
                                  TimeUnit unit)
    {
      return _baseBlocker.pollWait(sequence, timeout, unit);
    }
    
    @Override
    public final boolean isPollWait()
    {
      return _baseBlocker.isPollWait();
    }

    @Override
    public void pollWake()
    {
      _baseBlocker.pollWake();
    }

    /*
    @Override
    public void onActive()
    {
      _baseBlocker.onActive();
    }

    @Override
    public void onInit()
    {
      _baseBlocker.onInit();
    }
    */

    @Override
    public void shutdown(ShutdownModeAmp mode)
    {
      _baseBlocker.shutdown(mode);
    }
    
    @Override
    public String toString()
    {
      return getClass().getSimpleName() + "[" + _writeQueue + "]";
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy