///-*-C++-*-//////////////////////////////////////////////////////////////////
//
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
//        for Shared-Memory Multiprocessors
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
//
// Copyright (c) 1998-2000, The University of Texas at Austin.
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as
// published by the Free Software Foundation, http://www.fsf.org.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef _THREADHEAP_H_
#define _THREADHEAP_H_

#include "config.h"

#include <string.h>

#include "heap.h"

class processHeap; // forward declaration

//
// We use one threadHeap for each thread (processor).
//

class threadHeap : public hoardHeap {

public:

  threadHeap (void);

  // Memory allocation routines.
  void * malloc (const size_t sz);
  inline void * memalign (size_t alignment, size_t sz);

  // Find out how large an allocated object is.
  inline static size_t objectSize (void * ptr);

  // Set our process heap.
  inline void setpHeap (processHeap * p);

private:

  // Prevent copying and assignment.
  threadHeap (const threadHeap&);
  const threadHeap& operator= (const threadHeap&);

  // Our process heap.
  processHeap *	_pHeap;

  // We insert a cache pad here to avoid false sharing (the
  // processHeap holds an array of threadHeaps, and we don't want
  // these to share any cache lines).
  double _pad[CACHE_LINE / sizeof(double)];
};


void * threadHeap::memalign (size_t alignment,
			     size_t size)
{
  // Calculate the amount of space we need
  // to satisfy the alignment requirements.

  size_t newSize;

  // If the alignment is less than the required alignment,
  // just call malloc.
  if (alignment <= ALIGNMENT) {
    return this->malloc (size);
  }

  if (alignment < sizeof(block)) {
    alignment = sizeof(block);
  }

  // Alignment must be a power of two!
  assert ((alignment & (alignment - 1)) == 0);

  // Leave enough room to align the block within the malloced space.
  newSize = size + sizeof(block) + alignment;

  // Now malloc the space up with a little extra (we'll put the block
  // pointer in right behind the allocated space).

  void * ptr = this->malloc (newSize);
  if ((((unsigned long) ptr) & -((long) alignment)) == 0) {
    // ptr is already aligned, so return it.
    assert (((unsigned long) ptr % alignment) == 0);
    return ptr;

  } else {

    // Align ptr.
    char * newptr = (char *)
      (((unsigned long) ptr + alignment - 1) & -((long) alignment));

    // If there's not enough room for the block header, skip to the
    // next aligned space within the block..
    if ((unsigned long) newptr - (unsigned long) ptr < sizeof(block)) {
      newptr += alignment;
    }
    assert (((unsigned long) newptr % alignment) == 0);

    // Copy the block from the start of the allocated memory.
    block * b = ((block *) ptr - 1);

    assert (b->isValid());
    assert (b->getSuperblock()->isValid());

    // Make sure there's enough room for the block header.
    assert (((unsigned long) newptr - (unsigned long) ptr) >= sizeof(block));

    block * p = ((block *) newptr - 1);

    // Make sure there's enough room allocated for size bytes.
    assert (((unsigned long) p - sizeof(block)) >= (unsigned long) b);

    if (p != b) {
      assert ((unsigned long) newptr > (unsigned long) ptr);
      // Copy the block header.
      *p = *b;
      assert (p->isValid());
      assert (p->getSuperblock()->isValid());

      // Set the next pointer to point to b with the 1 bit set.
      // When this block is freed, it will be treated specially.
      p->setNext ((block *) ((unsigned long) b | 1));

    } else {
      assert (ptr != newptr);
    }

    assert (((unsigned long) ptr + newSize) >= ((unsigned long) newptr + size));
    return newptr;
  }
}


size_t threadHeap::objectSize (void * ptr)
{
  // Find the superblock pointer.

  block * b = ((block *) ptr - 1);
  assert (b->isValid());
  superblock * sb = b->getSuperblock ();
  assert (sb);

  // Return the size.
  return sizeFromClass (sb->getBlockSizeClass());
}


void threadHeap::setpHeap (processHeap * p)
{
  _pHeap = p;
}

#endif // _THREADHEAP_H_

