/** Release any free memory back to the system,
 * but only if you are above a certain fraction of use of the
 * total available PHYSICAL memory.
 * Calling this could help the system avoid going into swap.
 *
 * NOTE: This only works if you linked against tcmalloc.
 * NOTE 2: This takes at least 0.1 ms on a Ubuntu 10.10 system, because of
 *      the call to MemoryStats.update().
 *
 * @param threshold :: multiplier (0-1.0) of the amount of physical
 *        memory used that has to be in use before the call to
 *        release memory is actually called.
 */
void MemoryManagerImpl::releaseFreeMemoryIfAbove(double threshold)
{
    UNUSED_ARG(threshold);
#ifdef USE_TCMALLOC
    Kernel::MemoryStats mem;
    mem.update();
    double fraction_available = static_cast<double>(mem.availMem())
                                / static_cast<double>(mem.totalMem());
    if (fraction_available < (1.0 - threshold))
    {
      // Make TCMALLOC release memory to the system
      MallocExtension::instance()->ReleaseFreeMemory();
    }
#endif
}
/** Write the group labeled "data"
 *
 * @param bank :: name of the bank
 * @param is_definition
 * @return error code
 */
int SaveToSNSHistogramNexus::WriteDataGroup(std::string bank,
                                            int is_definition) {
  int dataType, dataRank, dataDimensions[NX_MAXRANK];
  NXname name;
  void *dataBuffer;

  if (NXgetinfo(inId, &dataRank, dataDimensions, &dataType) != NX_OK)
    return NX_ERROR;

  // Get the rectangular detector
  IComponent_const_sptr det_comp =
      inputWorkspace->getInstrument()->getComponentByName(std::string(bank));
  RectangularDetector_const_sptr det =
      boost::dynamic_pointer_cast<const RectangularDetector>(det_comp);
  if (!det) {
    g_log.information()
        << "Detector '" + bank +
               "' not found, or it is not a rectangular detector!\n";
    // Just copy that then.
    if (NXmalloc(&dataBuffer, dataRank, dataDimensions, dataType) != NX_OK)
      return NX_ERROR;
    if (NXgetdata(inId, dataBuffer) != NX_OK)
      return NX_ERROR;
    if (NXcompmakedata(outId, name, dataType, dataRank, dataDimensions,
                       NX_COMP_LZW, dataDimensions) != NX_OK)
      return NX_ERROR;
    if (NXopendata(outId, name) != NX_OK)
      return NX_ERROR;
    if (WriteAttributes(is_definition) != NX_OK)
      return NX_ERROR;
    if (NXputdata(outId, dataBuffer) != NX_OK)
      return NX_ERROR;
    if (NXfree(&dataBuffer) != NX_OK)
      return NX_ERROR;
    if (NXclosedata(outId) != NX_OK)
      return NX_ERROR;
  } else {
    // YES it is a rectangular detector.

    // --- Memory requirements ----
    size_t memory_required = size_t(det->xpixels() * det->ypixels()) *
                             size_t(inputWorkspace->blocksize()) * 2 *
                             sizeof(float);
    Kernel::MemoryStats mem;
    mem.update();
    size_t memory_available = mem.availMem() * 1024;

    std::cout << "Memory available: " << memory_available / 1024 << " kb. ";
    std::cout << "Memory required: " << memory_required / 1024 << " kb. ";

    // Give a 50% margin of error in allocating the memory
    memory_available = memory_available / 2;
    if (memory_available > static_cast<size_t>(5e9))
      memory_available = static_cast<size_t>(5e9);

    if (memory_available < memory_required) {
      // Compute how large of a slab you can still use.
      int x_slab;
      x_slab = static_cast<int>(
          memory_available /
          (det->ypixels() * inputWorkspace->blocksize() * 2 * sizeof(float)));
      if (x_slab <= 0)
        x_slab = 1;
      // Look for a slab size that evenly divides the # of pixels.
      while (x_slab > 1) {
        if ((det->xpixels() % x_slab) == 0)
          break;
        x_slab--;
      }

      std::cout << "Saving in slabs of " << x_slab << " X pixels.\n";
      if (this->WriteOutDataOrErrors(det, x_slab, "data", "data_errors", false,
                                     false, is_definition, bank) != NX_OK)
        return NX_ERROR;
      if (this->WriteOutDataOrErrors(det, x_slab, "errors", "", true, false,
                                     is_definition, bank) != NX_OK)
        return NX_ERROR;
    } else {
      std::cout << "Saving in one block.\n";
      if (this->WriteOutDataOrErrors(det, det->xpixels(), "data", "data_errors",
                                     false, true, is_definition, bank) != NX_OK)
        return NX_ERROR;
    }
  }

  return NX_OK;
}