Package freenet.client.async

Source Code of freenet.client.async.OfferedKeysList$MySendableRequestItem

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.client.async;

import java.util.ArrayList;
import java.util.HashSet;

import freenet.crypt.RandomSource;
import freenet.keys.Key;
import freenet.node.BaseSendableGet;
import freenet.node.KeysFetchingLocally;
import freenet.node.LowLevelGetException;
import freenet.node.NodeClientCore;
import freenet.node.RequestClient;
import freenet.node.RequestCompletionListener;
import freenet.node.RequestScheduler;
import freenet.node.SendableRequestItem;
import freenet.node.SendableRequestItemKey;
import freenet.node.SendableRequestSender;
import freenet.support.ListUtils;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;

/**
* All the keys at a given priority which we have received key offers from other nodes for.
*
* This list needs to be kept up to date when:
* - A request is removed.
* - A request's priority changes.
* - A key is found.
* - A node disconnects or restarts (through the BlockOffer objects on the FailureTable).
*
* And of course, when an offer is received, we need to add an element.
*
* @author toad
*
*/
@SuppressWarnings("serial") // We don't serialize this.
public class OfferedKeysList extends BaseSendableGet implements RequestClient {

  private final HashSet<Key> keys;
  private final ArrayList<Key> keysList; // O(1) remove random element the way we use it, see chooseKey().
  private static volatile boolean logMINOR;
  private static volatile boolean logDEBUG;

  static {
    Logger.registerLogThresholdCallback(new LogThresholdCallback(){
      @Override
      public void shouldUpdate(){
        logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
        logDEBUG = Logger.shouldLog(LogLevel.DEBUG, this);
      }
    });
  }
  private final RandomSource random;
  private final short priorityClass;
  private final boolean isSSK;
 
  OfferedKeysList(NodeClientCore core, RandomSource random, short priorityClass, boolean isSSK, boolean realTimeFlag) {
    super(false, realTimeFlag);
    this.keys = new HashSet<Key>();
    this.keysList = new ArrayList<Key>();
    this.random = random;
    this.priorityClass = priorityClass;
    this.isSSK = isSSK;
  }
 
  /** Called when a key is found, when it no longer belongs to this list etc. */
  public synchronized void remove(Key key) {
    assert(keysList.size() == keys.size());
    if(keys.remove(key)) {
      ListUtils.removeBySwapLast(keysList, key);
      if(logMINOR) Logger.minor(this, "Found "+key+" , removing it "+" for "+this+" size now "+keysList.size());
    }
    assert(keysList.size() == keys.size());
  }
 
  public synchronized boolean isEmpty() {
    return keys.isEmpty();
  }

  @Override
  public long countAllKeys(ClientContext context) {
    // Not supported.
    throw new UnsupportedOperationException();
  }

  @Override
  public long countSendableKeys(ClientContext context) {
    // Not supported.
    throw new UnsupportedOperationException();
  }

  private static class MySendableRequestItem implements SendableRequestItem, SendableRequestItemKey {
    final Key key;
    MySendableRequestItem(Key key) {
      this.key = key;
    }
    @Override
    public void dump() {
      // Ignore, we will be GC'ed
    }
    @Override
    public SendableRequestItemKey getKey() {
      return this;
    }
  }
 
  @Override
  public synchronized SendableRequestItem chooseKey(KeysFetchingLocally fetching, ClientContext context) {
    assert(keysList.size() == keys.size());
    if(keys.size() == 1) {
      // Shortcut the common case
      Key k = keysList.get(0);
      if(fetching.hasKey(k, null)) return null;
      // Ignore RecentlyFailed because an offered key overrides it.
      keys.remove(k);
      keysList.remove(0);
      keysList.trimToSize();
      return new MySendableRequestItem(k);
    }
    for(int i=0;i<10;i++) {
      // Pick a random key
      if(keysList.isEmpty()) return null;
      int ptr = random.nextInt(keysList.size());
      // Avoid shuffling penalty by swapping the chosen element with the end.
      Key k = keysList.get(ptr);
      if(fetching.hasKey(k, null)) continue;
      // Ignore RecentlyFailed because an offered key overrides it.
      ListUtils.removeBySwapLast(keysList, ptr);
      keys.remove(k);
      assert(keysList.size() == keys.size());
      return new MySendableRequestItem(k);
    }
    return null;
  }

  @Override
  public RequestClient getClient() {
    return this;
  }

  @Override
  public ClientRequester getClientRequest() {
    // FIXME is this safe?
    return null;
  }

  @Override
  public short getPriorityClass() {
    return priorityClass;
  }

  @Override
  public void internalError(Throwable t, RequestScheduler sched, ClientContext context, boolean persistent) {
    Logger.error(this, "Internal error: "+t, t);
  }
 
  @Override
  public SendableRequestSender getSender(ClientContext context) {
    return new SendableRequestSender() {

      @Override
      public boolean send(NodeClientCore core, final RequestScheduler sched, ClientContext context, ChosenBlock req) {
        final Key key = ((MySendableRequestItem) req.token).key;
        // Have to cache it in order to propagate it; FIXME
        // Don't let a node force us to start a real request for a specific key.
        // We check the datastore, take up offers if any (on a short timeout), and then quit if we still haven't fetched the data.
        // Obviously this may have a marginal impact on load but it should only be marginal.
        core.asyncGet(key, true, new RequestCompletionListener() {

          @Override
          public void onSucceeded() {
                        // We don't use ChosenBlockImpl so have to remove the keys from the fetching set ourselves.
            sched.removeFetchingKey(key);
            sched.wakeStarter();
          }

          @Override
          public void onFailed(LowLevelGetException e) {
              // We don't use ChosenBlockImpl so have to remove the keys from the fetching set ourselves.
            sched.removeFetchingKey(key);
            // Something might be waiting for a request to complete (e.g. if we have two requests for the same key),
            // so wake the starter thread.
            sched.wakeStarter();
          }

        }, true, false, realTimeFlag, false, false);
        // FIXME reconsider canWriteClientCache=false parameter.
        return true;
      }

      @Override
      public boolean sendIsBlocking() {
        return false;
      }
     
    };
  }

  @Override
  public boolean isCancelled() {
    return false;
  }

  public synchronized void queueKey(Key key) {
    assert(keysList.size() == keys.size());
    if(keys.add(key)) {
      keysList.add(key);
      if(logMINOR) Logger.minor(this, "Queued key "+key+" on "+this);
    }
    assert(keysList.size() == keys.size());
  }

  @Override
  public Key getNodeKey(SendableRequestItem token) {
    return ((MySendableRequestItem) token).key;
  }

  @Override
  public boolean isSSK() {
    return isSSK;
  }

  @Override
  public boolean isInsert() {
    return false;
  }

  @Override
  public ClientRequestScheduler getScheduler(ClientContext context) {
    if(isSSK)
      return context.getSskFetchScheduler(realTimeFlag);
    else
      return context.getChkFetchScheduler(realTimeFlag);
  }

  @Override
  public boolean preRegister(ClientContext context, boolean toNetwork) {
    // Ignore
    return false;
  }

  @Override
  public long getWakeupTime(ClientContext context, long now) {
    if(isEmpty()) {
        return Long.MAX_VALUE;
    }
    return 0;
  }

}
TOP

Related Classes of freenet.client.async.OfferedKeysList$MySendableRequestItem

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.