Ejemplo n.º 1
0
  /**
   * Bind the current thread to this reservable lock.
   *
   * @param wholeCore if true, also reserve the whole core.
   */
  public void bind(boolean wholeCore) {
    if (bound && assignedThread != null && assignedThread.isAlive())
      throw new IllegalStateException("cpu " + cpuId + " already bound to " + assignedThread);

    if (wholeCore) {
      int core = coreForId(cpuId);
      for (AffinityLock al : CORES.get(core)) {
        if (bound && al.assignedThread != null && al.assignedThread.isAlive()) {
          LOGGER.severe("cpu " + al.cpuId + " already bound to " + al.assignedThread);
        } else {
          al.bound = true;
          al.assignedThread = Thread.currentThread();
        }
      }
      if (LOGGER.isLoggable(Level.INFO)) {
        StringBuilder sb = new StringBuilder().append("Assigning core ").append(core);
        String sep = ": cpus ";
        for (AffinityLock al : CORES.get(core)) {
          sb.append(sep).append(al.cpuId);
          sep = ", ";
        }
        sb.append(" to ").append(assignedThread);
        LOGGER.info(sb.toString());
      }
    } else if (cpuId >= 0) {
      bound = true;
      assignedThread = Thread.currentThread();
      if (LOGGER.isLoggable(Level.INFO))
        LOGGER.info("Assigning cpu " + cpuId + " to " + assignedThread);
    }
    if (cpuId >= 0) AffinitySupport.setAffinity(1L << cpuId);
  }
Ejemplo n.º 2
0
  /**
   * Bind the current thread to this reservable lock.
   *
   * @param wholeCore if true, also reserve the whole core.
   */
  public void bind(boolean wholeCore) {
    if (bound && assignedThread != null && assignedThread.isAlive())
      throw new IllegalStateException("cpu " + cpuId + " already bound to " + assignedThread);

    if (wholeCore) {
      lockInventory.bindWholeCore(cpuId);

    } else if (cpuId >= 0) {
      bound = true;
      assignedThread = Thread.currentThread();
      LOGGER.info("Assigning cpu {} to {}", cpuId, assignedThread);
    }
    if (cpuId >= 0) AffinitySupport.setAffinity(1L << cpuId);
  }
Ejemplo n.º 3
0
 /** Release the current AffinityLock which can be discarded. */
 public void release() {
   Thread t = Thread.currentThread();
   synchronized (AffinityLock.class) {
     for (AffinityLock al : LOCKS) {
       Thread at = al.assignedThread;
       if (at == t) {
         if (LOGGER.isLoggable(Level.INFO))
           LOGGER.info("Releasing cpu " + al.cpuId + " from " + t);
         al.assignedThread = null;
         al.bound = false;
       } else if (at != null && !at.isAlive()) {
         LOGGER.warning("Releasing cpu " + al.cpuId + " from " + t + " as it is not alive.");
         al.assignedThread = null;
         al.bound = false;
       }
     }
   }
   AffinitySupport.setAffinity(BASE_AFFINITY);
 }
Ejemplo n.º 4
0
/**
 * This utility class support locking a thread to a single core, or reserving a whole core for a
 * thread.
 *
 * @author peter.lawrey
 */
public class AffinityLock {
  private static final Logger LOGGER = Logger.getLogger(AffinityLock.class.getName());

  // TODO It seems like on virtualized platforms .availableProcessors() value can change at
  // TODO runtime. We should think about how to adopt to such change

  // Static fields and methods.
  public static final String AFFINITY_RESERVED = "affinity.reserved";

  public static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
  public static final long BASE_AFFINITY = AffinitySupport.getAffinity();
  public static final long RESERVED_AFFINITY = getReservedAffinity0();

  private static AffinityLock[] LOCKS;
  private static NavigableMap<Integer, AffinityLock[]> CORES; // set by cpuLayout()
  private static final AffinityLock NONE = new AffinityLock(-1, false, false);
  private static CpuLayout cpuLayout = new NoCpuLayout(PROCESSORS);

  static {
    try {
      if (new File("/proc/cpuinfo").exists()) {
        cpuLayout(VanillaCpuLayout.fromCpuInfo());
      } else {
        LOCKS = new AffinityLock[PROCESSORS];
        CORES = new TreeMap<Integer, AffinityLock[]>();
        for (int i = 0; i < PROCESSORS; i++) {
          AffinityLock al =
              LOCKS[i] =
                  new AffinityLock(
                      i, ((BASE_AFFINITY >> i) & 1) != 0, ((RESERVED_AFFINITY >> i) & 1) != 0);

          final int layoutId = al.cpuId;
          int logicalCpuId = coreForId(layoutId);
          AffinityLock[] als = CORES.get(logicalCpuId);
          if (als == null) CORES.put(logicalCpuId, als = new AffinityLock[1]);
          als[cpuLayout.threadId(layoutId)] = al;
        }
      }
    } catch (IOException e) {
      LOGGER.log(Level.WARNING, "Unable to load /proc/cpuinfo", e);
    }
  }

  /**
   * Set the CPU layout for this machine. CPUs which are not mentioned will be ignored.
   *
   * <p>Changing the layout will have no impact on thread which have already been assigned. It only
   * affects subsequent assignments.
   *
   * @param cpuLayout for this application to use for this machine.
   */
  public static void cpuLayout(CpuLayout cpuLayout) {
    synchronized (AffinityLock.class) {
      if (cpuLayout.equals(AffinityLock.cpuLayout)) return;
      AffinityLock.cpuLayout = cpuLayout;
      System.out.println("Locks= " + cpuLayout.cpus());
      LOCKS = new AffinityLock[cpuLayout.cpus()];
      int threads = cpuLayout.threadsPerCore();
      CORES = new TreeMap<Integer, AffinityLock[]>();
      for (int i = 0; i < cpuLayout.cpus(); i++) {
        boolean base1 = ((BASE_AFFINITY >> i) & 1) != 0;
        boolean reservable1 = ((RESERVED_AFFINITY >> i) & 1) != 0;
        if (LOGGER.isLoggable(Level.FINE))
          LOGGER.fine("cpu " + i + " base= " + base1 + " reservable= " + reservable1);
        AffinityLock al = LOCKS[i] = new AffinityLock(i, base1, reservable1);
        final int layoutId = al.cpuId;
        int logicalCpuId = coreForId(layoutId);
        AffinityLock[] als = CORES.get(logicalCpuId);
        if (als == null) CORES.put(logicalCpuId, als = new AffinityLock[threads]);
        als[cpuLayout.threadId(layoutId)] = al;
      }
    }
  }

  /**
   * Translate a layout id into a logical cpu id.
   *
   * <p>This translation is perform so that regardless of how
   *
   * @param id
   * @return
   */
  private static int coreForId(int id) {
    return cpuLayout.socketId(id) * cpuLayout.coresPerSocket() + cpuLayout.coreId(id);
  }

  /** @return The current CpuLayout for the application. */
  public static CpuLayout cpuLayout() {
    return cpuLayout;
  }

  private static long getReservedAffinity0() {
    String reservedAffinity = System.getProperty(AFFINITY_RESERVED);
    if (reservedAffinity == null || reservedAffinity.trim().isEmpty()) {
      long reserverable = ((1 << PROCESSORS) - 1) ^ BASE_AFFINITY;
      if (reserverable == 0 && PROCESSORS > 1) {
        LOGGER.log(
            Level.INFO,
            "No isolated CPUs found, so assuming CPUs 1 to " + (PROCESSORS - 1) + " available.");
        return ((1 << PROCESSORS) - 2);
      }
      return reserverable;
    }
    return Long.parseLong(reservedAffinity, 16);
  }

  /**
   * Assign any free cpu to this thread.
   *
   * @return A handle for the current AffinityLock.
   */
  public static AffinityLock acquireLock() {
    return acquireLock(true);
  }

  /**
   * Assign any free core to this thread.
   *
   * <p>In reality, only one cpu is assigned, the rest of the threads for that core are reservable
   * so they are not used.
   *
   * @return A handle for the current AffinityLock.
   */
  public static AffinityLock acquireCore() {
    return acquireCore(true);
  }

  /**
   * Assign a cpu which can be bound to the current thread or another thread.
   *
   * <p>This can be used for defining your thread layout centrally and passing the handle via
   * dependency injection.
   *
   * @param bind if true, bind the current thread, if false, reserve a cpu which can be bound later.
   * @return A handle for an affinity lock.
   */
  public static AffinityLock acquireLock(boolean bind) {
    return acquireLock(bind, -1, AffinityStrategies.ANY);
  }

  /**
   * Assign a core(and all its cpus) which can be bound to the current thread or another thread.
   *
   * <p>This can be used for defining your thread layout centrally and passing the handle via
   * dependency injection.
   *
   * @param bind if true, bind the current thread, if false, reserve a cpu which can be bound later.
   * @return A handle for an affinity lock.
   */
  public static AffinityLock acquireCore(boolean bind) {
    return acquireCore(bind, -1, AffinityStrategies.ANY);
  }

  private static AffinityLock acquireLock(boolean bind, int cpuId, AffinityStrategy... strategies) {
    synchronized (AffinityLock.class) {
      for (AffinityStrategy strategy : strategies) {
        // consider all processors except cpu 0 which is usually used by the OS.
        // if you have only one core, this library is not appropriate in any case.
        for (int i = LOCKS.length - 1; i > 0; i--) {
          AffinityLock al = LOCKS[i];
          if (al.canReserve() && (cpuId < 0 || strategy.matches(cpuId, al.cpuId))) {
            al.assignCurrentThread(bind, false);
            return al;
          }
        }
      }
    }
    if (LOGGER.isLoggable(Level.WARNING))
      LOGGER.warning("No reservable CPU for " + Thread.currentThread());
    return AffinityLock.NONE;
  }

  private static AffinityLock acquireCore(boolean bind, int cpuId, AffinityStrategy... strategies) {
    synchronized (AffinityLock.class) {
      for (AffinityStrategy strategy : strategies) {
        LOOP:
        for (AffinityLock[] als : CORES.descendingMap().values()) {
          for (AffinityLock al : als)
            if (!al.canReserve() || !strategy.matches(cpuId, al.cpuId)) continue LOOP;

          final AffinityLock al = als[0];
          al.assignCurrentThread(bind, true);
          return al;
        }
      }
    }
    if (LOGGER.isLoggable(Level.WARNING))
      LOGGER.warning("No reservable Core for " + Thread.currentThread());
    return acquireLock(bind, cpuId, strategies);
  }

  /** @return All the current locks as a String. */
  public static String dumpLocks() {
    return dumpLocks0(LOCKS);
  }

  static String dumpLocks0(AffinityLock[] locks) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < locks.length; i++) {
      AffinityLock al = locks[i];
      sb.append(i).append(": ");
      if (al.assignedThread != null)
        sb.append(al.assignedThread).append(" alive=").append(al.assignedThread.isAlive());
      else if (al.reservable) sb.append("Reserved for this application");
      else if (al.base) sb.append("General use CPU");
      else sb.append("CPU not available");
      sb.append('\n');
    }
    return sb.toString();
  }

  //// Non static fields and methods.
  private final int cpuId;
  private final boolean base;
  private final boolean reservable;
  boolean bound = false;
  Thread assignedThread;

  AffinityLock(int cpuId, boolean base, boolean reservable) {
    this.cpuId = cpuId;
    this.base = base;
    this.reservable = reservable;
  }

  /**
   * Assigning the current thread has a side effect of preventing the lock being used again until it
   * is released.
   *
   * @param bind whether to bind the thread as well
   * @param wholeCore whether to reserve all the thread in the same core.
   */
  private void assignCurrentThread(boolean bind, boolean wholeCore) {
    assignedThread = Thread.currentThread();
    if (bind) bind(wholeCore);
  }

  /** Bind the current thread to this reservable lock. */
  public void bind() {
    bind(false);
  }

  /**
   * Bind the current thread to this reservable lock.
   *
   * @param wholeCore if true, also reserve the whole core.
   */
  public void bind(boolean wholeCore) {
    if (bound && assignedThread != null && assignedThread.isAlive())
      throw new IllegalStateException("cpu " + cpuId + " already bound to " + assignedThread);

    if (wholeCore) {
      int core = coreForId(cpuId);
      for (AffinityLock al : CORES.get(core)) {
        if (bound && al.assignedThread != null && al.assignedThread.isAlive()) {
          LOGGER.severe("cpu " + al.cpuId + " already bound to " + al.assignedThread);
        } else {
          al.bound = true;
          al.assignedThread = Thread.currentThread();
        }
      }
      if (LOGGER.isLoggable(Level.INFO)) {
        StringBuilder sb = new StringBuilder().append("Assigning core ").append(core);
        String sep = ": cpus ";
        for (AffinityLock al : CORES.get(core)) {
          sb.append(sep).append(al.cpuId);
          sep = ", ";
        }
        sb.append(" to ").append(assignedThread);
        LOGGER.info(sb.toString());
      }
    } else if (cpuId >= 0) {
      bound = true;
      assignedThread = Thread.currentThread();
      if (LOGGER.isLoggable(Level.INFO))
        LOGGER.info("Assigning cpu " + cpuId + " to " + assignedThread);
    }
    if (cpuId >= 0) AffinitySupport.setAffinity(1L << cpuId);
  }

  private boolean canReserve() {
    if (!reservable) return false;
    if (assignedThread != null) {
      if (assignedThread.isAlive()) return false;
      LOGGER.severe("Lock assigned to " + assignedThread + " but this thread is dead.");
    }
    return true;
  }

  /**
   * Give another affinity lock relative to this one based on a list of strategies.
   *
   * <p>The strategies are evaluated in order to (like a search path) to find the next appropriate
   * thread. If ANY is not the last strategy, a warning is logged and no cpu is assigned (leaving
   * the OS to choose)
   *
   * @param strategies To determine if you want the same/different core/socket.
   * @return A matching AffinityLock.
   */
  public AffinityLock acquireLock(AffinityStrategy... strategies) {
    return acquireLock(false, cpuId, strategies);
  }

  /** Release the current AffinityLock which can be discarded. */
  public void release() {
    Thread t = Thread.currentThread();
    synchronized (AffinityLock.class) {
      for (AffinityLock al : LOCKS) {
        Thread at = al.assignedThread;
        if (at == t) {
          if (LOGGER.isLoggable(Level.INFO))
            LOGGER.info("Releasing cpu " + al.cpuId + " from " + t);
          al.assignedThread = null;
          al.bound = false;
        } else if (at != null && !at.isAlive()) {
          LOGGER.warning("Releasing cpu " + al.cpuId + " from " + t + " as it is not alive.");
          al.assignedThread = null;
          al.bound = false;
        }
      }
    }
    AffinitySupport.setAffinity(BASE_AFFINITY);
  }

  @Override
  protected void finalize() throws Throwable {
    if (reservable) {
      LOGGER.warning(
          "Affinity lock for "
              + assignedThread
              + " was discarded rather than release()d in a controlled manner.");
      release();
    }
    super.finalize();
  }

  /** @return unique id for this CPI or -1 if not allocated. */
  public int cpuId() {
    return cpuId;
  }

  /** @return Was a cpu found to bind this lock to. */
  public boolean isAllocated() {
    return cpuId >= 0;
  }

  /** @return Has this AffinityLock been bound? */
  public boolean isBound() {
    return bound;
  }
}
Ejemplo n.º 5
0
/**
 * This utility class support locking a thread to a single core, or reserving a whole core for a
 * thread.
 *
 * @author peter.lawrey
 */
public class AffinityLock implements Closeable {
  private static final Logger LOGGER = LoggerFactory.getLogger(AffinityLock.class);

  // Static fields and methods.
  public static final String AFFINITY_RESERVED = "affinity.reserved";

  // TODO It seems like on virtualized platforms .availableProcessors() value can change at
  // TODO runtime. We should think about how to adopt to such change
  public static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
  public static final long BASE_AFFINITY = AffinitySupport.getAffinity();
  public static final long RESERVED_AFFINITY = getReservedAffinity0();
  private static final LockInventory LOCK_INVENTORY =
      new LockInventory(new NoCpuLayout(PROCESSORS));

  static {
    try {
      if (new File("/proc/cpuinfo").exists()) {
        cpuLayout(VanillaCpuLayout.fromCpuInfo());
      }
    } catch (IOException e) {
      LOGGER.warn("Unable to load /proc/cpuinfo", e);
    }
  }
  /** Logical ID of the CPU to which this lock belongs to. */
  private final int cpuId;
  /** CPU to which this lock belongs to is of general use. */
  private final boolean base;
  /** CPU to which this lock belongs to is reservable. */
  private final boolean reservable;
  /**
   * An inventory build from the CPU layout which keeps track of the various locks belonging to each
   * CPU.
   */
  private final LockInventory lockInventory;

  boolean bound = false;
  @Nullable Thread assignedThread;

  AffinityLock(int cpuId, boolean base, boolean reservable, LockInventory lockInventory) {

    this.lockInventory = lockInventory;
    this.cpuId = cpuId;
    this.base = base;
    this.reservable = reservable;
  }

  /**
   * Set the CPU layout for this machine. CPUs which are not mentioned will be ignored.
   *
   * <p>Changing the layout will have no impact on thread which have already been assigned. It only
   * affects subsequent assignments.
   *
   * @param cpuLayout for this application to use for this machine.
   */
  public static void cpuLayout(@NotNull CpuLayout cpuLayout) {
    LOCK_INVENTORY.set(cpuLayout);
  }

  /** @return The current CpuLayout for the application. */
  @NotNull
  public static CpuLayout cpuLayout() {
    return LOCK_INVENTORY.getCpuLayout();
  }

  private static long getReservedAffinity0() {
    String reservedAffinity = System.getProperty(AFFINITY_RESERVED);
    if (reservedAffinity == null || reservedAffinity.trim().isEmpty()) {
      long reserverable = ((1 << PROCESSORS) - 1) ^ BASE_AFFINITY;
      if (reserverable == 0 && PROCESSORS > 1) {
        LoggerFactory.getLogger(AffinityLock.class)
            .info("No isolated CPUs found, so assuming CPUs 0 to {} available.", (PROCESSORS - 1));
        return ((1 << PROCESSORS) - 2);
      }
      return reserverable;
    }

    return Long.parseLong(reservedAffinity, 16);
  }

  /**
   * Assign any free cpu to this thread.
   *
   * @return A handle for the current AffinityLock.
   */
  public static AffinityLock acquireLock() {
    return acquireLock(true);
  }

  /**
   * Assign any free core to this thread.
   *
   * <p>In reality, only one cpu is assigned, the rest of the threads for that core are reservable
   * so they are not used.
   *
   * @return A handle for the current AffinityLock.
   */
  public static AffinityLock acquireCore() {
    return acquireCore(true);
  }

  /**
   * Assign a cpu which can be bound to the current thread or another thread.
   *
   * <p>This can be used for defining your thread layout centrally and passing the handle via
   * dependency injection.
   *
   * @param bind if true, bind the current thread, if false, reserve a cpu which can be bound later.
   * @return A handle for an affinity lock.
   */
  public static AffinityLock acquireLock(boolean bind) {
    return acquireLock(bind, -1, AffinityStrategies.ANY);
  }

  /**
   * Assign a core(and all its cpus) which can be bound to the current thread or another thread.
   *
   * <p>This can be used for defining your thread layout centrally and passing the handle via
   * dependency injection.
   *
   * @param bind if true, bind the current thread, if false, reserve a cpu which can be bound later.
   * @return A handle for an affinity lock.
   */
  public static AffinityLock acquireCore(boolean bind) {
    return acquireCore(bind, -1, AffinityStrategies.ANY);
  }

  public static AffinityLock acquireCore(boolean bind, int cpuId) {
    return acquireCore(
        bind,
        cpuId,
        AffinityStrategies.SAME_CPU_ID,
        AffinityStrategies.DIFFERENT_CORE,
        AffinityStrategies.ANY);
  }

  private static AffinityLock acquireLock(
      boolean bind, int cpuId, @NotNull AffinityStrategy... strategies) {
    return LOCK_INVENTORY.acquireLock(bind, cpuId, strategies);
  }

  private static AffinityLock acquireCore(
      boolean bind, int cpuId, @NotNull AffinityStrategy... strategies) {
    return LOCK_INVENTORY.acquireCore(bind, cpuId, strategies);
  }

  /** @return All the current locks as a String. */
  @NotNull
  public static String dumpLocks() {
    return LOCK_INVENTORY.dumpLocks();
  }

  /**
   * Assigning the current thread has a side effect of preventing the lock being used again until it
   * is released.
   *
   * @param bind whether to bind the thread as well
   * @param wholeCore whether to reserve all the thread in the same core.
   */
  final void assignCurrentThread(boolean bind, boolean wholeCore) {
    assignedThread = Thread.currentThread();
    if (bind) bind(wholeCore);
  }

  /** Bind the current thread to this reservable lock. */
  public void bind() {
    bind(false);
  }

  /**
   * Bind the current thread to this reservable lock.
   *
   * @param wholeCore if true, also reserve the whole core.
   */
  public void bind(boolean wholeCore) {
    if (bound && assignedThread != null && assignedThread.isAlive())
      throw new IllegalStateException("cpu " + cpuId + " already bound to " + assignedThread);

    if (wholeCore) {
      lockInventory.bindWholeCore(cpuId);

    } else if (cpuId >= 0) {
      bound = true;
      assignedThread = Thread.currentThread();
      LOGGER.info("Assigning cpu {} to {}", cpuId, assignedThread);
    }
    if (cpuId >= 0) AffinitySupport.setAffinity(1L << cpuId);
  }

  final boolean canReserve() {
    // if (!reservable) return false;
    if (assignedThread != null) {
      if (assignedThread.isAlive()) {
        return false;
      }

      LOGGER.warn("Lock assigned to {} but this thread is dead.", assignedThread);
    }
    return true;
  }

  /**
   * Give another affinity lock relative to this one based on a list of strategies.
   *
   * <p>The strategies are evaluated in order to (like a search path) to find the next appropriate
   * thread. If ANY is not the last strategy, a warning is logged and no cpu is assigned (leaving
   * the OS to choose)
   *
   * @param strategies To determine if you want the same/different core/socket.
   * @return A matching AffinityLock.
   */
  public AffinityLock acquireLock(AffinityStrategy... strategies) {
    return acquireLock(false, cpuId, strategies);
  }

  /** Release the current AffinityLock which can be discarded. */
  public void release() {
    lockInventory.release();
  }

  @Override
  public void close() {
    release();
  }

  @Override
  protected void finalize() throws Throwable {
    if (reservable) {
      LOGGER.warn(
          "Affinity lock for {} was discarded rather than release()d in a controlled manner.",
          assignedThread);
      release();
    }
    super.finalize();
  }

  /** @return unique id for this CPI or -1 if not allocated. */
  public int cpuId() {
    return cpuId;
  }

  /** @return Was a cpu found to bind this lock to. */
  public boolean isAllocated() {
    return cpuId >= 0;
  }

  /** @return Has this AffinityLock been bound? */
  public boolean isBound() {
    return bound;
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    if (assignedThread != null)
      sb.append(assignedThread).append(" alive=").append(assignedThread.isAlive());
    else if (reservable) sb.append("Reserved for this application");
    else if (base) sb.append("General use CPU");
    else sb.append("CPU not available");
    return sb.toString();
  }

  public static void main(String[] args) {
    System.out.println("Test");
  }
}