private RemoveRandomReturn removeRandomExhaustive(
      RandomGrabArrayItemExclusionList excluding, ClientContext context, long now) {
    synchronized (root) {
      long wakeupTime = Long.MAX_VALUE;
      if (grabArrays.length == 0) return null;
      int x = context.fastWeakRandom.nextInt(grabArrays.length);
      for (int i = 0; i < grabArrays.length; i++) {
        x++;
        if (x >= grabArrays.length) x = 0;
        RemoveRandomWithObject<T> rga = grabArrays[x];
        long excludeTime = rga.getWakeupTime(context, now);
        if (excludeTime > 0) {
          if (wakeupTime > excludeTime) wakeupTime = excludeTime;
          continue;
        }
        if (logMINOR)
          Logger.minor(
              this, "Picked " + x + " of " + grabArrays.length + " : " + rga + " on " + this);

        RandomGrabArrayItem item = null;
        RemoveRandomReturn val = rga.removeRandom(excluding, context, now);
        if (val != null) {
          if (val.item != null) item = val.item;
          else {
            if (wakeupTime > val.wakeupTime) wakeupTime = val.wakeupTime;
          }
        }
        if (logMINOR)
          Logger.minor(
              this,
              "RGA has picked "
                  + x
                  + "/"
                  + grabArrays.length
                  + ": "
                  + item
                  + " rga.isEmpty="
                  + rga.isEmpty());
        if (item != null) {
          return new RemoveRandomReturn(item);
        } else if (rga.isEmpty()) {
          if (logMINOR)
            Logger.minor(this, "Removing grab array " + x + " : " + rga + " (is empty)");
          removeElement(x);
        }
      }
      reduceWakeupTime(wakeupTime, context);
      return new RemoveRandomReturn(wakeupTime);
    }
  }
 private RemoveRandomReturn removeRandomOneOnly(
     RandomGrabArrayItemExclusionList excluding, ClientContext context, long now) {
   synchronized (root) {
     long wakeupTime = Long.MAX_VALUE;
     // Optimise the common case
     RemoveRandomWithObject<T> rga = grabArrays[0];
     if (logMINOR) Logger.minor(this, "Only one RGA: " + rga);
     long excludeTime = rga.getWakeupTime(context, now);
     if (excludeTime > 0) return new RemoveRandomReturn(excludeTime);
     if (rga == null) {
       Logger.error(this, "Only one entry and that is null");
       // We are sure
       grabArrays = (C[]) new RemoveRandomWithObject[0];
       grabClients = (T[]) new Object[0];
       return null;
     }
     RemoveRandomReturn val = rga.removeRandom(excluding, context, now);
     RandomGrabArrayItem item = null;
     if (val != null) { // val == null => remove it
       if (val.item != null) item = val.item;
       else {
         wakeupTime = val.wakeupTime;
       }
     }
     if (rga.isEmpty()) {
       if (logMINOR) Logger.minor(this, "Removing only grab array (0) : " + rga);
       grabArrays = (C[]) new RemoveRandomWithObject[0];
       grabClients = (T[]) new Object[0];
     }
     if (logMINOR) Logger.minor(this, "Returning (one item only) " + item + " for " + rga);
     if (item == null) {
       if (grabArrays.length == 0) {
         if (logMINOR) Logger.minor(this, "Arrays are empty on " + this);
         return null; // Remove this as well
       }
       reduceWakeupTime(wakeupTime, context);
       return new RemoveRandomReturn(wakeupTime);
     } else return new RemoveRandomReturn(item);
   }
 }
 private RemoveRandomReturn removeRandomTwoOnly(
     RandomGrabArrayItemExclusionList excluding, ClientContext context, long now) {
   synchronized (root) {
     long wakeupTime = Long.MAX_VALUE;
     // Another simple common case
     int x = context.fastWeakRandom.nextBoolean() ? 1 : 0;
     RemoveRandomWithObject<T> rga = grabArrays[x];
     RemoveRandomWithObject<T> firstRGA = rga;
     if (rga == null) {
       Logger.error(this, "rga = null on " + this);
       if (grabArrays[1 - x] == null) {
         Logger.error(this, "other rga is also null on " + this);
         grabArrays = (C[]) new RemoveRandomWithObject[0];
         grabClients = (T[]) new Object[0];
         return null;
       } else {
         Logger.error(
             this, "grabArrays[" + (1 - x) + "] is valid but [" + x + "] is null, correcting...");
         grabArrays = (C[]) new RemoveRandomWithObject[] {grabArrays[1 - x]};
         grabClients = (T[]) new Object[] {grabClients[1 - x]};
         return null;
       }
     }
     RandomGrabArrayItem item = null;
     RemoveRandomReturn val = null;
     if (logMINOR) Logger.minor(this, "Only 2, trying " + rga);
     long excludeTime = rga.getWakeupTime(context, now);
     if (excludeTime > 0) {
       wakeupTime = excludeTime;
       rga = null;
       firstRGA = null;
     } else {
       val = rga.removeRandom(excluding, context, now);
       if (val != null) {
         if (val.item != null) item = val.item;
         else {
           if (wakeupTime > val.wakeupTime) wakeupTime = val.wakeupTime;
         }
       }
     }
     if (item != null) {
       if (logMINOR) Logger.minor(this, "Returning (two items only) " + item + " for " + rga);
       return new RemoveRandomReturn(item);
     } else {
       x = 1 - x;
       rga = grabArrays[x];
       if (rga == null) {
         Logger.error(this, "Other RGA is null later on on " + this);
         grabArrays = (C[]) new RemoveRandomWithObject[] {grabArrays[1 - x]};
         grabClients = (T[]) new Object[] {grabClients[1 - x]};
         reduceWakeupTime(wakeupTime, context);
         return new RemoveRandomReturn(wakeupTime);
       }
       excludeTime = rga.getWakeupTime(context, now);
       if (excludeTime > 0) {
         if (wakeupTime > excludeTime) wakeupTime = excludeTime;
         rga = null;
       } else {
         val = rga.removeRandom(excluding, context, now);
         if (val != null) {
           if (val.item != null) item = val.item;
           else {
             if (wakeupTime > val.wakeupTime) wakeupTime = val.wakeupTime;
           }
         }
       }
       if (firstRGA != null && firstRGA.isEmpty() && rga != null && rga.isEmpty()) {
         if (logMINOR)
           Logger.minor(
               this, "Removing both on " + this + " : " + firstRGA + " and " + rga + " are empty");
         grabArrays = (C[]) new RemoveRandomWithObject[0];
         grabClients = (T[]) new Object[0];
       } else if (firstRGA != null && firstRGA.isEmpty()) {
         if (logMINOR) Logger.minor(this, "Removing first: " + firstRGA + " is empty on " + this);
         grabArrays =
             (C[])
                 new RemoveRandomWithObject[] {
                   grabArrays[x]
                 }; // don't use RGA, it may be nulled out
         grabClients = (T[]) new Object[] {grabClients[x]};
       }
       if (logMINOR) Logger.minor(this, "Returning (two items only) " + item + " for " + rga);
       if (item == null) {
         if (grabArrays.length == 0) return null; // Remove this as well
         reduceWakeupTime(wakeupTime, context);
         return new RemoveRandomReturn(wakeupTime);
       } else return new RemoveRandomReturn(item);
     }
   }
 }
  private RandomGrabArrayItem removeRandomLimited(
      RandomGrabArrayItemExclusionList excluding, ClientContext context, long now) {
    synchronized (root) {
      /** Count of arrays that have items but didn't return anything because of exclusions */
      final int MAX_EXCLUDED = 10;
      int excluded = 0;
      while (true) {
        if (grabArrays.length == 0) return null;
        int x = context.fastWeakRandom.nextInt(grabArrays.length);
        RemoveRandomWithObject<T> rga = grabArrays[x];
        if (rga == null) {
          // We handle this in the other cases so we should handle it here.
          Logger.error(this, "Slot " + x + " is null for client " + grabClients[x]);
          excluded++;
          if (excluded > MAX_EXCLUDED) {
            Logger.normal(
                this,
                "Too many sub-arrays are entirely excluded on "
                    + this
                    + " length = "
                    + grabArrays.length,
                new Exception("error"));
            return null;
          }
          continue;
        }
        long excludeTime = rga.getWakeupTime(context, now);
        if (excludeTime > 0) {
          excluded++;
          if (excluded > MAX_EXCLUDED) {
            Logger.normal(
                this,
                "Too many sub-arrays are entirely excluded on "
                    + this
                    + " length = "
                    + grabArrays.length,
                new Exception("error"));
            return null;
          }
          continue;
        }
        if (logMINOR)
          Logger.minor(
              this, "Picked " + x + " of " + grabArrays.length + " : " + rga + " on " + this);

        RandomGrabArrayItem item = null;
        RemoveRandomReturn val = rga.removeRandom(excluding, context, now);
        if (val != null && val.item != null) item = val.item;
        if (logMINOR)
          Logger.minor(
              this,
              "RGA has picked "
                  + x
                  + "/"
                  + grabArrays.length
                  + ": "
                  + item
                  + " rga.isEmpty="
                  + rga.isEmpty());
        // If it is not empty but returns null we exclude it, and count the exclusion.
        // If it is empty we remove it, and don't count the exclusion.
        if (item != null) {
          return item;
        } else {
          if (rga.isEmpty()) {
            if (logMINOR)
              Logger.minor(this, "Removing grab array " + x + " : " + rga + " (is empty)");
            removeElement(x);
          } else {
            excluded++;
            if (excluded > MAX_EXCLUDED) {
              Logger.normal(
                  this,
                  "Too many sub-arrays are entirely excluded on "
                      + this
                      + " length = "
                      + grabArrays.length,
                  new Exception("error"));
              return null;
            }
          }
          continue;
        }
      }
    }
  }