Package net.tomp2p.connection

Source Code of net.tomp2p.connection.Reservation$WaitReservationPermanent

/*
* Copyright 2013 Thomas Bocek
*
* 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 net.tomp2p.connection;

import io.netty.channel.EventLoopGroup;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import net.tomp2p.futures.BaseFutureAdapter;
import net.tomp2p.futures.FutureChannelCreator;
import net.tomp2p.futures.FutureDone;
import net.tomp2p.p2p.RequestConfiguration;
import net.tomp2p.p2p.RoutingConfiguration;

/**
* Reserves a block of connections.
*
* @author Thomas Bocek
*
*/
public class Reservation {

  private final int maxPermitsUDP;
  private final int maxPermitsTCP;
  private final int maxPermitsPermanentTCP;

  private final Semaphore semaphoreUPD;
  private final Semaphore semaphoreTCP;
  private final Semaphore semaphorePermanentTCP;

  private final ChannelClientConfiguration channelClientConfiguration;

  private final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
  // single thread
  private final ExecutorService executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue);
  private final EventLoopGroup workerGroup;

  // we should be fair, otherwise we see connection timeouts due to unfairness
  // if busy
  private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
  private final Lock read = readWriteLock.readLock();
  private final Lock write = readWriteLock.writeLock();
  private boolean shutdown = false;
  private final Collection<ChannelCreator> channelCreators = Collections
          .synchronizedList(new ArrayList<ChannelCreator>());

  private final FutureDone<Void> futureReservationDone = new FutureDone<Void>();

  /**
   * Creates a new reservation class with the 3 permits.
   *
   * @param workerGroup
   *            The worker group for both UDP and TCP channels. This will not
   *            be shutdown in this class, you need to shutdown it outside.
   * @param channelClientConfiguration
   *            Sets maxPermitsUDP: the number of maximum short-lived UDP
   *            connections, maxPermitsTCP: the number of maximum short-lived
   *            TCP connections, maxPermitsPermanentTCP: the number of maximum
   *            permanent TCP connections
   */
  public Reservation(final EventLoopGroup workerGroup, final ChannelClientConfiguration channelClientConfiguration) {

    this.workerGroup = workerGroup;
    this.maxPermitsUDP = channelClientConfiguration.maxPermitsUDP();
    this.maxPermitsTCP = channelClientConfiguration.maxPermitsTCP();
    this.maxPermitsPermanentTCP = channelClientConfiguration.maxPermitsPermanentTCP();
    this.semaphoreUPD = new Semaphore(maxPermitsUDP);
    this.semaphoreTCP = new Semaphore(maxPermitsTCP);
    this.semaphorePermanentTCP = new Semaphore(maxPermitsPermanentTCP);
    this.channelClientConfiguration = channelClientConfiguration;
  }

  /**
   * @return The pending number of requests that are scheduled but not
   *         executed yet.
   */
  public int pendingRequests() {
    return queue.size();
  }

  /**
   * This will calculate the number of required connection for routing and
   * request messages.
   *
   * @param routingConfiguration
   *            Contains the number of routing requests in parallel
   * @param requestP2PConfiguration
   *            Contains the number of requests for P2P operations in parallel
   * @param builder
   *            The builder that tells us if we should use TCP or UPD
   * @return The future channel creator
   */
  public FutureChannelCreator create(final RoutingConfiguration routingConfiguration,
          final RequestConfiguration requestP2PConfiguration, final DefaultConnectionConfiguration builder) {
    if (routingConfiguration == null && requestP2PConfiguration == null) {
      throw new IllegalArgumentException("Both routingConfiguration and requestP2PConfiguration cannot be null");
    }

    int nrConnectionsTCP = 0;
    int nrConnectionsUDP = 0;
    if (requestP2PConfiguration != null) {
      if (builder.isForceUDP()) {
        nrConnectionsUDP = requestP2PConfiguration.parallel();
      } else {
        nrConnectionsTCP = requestP2PConfiguration.parallel();
      }
    }
    if (routingConfiguration != null) {
      if (!builder.isForceTCP()) {
        nrConnectionsUDP = Math.max(nrConnectionsUDP, routingConfiguration.parallel());
      } else {
        nrConnectionsTCP = Math.max(nrConnectionsTCP, routingConfiguration.parallel());
      }
    }

    return create(nrConnectionsUDP, nrConnectionsTCP);
  }

  /**
   * Create a connection creator for short-lived connections. Always call
   * {@link ChannelCreator#shutdown()} to release all resources. This needs to
   * be done in any case, whetere FutureChannelCreator return failed or
   * success!
   *
   * @param permitsUDP
   *            The number of short-lived UDP connections
   * @param permitsTCP
   *            The number of short-lived TCP connections
   * @return The future channel creator
   */
  public FutureChannelCreator create(final int permitsUDP, final int permitsTCP) {
    if (permitsUDP > maxPermitsUDP) {
      throw new IllegalArgumentException("cannot aquire more UDP connections (" + permitsUDP + ") than maximum "
              + maxPermitsUDP);
    }
    if (permitsTCP > maxPermitsTCP) {
      throw new IllegalArgumentException("cannot aquire more TCP connections (" + permitsTCP + ") than maximum "
              + maxPermitsTCP);
    }
    final FutureChannelCreator futureChannelCreator = new FutureChannelCreator();
    read.lock();
    try {
      if (shutdown) {
        return futureChannelCreator.failed("shutting down");
      }

      FutureDone<Void> futureChannelCreationDone = new FutureDone<Void>();
      futureChannelCreationDone.addListener(new BaseFutureAdapter<FutureDone<Void>>() {
        @Override
        public void operationComplete(final FutureDone<Void> future) throws Exception {
          // release the permits in all cases, otherwise we may see
          // inconsitencies
          semaphoreUPD.release(permitsUDP);
          semaphoreTCP.release(permitsTCP);
        }
      }, false); // false is important, to be always the first listener
      executor.execute(new WaitReservation(futureChannelCreator, futureChannelCreationDone, permitsUDP,
              permitsTCP));
      return futureChannelCreator;

    } finally {
      read.unlock();
    }
  }

  /**
   * Create a connection creator for permanent connections.
   *
   * @param permitsPermanentTCP
   *            The number of long-lived TCP connections
   * @return The future channel creator
   */
  public FutureChannelCreator createPermanent(final int permitsPermanentTCP) {
    if (permitsPermanentTCP > maxPermitsPermanentTCP) {
      throw new IllegalArgumentException("cannot aquire more TCP connections (" + permitsPermanentTCP
              + ") than maximum " + maxPermitsPermanentTCP);
    }
    final FutureChannelCreator futureChannelCreator = new FutureChannelCreator();
    read.lock();
    try {
      if (shutdown) {
        return futureChannelCreator.failed("shutting down");
      }
      FutureDone<Void> futureChannelCreationDone = new FutureDone<Void>();
      futureChannelCreationDone.addListener(new BaseFutureAdapter<FutureDone<Void>>() {
        @Override
        public void operationComplete(final FutureDone<Void> future) throws Exception {
          // release the permits in all cases, otherwise we may see
          // inconsitencies
          semaphorePermanentTCP.release(permitsPermanentTCP);
        }
      }, false); // false is important, to be always the first listener
      executor.execute(new WaitReservationPermanent(futureChannelCreator, futureChannelCreationDone,
              permitsPermanentTCP));
      return futureChannelCreator;
    } finally {
      read.unlock();
    }
  }

  /**
   * Shutdown all the channel creators out there.
   *
   * @return The future when the shutdown is complete
   */
  public FutureDone<Void> shutdown() {
    write.lock();
    try {
      if (shutdown) {
        shutdownFuture().failed("already shutting down");
        return shutdownFuture();
      }
      shutdown = true;
    } finally {
      write.unlock();
    }

    // fast shutdown for those that are in the queue is not required. We
    // could let the executor finish since the
    // shutdown flag is set and the future will be set as well to
    // "shutting down":

    for (Runnable r : executor.shutdownNow()) {
      if (r instanceof WaitReservation) {
        WaitReservation wr = (WaitReservation) r;
        wr.futureChannelCreator().failed("shutting down");
      } else {
        WaitReservationPermanent wr = (WaitReservationPermanent) r;
        wr.futureChannelCreator().failed("shutting down");
      }
    }

    // the channelCreator does not change anymore from here on
    final int size = channelCreators.size();
    if (size == 0) {
      futureReservationDone.done();
    } else {
      final AtomicInteger completeCounter = new AtomicInteger(0);
      for (final ChannelCreator channelCreator : channelCreators) {
        // this is very important that we set first the listener and
        // then call shutdown. Otherwise, the order of
        // the listener calls is not guaranteed and we may call this
        // listener before the semphore.release,
        // causing an exception.
        channelCreator.shutdownFuture().addListener(new BaseFutureAdapter<FutureDone<Void>>() {
          @Override
          public void operationComplete(final FutureDone<Void> future) throws Exception {
            if (completeCounter.incrementAndGet() == size) {
              // we can block here as we block in
              // GlobalEventExecutor.INSTANCE
              semaphoreUPD.acquireUninterruptibly(maxPermitsUDP);
              semaphoreTCP.acquireUninterruptibly(maxPermitsTCP);
              semaphorePermanentTCP.acquireUninterruptibly(maxPermitsPermanentTCP);
              shutdownFuture().done();
            }
          }
        });
        channelCreator.shutdown();
      }
    }
    // wait for completion
    return shutdownFuture();
  }

  /**
   * @return The shutdown future that is used when calling {@link #shutdown()}
   */
  public FutureDone<Void> shutdownFuture() {
    return futureReservationDone;
  }

  /**
   * Adds a channel creator to the set and also adds it the the
   * shutdownlistener.
   *
   * @param channelCreator
   *            The channel creator
   */
  private void addToSet(final ChannelCreator channelCreator) {
    channelCreator.shutdownFuture().addListener(new BaseFutureAdapter<FutureDone<Void>>() {
      @Override
      public void operationComplete(final FutureDone<Void> future) throws Exception {
        read.lock();
        try {
          if (shutdown) {
            return;
          }
          channelCreators.remove(channelCreator);
        } finally {
          read.unlock();
        }
      }
    });
    channelCreators.add(channelCreator);
  }

  /**
   * Tries to reserve a channel creator. If too many channel already created,
   * wait until channels are closed. This waiter is for the short-lived
   * connections.
   *
   * @author Thomas Bocek
   *
   */
  private class WaitReservation implements Runnable {
    private final FutureChannelCreator futureChannelCreator;
    private final FutureDone<Void> futureChannelCreationShutdown;
    private final int permitsUDP;
    private final int permitsTCP;

    /**
     * Creates a reservation that returns a {@link ChannelCreator} in a
     * future once we have the semaphore.
     *
     * @param futureChannelCreator
     *            The status of the creating
     * @param futureChannelCreationShutdown
     *            The {@link ChannelCreator} shutdown feature needs to be
     *            passed since we need it for {@link Reservation#shutdown()}
     *            .
     * @param permitsUDP
     *            The number of permits for UDP
     * @param permitsTCP
     *            The number of permits for TCP
     */
    public WaitReservation(final FutureChannelCreator futureChannelCreator,
            final FutureDone<Void> futureChannelCreationShutdown, final int permitsUDP, final int permitsTCP) {
      this.futureChannelCreator = futureChannelCreator;
      this.futureChannelCreationShutdown = futureChannelCreationShutdown;
      this.permitsUDP = permitsUDP;
      this.permitsTCP = permitsTCP;
    }

    @Override
    public void run() {
      final ChannelCreator channelCreator;
      read.lock();
      try {
        if (shutdown) {
          futureChannelCreator.failed("shutting down");
          return;
        }

        try {
          semaphoreUPD.acquire(permitsUDP);
        } catch (InterruptedException e) {
          futureChannelCreator.failed(e);
          return;
        }
        try {
          semaphoreTCP.acquire(permitsTCP);
        } catch (InterruptedException e) {
          semaphoreUPD.release(permitsUDP);
          futureChannelCreator.failed(e);
          return;
        }

        channelCreator = new ChannelCreator(workerGroup, futureChannelCreationShutdown, permitsUDP, permitsTCP,
                channelClientConfiguration);
        addToSet(channelCreator);
      } finally {
        read.unlock();
      }
      futureChannelCreator.reserved(channelCreator);
    }

    private FutureChannelCreator futureChannelCreator() {
      return futureChannelCreator;
    }

  }

  /**
   * Tries to reserve a channel creator. If too many channel already created,
   * wait until channels are closed. This waiter is for the long-lived
   * connections.
   *
   * @author Thomas Bocek
   *
   */
  private final class WaitReservationPermanent implements Runnable {
    private final FutureChannelCreator futureChannelCreator;
    private final FutureDone<Void> futureChannelCreationShutdown;
    private final int permitsPermanentTCP;

    /**
     * Creates a reservation that returns a {@link ChannelCreator} in a
     * future once we have the semaphore.
     *
     * @param futureChannelCreator
     *            The status of the creating
     * @param futureChannelCreationShutdown
     *            The {@link ChannelCreator} shutdown feature needs to be
     *            passed since we need it for {@link Reservation#shutdown()}
     *            .
     * @param permitsPermanentTCP
     *            The number of permits
     */
    private WaitReservationPermanent(final FutureChannelCreator futureChannelCreator,
            final FutureDone<Void> futureChannelCreationShutdown, final int permitsPermanentTCP) {
      this.futureChannelCreator = futureChannelCreator;
      this.futureChannelCreationShutdown = futureChannelCreationShutdown;
      this.permitsPermanentTCP = permitsPermanentTCP;
    }

    @Override
    public void run() {
      ChannelCreator channelCreator;
      read.lock();
      try {
        if (shutdown) {
          futureChannelCreator.failed("shutting down");
          return;
        }

        try {
          semaphorePermanentTCP.acquire(permitsPermanentTCP);
        } catch (InterruptedException e) {
          futureChannelCreator.failed(e);
          return;
        }

        channelCreator = new ChannelCreator(workerGroup, futureChannelCreationShutdown, 0, permitsPermanentTCP,
                channelClientConfiguration);
        addToSet(channelCreator);
      } finally {
        read.unlock();
      }
      futureChannelCreator.reserved(channelCreator);
    }

    private FutureChannelCreator futureChannelCreator() {
      return futureChannelCreator;
    }
  }

}
TOP

Related Classes of net.tomp2p.connection.Reservation$WaitReservationPermanent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.