class_docstr: Allocator style helper class to assign individual tasks to a core range.
  
  Pinning tasks to separate cores (or core ranges if `num_threads` > 1)
  serves two purposes. First, it prevents the machine from being overloaded,
  which can result in OOMs or Callgrind crashes. Second, it helps reduce
  noise in the wall times, which are collected as a secondary metric. For
  multi-threaded workloads, adjacency is important. Often pairs of cores
  share silicon (e.g. cache), while far away cores may lie on separate NUMA
  nodes. For this reason, CorePool will only allocate contiguous core ranges.
  This falls short of full architecture awareness, and instead tries to find
  a balance between rigor and engineering complexity.
docstr: Simple first-fit policy.
  
  If successful, return a string for `taskset`. Otherwise, return None.
docstr_tok: ["Simple","first-fit","policy",".","If","successful",",","return","a","string","for","`","taskset","`",".","Otherwise",",","return","None","."]
code: def reserve(self, n: int) -> Optional[str]:
      """Simple first-fit policy.
  
      If successful, return a string for `taskset`. Otherwise, return None.
      """
      with self._lock:
          for lower_index in range(self._num_cores - n + 1):
              indices = tuple(range(lower_index, lower_index + n))
              if all(self._available[i] for i in indices):
                  for i in indices:
                      self._available[i] = False
  
                  lower_core = indices[0] + self._min_core_id
                  upper_core = indices[-1] + self._min_core_id
                  key = (
                      f"{lower_core}-{upper_core}"
                      if n > 1
                      else f"{lower_core}"
                  )
                  self._reservations[key] = indices
                  return key
      return None
  
imports: import math
  import multiprocessing
  import subprocess
  import textwrap
  import threading
  import time
  from typing import Dict, List, Optional, Set, Tuple, Union
  from worker.main import WorkerFailure, WorkerOutput
  from execution.work import InProgress, PYTHON_CMD, SHELL, WorkOrder
label_desc: Use image node_id 2 for calling the CorePool obj's underlying member method code with example usage: obj.reserve(n) and returns: None, key