www.pudn.com > allocator.rar > shared_memory.h


// file: shared_memory.h
// author: Marc Bumble
// May 22, 2000
// Page memory source for shared memory 
// Copyright (C) 2000 by Marc D. Bumble

//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.

//  This program 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 General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#ifndef SHARED_MEMORY_H
#define SHARED_MEMORY_H

extern "C" {
#include               // for shm_open()
#include            // for shm_open()
#include 		// for shared memory shmget(),shmat(),ftok(),semop
#include 		// for fork(),getpid(),getppid(), shm_open()
#include             // for semop
}

#include 
#include 
#include               // for std::ofstream to()
#include 		// string stream for stringstream
#include 
#include 

// #include 
#include 

#include 
#include 

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including  */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
  int val;                    /* value for SETVAL */
  struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
  unsigned short int *array;  /* array for GETALL, SETALL */
  struct seminfo *__buf;      /* buffer for IPC_INFO */
};
#endif


namespace mem_space {

//   enum allocator_t {sharedpooled, persistentpooled};
  enum {name_length=512};

  // key string for ftok(), the filename
  typedef const char* allocator_key_t;
  // address process uses to attach to shared memory
  typedef const char* allocator_addr_t;
  //  typedef const char* object_key_t;

  ////////////////////////////////////////////////////////////////////
  //////                             Memory Locks
  ////////////////////////////////////////////////////////////////////
  //////   
  //////    Locks are  mutual exclusion locks used  to protect process
  //////    from  writing  simultaneously   to  shared  memory.   Each
  //////    allocator will  get one semaphore set, and  then with that
  //////    semaphore  set, each chunk  will individual  elements from
  //////    that semaphore  set.  So each  chunk is has access  to the
  //////    semid and  the semnum.  A  semid is common for  all chunks
  //////    within a given allocator, and then each of the allocator's
  //////    chunks will have its own individual semnum which indicates
  //////    which semaphore in the semid array.
  //////    
  ////////////////////////////////////////////////////////////////////

  class locks {
    enum {
      num_of_arrays=128,
      num_in_array=250
    };
    int semid;
  public:
    // constructor
    locks(const mem_space::allocator_key_t& alloc_key,
	  const int& proj_id);
    // destructor
    virtual ~locks();
    // copy constructor
    locks(const locks& t);
    // assignment operator
    locks& operator=(const locks& t);
    // equality operator
    bool operator==(const locks& t);
    int get_num_of_arrays() {return num_of_arrays;}
    int get_num_in_array() {return num_in_array;}
    void lock(int semnum);
    void unlock(int semnum);
    void print() const;
  }; // class locks

  ////////////////////////////////////////////////////////////////////////////////
  //////                          Class Memory_Index
  ////////////////////////////////////////////////////////////////////////////////
  //////   
  //////    Class Memory_Index  is just an  aggregation of information
  //////    about shared  memory segments.  This class  is designed to
  //////    be  passed  back  and   forth  as  a  parameter  to  carry
  //////    information.
  //////    
  //////    memory_ptr  - a  pointer to  the beginning  of  the shared
  //////                  memory segment
  //////    segment_num - the segment number
  //////    initialized - whether or not the segment is already in use
  //////                  and initialized.
  //////    
  ////////////////////////////////////////////////////////////////////////////////
  
  class memory_index_t {
    void* memory_ptr;		// pointer    to    returned   memory,
				// address.
//     void* chunklist_ptr;	// pointer to chunklist for an object.
// 				// Needed  by   an  attaching  process
// 				// which  must   make  sure  that  its
// 				// allocators  point  to  the  correct
// 				// chunklist.
    char key[name_length];	// alloc_key  used to  retrieve shared
				// memory segment
    int proj_id;		// shared memory number for this alloc
 				// key. proj_id == segment_num
    bool initialized;		// false  if   only  one  attached  to
                                // memory   true  if  more   than  one
                                // process attached
    int segment_page_num;	// start page within shared memory
				// segment where chunk is located
    int element_offset;		// offset  from   start  of  chunk  to
				// element number
    int global_starting_element_num;	// memory starts at element number.

  public:
    // constructor
    //     memory_index_t(void* mem_ptr, int buff_num, bool init) {
    memory_index_t(void* mem_ptr, int proj_id_val, bool init) {
      memory_ptr=mem_ptr;
//       chunklist_ptr=0;
      for (int i=0; i < name_length; i++)
	key[i]=0;
      // proj_id identifies which  segment within the series specified
      // by the alloc_key
      proj_id=proj_id_val;		// proj_id == segment number
      initialized=init;
      // segment_page_num identifies which page within a given segment
      // Chunks are allocated on inter segment page boundaries.
      segment_page_num=-1;	// page number within shared mem segment	
      element_offset=-1;
      global_starting_element_num=-1;
    }
    memory_index_t(void* p,
		   char* path_name,
 		   int project_id,
		   int shared_mem_page_num,
		   int elem_offset,
		   int global_start_elem_num) {
      memory_ptr=p;
//       chunklist_ptr=0;
      strcpy(key,path_name);
      proj_id=project_id;
      initialized=true;
      segment_page_num=shared_mem_page_num;		
      element_offset=elem_offset;
      global_starting_element_num=global_start_elem_num;
    }
    memory_index_t(void) {
      // initialize with null data
      memory_ptr=0;
//       chunklist_ptr=0;
      // memset(key,0,name_length);
      proj_id=-1;
      initialized=false;
      segment_page_num=-1;		
      element_offset=-1;
      global_starting_element_num=-1;
    }
    // destructor
    virtual ~memory_index_t() {};
    // copy constructor
    memory_index_t(const memory_index_t& t) {
      memory_ptr=t.memory_ptr;
//       chunklist_ptr=t.chunklist_ptr;
      proj_id=t.proj_id;
      initialized=t.initialized;
      strcpy(key,t.key);
      segment_page_num=t.segment_page_num;
      element_offset=t.element_offset;
      global_starting_element_num=t.global_starting_element_num;
    }
    // assignment operator
    memory_index_t& operator=(const memory_index_t& t) {
      if (this != &t) {		// avoid self assignment: t=t
	memory_ptr=t.memory_ptr;
// 	chunklist_ptr=t.chunklist_ptr;
 	proj_id=t.proj_id;
	initialized=t.initialized;
	strcpy(key,t.key);
	segment_page_num=t.segment_page_num;
	element_offset=t.element_offset;
	global_starting_element_num=t.global_starting_element_num;
      }
      return *this;
    }
    void* get_memory_ptr(void) {return memory_ptr;}
    void set_memory_ptr(void* val) {memory_ptr=val;}
//     void* get_chunklist_ptr(void) {return chunklist_ptr;}
//     void set_chunklist_ptr(void* val) {chunklist_ptr=val;}
    int get_proj_id(void) {return proj_id;}
//     int get_segment_num(void) {return proj_id;}
    bool get_initialized(void) {return initialized;}
    void set_proj_id(int val) {proj_id=val;}
    void set_initialized(bool val) {initialized=val;}
    void get_key(char* output) {strcpy(output,key);}
    void set_key(char* input) {strcpy(key,input);}
    void set_segment_page_num(int val) {segment_page_num=val;}		
    int get_segment_page_num() {return segment_page_num;}		
    void set_element_offset(int val) {element_offset=val;}
    int get_element_offset() {return element_offset;}
    void set_global_starting_element_num(int val)
    {global_starting_element_num=val;}
    int get_global_starting_element_num()
    {return global_starting_element_num;}
    void print() const {
      std::clog << "Print: class memory_index_t() object:" << std::endl;
      std::clog << "\tmemory_ptr: " << memory_ptr << std::endl;
      std::clog << "\tkey: " << key << std::endl;
      std::clog << "\tproj_id: " << proj_id << std::endl;
      std::clog << "\tinitialized: " << initialized << std::endl;
      std::clog << "\tsegment_page_num: " << segment_page_num << std::endl;
      std::clog << "\telement_offset: " << element_offset << std::endl;
      std::clog << "\tglobal_starting_element_num: " << global_starting_element_num<< std::endl;
    }
  }; // class memory_index_t


  ////////////////////////////////////////////////////////////////////
  //////                           Class map_index_t
  ////////////////////////////////////////////////////////////////////
  //////        
  //////        Class  map_index_t  is  used  to  store  and  retrieve
  //////        pointers to chunk lists.   The Chunk list is stored in
  //////        the  allocator's  Pool class  as  a static  attribute.
  //////        Attaching  classes must  first initialize  their chunk
  //////        list to point to  the correct address in shared memory
  //////        so that these attaching allocators can access the same
  //////        objects on the chunk list as the original allocator in
  //////        the original process.
  //////        
  ////////////////////////////////////////////////////////////////////

  
  class map_index_t :
    public memory_index_t {
  private:
    void* char_chunklist;
    void* map_pr_chunklist;
    void* map_chunklist;
    void* container_chunklist;
    void* object_chunklist;
    pid_t creator_pid;
    int ref_count;		// How many process refer to this object?
  public:
    // constructor
    map_index_t(memory_index_t obj_idx,
		void* str_chk_list,
		void* map_pr_chk_list,
		void* map_chk_list,
		void* con_chk_list,
		void* obj_chk_list,
		pid_t pid) :
      memory_index_t(obj_idx),
      char_chunklist(str_chk_list),
      map_pr_chunklist(map_pr_chk_list),
      map_chunklist(map_chk_list),
      container_chunklist(con_chk_list),
      object_chunklist(obj_chk_list),
      creator_pid(pid){
      ref_count=0;
    };
    // constructor
    map_index_t(memory_index_t mem_idx) :
      memory_index_t(mem_idx) {
      char_chunklist=0;
      map_pr_chunklist=0;
      map_chunklist=0;
      container_chunklist=0;
      object_chunklist=0;
      creator_pid=0;
      ref_count=0;
    }
    // constructor
    map_index_t(void) :
      memory_index_t() {
      char_chunklist=0;
      map_pr_chunklist=0;
      map_chunklist=0;
      container_chunklist=0;
      object_chunklist=0;
      creator_pid=0;
      ref_count=0;
    }
    // destructor
    virtual ~map_index_t() {
      
    }
    // copy constructor
    map_index_t(const map_index_t& t) :
      memory_index_t(t),
      char_chunklist(t.char_chunklist),
      map_pr_chunklist(t.map_pr_chunklist),
      map_chunklist(t.map_chunklist),
      container_chunklist(t.container_chunklist),
      object_chunklist(t.object_chunklist),
      creator_pid(t.creator_pid) {
      ref_count=t.ref_count;
    }
    // assignment operator
    map_index_t& operator=(const map_index_t& t)  {
      // Shows example of superclass assignment
      if (this != &t) {		// avoid self assignment: t=t
	memory_index_t::operator=((memory_index_t&) t);  // do      superclass
	// assignment first.
	char_chunklist=t.char_chunklist;
	map_pr_chunklist=t.map_pr_chunklist;
	map_chunklist=t.map_chunklist;
	container_chunklist=t.container_chunklist;
	object_chunklist=t.object_chunklist;
	creator_pid=t.creator_pid;
	ref_count=t.ref_count;
      }
      return *this;
    }
    void* get_char_chunklist(void) {return char_chunklist;}
    void set_char_chunklist(void* val) {char_chunklist=val;}
    void* get_map_pr_chunklist(void) {return map_pr_chunklist;}
    void set_map_pr_chunklist(void* val) {map_pr_chunklist=val;}
    void* get_map_chunklist(void) {return map_chunklist;}
    void set_map_chunklist(void* val) {map_chunklist=val;}
    void* get_container_chunklist(void) {return container_chunklist;}
    void set_container_chunklist(void* val) {container_chunklist=val;}
    void* get_object_chunklist(void) {return object_chunklist;}
    void set_object_chunklist(void* val) {object_chunklist=val;}
    pid_t get_creator_pid(void) {return creator_pid;}
    void set_creator_pid(pid_t val) {creator_pid=val;}
    const int get_ref_count(void) {return ref_count;}
    void increment_ref_count(void) {ref_count++;}
    void decrement_ref_count(void) {--ref_count;}
  };  // class map_index_t
  

  ////////////////////////////////////////////////////////////////////
  //////           class shared_memory_header
  ////////////////////////////////////////////////////////////////////
  //////   
  //////    This class is embedded as  the first element in all shared
  //////    memory segments.   It contains information  describing the
  //////    state of the allocated memory segment.  This class uses no
  //////    memory pointers as the  shared memory segment is mapped to
  //////    different  addresses  for  different  processes.   So  all
  //////    address values must be relative offsets from the beginning
  //////    of the allocated segment.
  //////    
  //////    The shared_memory_header is followed  in memory by the bit
  //////    vector which  is used to  keep track of the  shared memory
  //////    pages which  have been allocated versus  the shared memory
  //////    pages which are free and available to be allocated.
  //////    
  ////////////////////////////////////////////////////////////////////

  class shared_memory_header_t {
  private:
    // This data must be visible to all shared segment users and it is
    // stored at the beginning of the shared memory segment
    
    const int num_of_pages;  // within the segment, tracked separately
    const int page_size;     // segment size = num_of_pages*page_size +
                             //                sizeof(header)
    const int proj_id;  // NOTE: proj_id == segment_num
    allocator_bit_vector::allocator_bit_vector_t bit_vec;	
    const int bit_vec_size;	// size of the bit vector
    const int mem_start_offset; // offset in bytes to usable memory
                                // mem_start_offset = sizeof(header) +
                                //                    sizeof(bit_vec)
    const int object_size;	// total    size   including   header,
                                // bit_vec, and available space


    const int shared_memory_addr; // address of  shared memory segment
				  // in memory.
    const int shared_memory_size; // total  size  of allocated  shared
				  // memory segment.

    // Key value used by ftok, same as the alloc_key
    char key[name_length];
    int header_size;
    // Create a reference to retrieve an object index
    map_index_t object_index;

    locks lock_set;

    int ref_count; 		// Used  to  keep  track of  how  many
				// objects  are  referencing or  using
				// the  shared_memory_header_t object.
				// When only one object is referencing
				// the  segment,  the  value is  zero.
				// When  the last  object  shuts down,
				// the locks  object, lock_set is also
				// destroyed by teh destructor.

  public:
    // constructor
    // place the bit_vec at the address within the segment
    // directly after the shared_memory_header_t
    // so the address is from the beginning of this plus
    // the size of this.
    shared_memory_header_t(int num_of_pages,
			   int page_size,
			   allocator_key_t alloc_key,
			   int shared_mem_addr,
			   int sh_mem_size,
 			   int proj_id) :
// 			   int shmid_val) :
      num_of_pages(num_of_pages),
      page_size(page_size),
      proj_id(proj_id),
      bit_vec(num_of_pages,	// keeps track of  pages in shared mem
				// segment
	      reinterpret_cast(this)+
	      sizeof(shared_memory_header_t)),
      bit_vec_size((num_of_pages/8) + 1),
      // mem_start_offset = sizeof(header) + sizeof(bit_vec)
      mem_start_offset(sizeof(shared_memory_header_t) +  
		       bit_vec_size),	// offset to  usable memory in
					// segment
      // includes header, bit_vec, and usable memory.
      object_size(mem_start_offset +
		  num_of_pages*page_size),
      shared_memory_addr(shared_mem_addr),
      shared_memory_size(sh_mem_size),
      object_index(),
      lock_set(alloc_key,proj_id) {

      strcpy(key,alloc_key);
//       shmid=shmid_val;
      header_size=sizeof(this);
      bit_vec.clear_items(0,num_of_pages);	// page free/allocated list
      ref_count = 0;
#ifdef DEBUG
      std::cerr << __FILE__ << ":" << __LINE__ <<
	" shared_memory_header_t()constructor : ref_count : " <<
	ref_count << std::endl;
#endif
    } // constructor
    
    // destructor
    virtual ~shared_memory_header_t() {
#ifdef DEBUG
      std::cerr << __FILE__ << ":" << __LINE__ << " ~shared_memory_header_t()" << std::endl;
#endif
      lock_set.~locks();
    };
    // copy constructor
    shared_memory_header_t(const shared_memory_header_t& t) :
      num_of_pages(t.num_of_pages),
      page_size(t.page_size),  //
      proj_id(t.proj_id),
      bit_vec(t.bit_vec),
      bit_vec_size(t.bit_vec_size),
      // mem_start_offset = sizeof(header) + sizeof(bit_vec)
      mem_start_offset(t.mem_start_offset),	
      // includes header, bit_vec, and usable memory.
      object_size(t.object_size),
      shared_memory_addr(t.shared_memory_addr),
      shared_memory_size(t.shared_memory_size),
      object_index(t.object_index),
      lock_set(t.lock_set) {

      strcpy(key,t.key);
//       shmid=t.shmid;
      header_size=t.header_size;
      ref_count = 0;
#ifdef DEBUG
      std::cerr << __FILE__ << ":" << __LINE__ <<
	" shared_memory_header_t()copyconstructor : ref_count : " <<
	ref_count << std::endl;
#endif
    } // copy constructor
    
    // assignment operator
    shared_memory_header_t& operator=(const shared_memory_header_t& t) {
      if (this != &t) {		// avoid self assignment: t=t
 	lock_set=t.lock_set;
 	bit_vec=t.bit_vec;
	//	bit_vec_size=t.bit_vec_size;
	strcpy(key,t.key);
// 	shmid=t.shmid;
	header_size=t.header_size;
	ref_count = 0;
#ifdef DEBUG
	std::cerr << __FILE__ << ":" << __LINE__ <<
	  " shared_memory_header_t<>operator= : ref_count : " <<
	  ref_count << std::endl;
#endif
      }
      return *this;
    }  // assignment operator
    const int get_bit_vec_size() {return bit_vec_size;}
//     int get_shmid(void) {return shmid;}
    int get_header_size(void) {return header_size;}
    int get_proj_id(void) {return proj_id;}
//     void set_shmid(int val) {shmid=val;}
    void mark_pages(int page_num, int how_many) {bit_vec.mark_items(page_num,how_many);}
    void clear_pages(int page_num, int how_many) {bit_vec.clear_items(page_num,how_many);}
    int find_free_pages(int pages) {return bit_vec.find_free_items(pages);}
    bool assigned(int page_num) {return bit_vec.assigned(page_num);}
    int get_page_offset(int page_num);
    void get_key(char* val) {strcpy(val,key);}
    void set_key(std::string val) {strcpy(key,val.data());}
//     // for semaphore locking
    void lock(int segment_page_num) {lock_set.lock(segment_page_num);}
    void unlock(int segment_page_num) {lock_set.unlock(segment_page_num);}
    // for object index information
    map_index_t 
    get_map_index(void) {
      // object_index.print();
      return object_index;
    }
    void set_map_index(map_index_t obj_idx) {
      object_index=obj_idx;
      // object_index.print();
    }
    void print_lock_set() const {lock_set.print();}
    void print() const {
      std::cerr << "class shared_memory_header_t<>::print()" << std::endl;
      std::cerr << "\tnum_of_pages: " << num_of_pages << std::endl;
      std::cerr << "\tpage_size: " << page_size << std::endl;
      std::cerr << "\tproj_id: " << proj_id << std::endl;
      std::cerr << "\tbit_vec_size: " << bit_vec_size << std::endl;
//       std::cerr << "\tshmid: " << shmid << std::endl;
      std::cerr << "\theader_size: " << header_size << std::endl;
      lock_set.print();
    }
    // Shared memory synchronize through memory map
    int sync(void) {
      int return_val = msync((void*) shared_memory_addr,
			     (size_t) shared_memory_size,
			     MS_SYNC | MS_INVALIDATE);
      if (return_val == -1) {
	std::clog << "Status: " << __FILE__ << ':' << __LINE__ ;
	std::clog << ": shared memory msync: " << strerror(errno) << std::endl;
      }
      return return_val;
    };
    const int get_shared_memory_addr() {return shared_memory_addr;}
    const int get_shared_memory_size() {return shared_memory_size;}
    int get_ref_count() const {return ref_count;}
    void set_ref_count(int val) {ref_count=val;}
    int get_segment_size(void) {
      return sizeof(mem_space::shared_memory_header_t) + // size of header + 
	((num_of_pages/8) +1) +          // seg bit_vec + 
	num_of_pages*page_size;         // usable memory
    }
  }; // class shared_memory_header_t


  ////////////////////////////////////////////////////////////////////
  //////                       Shared Memory
  ////////////////////////////////////////////////////////////////////
  //////   
  //////    Shared  Memory  is the  class  interface  to the  specific
  //////    operating system.  Class Shared is designed to be embedded
  //////    as  an  attribute in  a  higher  level  class.  Shared  is
  //////    expected to  be static  such that there  will be  only one
  //////    instantiated global copy of this class per process.
  //////    
  //////    The  same shared  memory segment  address space  is mapped
  //////    differently  for each process.   Therefore, if  the memory
  //////    view is  divided into the individual process  view and the
  //////    overall shared memory segment  view which is shared by all
  //////    the  processes, the  overall view  has  assigned different
  //////    absolute  memory addresses  for the  same  physical memory
  //////    space.   So at  the  overall shared  memory level,  memory
  //////    addressing must be accomplished by generic numeric offsets
  //////    from the  beginning memory address (or  base address).  No
  //////    absolute  addressing  should  be  used.  Access  from  the
  //////    process  level does  require absolute  addressing methods,
  //////    and  within each  process,  the addressing  scheme of  the
  //////    shared memory segment should be consistent.
  //////    
  ////////////////////////////////////////////////////////////////////
  
  template
  class shared {
  public:
    const int num_of_pages; // number of pages per segment
    const int page_size;    // segment_size = num_of_pages*page_size +
                            //                sizeof(header)
    enum {
      num_of_segments=127,  // using the proj_id in ftok, 1-127 pages
                            // possible.  Fixed by ftok
      bit_vec_size=num_of_segments/8 + 1
      // bit vector tracks segments not pages.
    };
  private:
    const int segment_size;
    // bit_vec will use bit_vector_buff
    allocator_bit_vector::allocator_bit_vector_t bit_vec;
    // Store a single memory segment pointer
    void* mem_ptr;

    // contains pointers to memory segments.  This is only valid on a
    // per process basis where the memory pointers are consistent
    // static void* buffers[num_of_segments];
    // shared memory id from shmget on a per segment basis
    // static int shmid[num_of_segments];
    
    // use same bit_vec paradigm for shared memory but allocate memory
    // in class  as bit_vector_buff for  its storage.  bit_vec  is not
    // allocated in shared memory as the data is redundant and can be
    // determined using shmat() to see if anyone else is yet attached
    // to the memory.  This structure also keeps track of a collection
    // of shared memory segments
    static unsigned char bit_vector_buff[bit_vec_size];
    typedef std::map > mappings_t;
    static mappings_t mappings;
    // typedef mappings_t::iterator map_it_t;

  public:
    // constructor
    shared(int num_of_pages, int page_size);
    // destructor
    virtual ~shared();
    // copy constructor
    shared(const shared& t);
    // assignment operator
    shared& operator=(const shared& t);
    // allocate does  a memory allocate  or if the memory  segment has
    // already been  allocated, then it does the  attach function.  If
    // proj_id  ==  0, then  allocate  return  the  next free,  unused
    // segment.   If 1  <=  proj_id  < 128,  then  allocate returns  a
    // pointer to the specified segment.
    memory_index_t allocate(const int& proj_id);
    //    memory_index_t allocate();
    bool free();
    void unlink(void) {
      // locate all shared memory segments and unlink them
      for(mappings_t::iterator it = mappings.begin(); it != mappings.end(); it++) {
	// find each existing shared memory segement
#ifdef DEBUG
	std::clog << __FILE__ << ':' << __LINE__ << ':' <<
	  "shared<>::unlink found and unlinking shared segment: " <<
	  it->first << std::endl;
#endif
	shared_memory_header_t* smh =
	  static_cast(it->second.first);
	smh->~shared_memory_header_t();
	shm_unlink(it->first.data());
      }  // for(mappings_t::iterator it = mappings.begin(); it != mappings.end(); it++)
      
    }  // void unlink(void)
  };  // class shared

  ////////////////////////////////////////////////////////////////////////////////
  //////                             Shared Memory
  ////////////////////////////////////////////////////////////////////////////////
  //////   
  //////    Shared Memory is the class interface to the specific operating system.
  //////    This is class is user defined and allows the Arena to generate the 
  //////    generic segments of memory which it will provide to the allocator.  
  //////    
  //////    
  //////    
  ////////////////////////////////////////////////////////////////////////////////

  // The following variables are static, so there is one copy
  // for all objects instantiated within a process
//   template
//   int shared::shmid[shared::num_of_segments] = {0};

//   template
//   void* shared::buffers[shared::num_of_segments] = {0};

  template
  unsigned char shared::bit_vector_buff[shared::bit_vec_size] = {0};

  template
  std::map > shared::mappings;

  // constructor
  template
  shared::shared(int number_of_pages, int page_sz) :
    num_of_pages(number_of_pages),
    page_size(page_sz),
    // segment pages have no overhead, they are just space
    // NOTE: the bit_vec in segment size is different than
    //       the bit_vec local to class shared.  In shared,
    //       the bit_vec keeps track of the total number of
    //       segments allocated for alloc_key.  The bit_vec
    //       used within the segment keeps track of sub-
    //       segment pages of memory.
    segment_size (sizeof(shared_memory_header_t) + // size of header + 
		  ((num_of_pages/8) +1) +          // seg bit_vec + 
		  num_of_pages*page_size),         // usable memory
    // bit_vec is not allocated in shared memory, but on a
    // per process basis.
    bit_vec(num_of_segments,bit_vector_buff) {
    // initialize out to end of the bit_vector buffer
    for (int i=0; i
  shared::~shared() {
    // Just the bit_vec should be automatically destroyed.
    // Opposite of the constructor
    // Check the reference count on the shared memory segment
    // If last object referencing shared memory, delete it
 
#ifdef DEBUG
    std::cerr << __FILE__ << ":" << __LINE__ <<
      " ~shared() : mem_ptr : " << mem_ptr << std::endl;
#endif
    // unmap shared memory
    for(mappings_t::iterator it = mappings.begin(); it != mappings.end(); it++) {
      // for each segment in the shared object
      // if it is not referenced, unlink it
#ifdef DEBUG
      std::cerr << __FILE__ << ":" << __LINE__ << " ~shared() : alloc_addr : "
		<< it->first << std::endl;
#endif
      shared_memory_header_t* smh = static_cast(it->second.first);
      int ref_count = smh->get_ref_count();
#ifdef DEBUG
      std::cerr << __FILE__ << ":" << __LINE__ << " ~shared() : ref_count : "
		<< ref_count << std::endl;
#endif
      std::cerr.flush();
      if (!ref_count) {
	smh->~shared_memory_header_t();
	int ret_val = munmap(it->second.first, it->second.second.st_size);
	if (ret_val == -1) {
	  std::clog << "Status: " << __FILE__ << ':' << __LINE__ ;
	  std::clog << ": shared memory munmap: " << strerror(errno) << std::endl;
	}
	// unlink shared memory
	std::clog << "Status: " << __FILE__ << ':' << __LINE__ ;
	std::clog << ": shared memory shm_unlink called" << std::endl;
	ret_val = shm_unlink(it->first.data());
	if (ret_val == -1) {
	  std::clog << "Status: " << __FILE__ << ':' << __LINE__ ;
	  std::clog << ": shared memory shm_unlink: " << strerror(errno) << std::endl;
	  std::clog << "Shared memory segement: " << it->first.data() << std::endl;
	}
#ifdef DEBUG
	std::cerr << __FILE__ << ":" << __LINE__ <<
	  " ~shared() deleting shm : " << std::endl;
#endif
	// delete the map key
	mappings.erase(it);
      }
    }
  };  // destructor
  

//     }
//     std::map >::iterator it = mappings.find(alloc_key);
//     if (it != mappings.end()) {
//       // Both initialized and mapped. Retrieve pointer and allocation is over.
//       // first, destroy the locks
//       } else {
// 	// Other process still pointing  to the shared memory.  Do not
// 	// remove the  shared memory

// 	// Further  do  not   decrement  the  ref_count.   Only  those
// 	// functions with actually allocate and deallocate memory from
// 	// the  shared  memory  segment  change the  reference  count.
// 	// Shared creates a segment if  it does not exist, and removes
// 	// it  when  the  reference   count  is  zero  and  it  exits.
// 	// Otherwise, the it does not affect the ref_count.

// #ifdef DEBUG
// 	std::cerr << __FILE__ << ":" << __LINE__ <<
// 	  " ~shared() : decrementing ref_count: "  <<
// 	  ref_count << std::endl;
// #endif
//       }

  // copy constructor
  template
  shared::shared(const shared& t)  :
    // segment pages have no overhead, they are just space
    num_of_pages(t.num_of_pages),
    page_size(t.page_size),
    segment_size (t.segment_size),
    bit_vec(t.bit_vec) {

    mem_ptr = t.mem_ptr;
    // Most  of the  important shared::shared  attributes  are static.
    // therefore there  is only one  version of them declared  for all
    // process  objects.  They are  in a  sense global,  and therefore
    // need  not be copied,  as the  same verison  is accessed  by all
    // objects in the same process.
  }; // copy constructor
  
  // assignment operator
  template
  shared& shared::operator=(const shared& t)  {
    if (this != &t) {		// avoid self assignment: t=t
      mem_ptr = t.mem_ptr;
    }
    return *this;
  };  // assignment operator
  
  ////////////////////////////////////////////////////////////////////
  //////    shared<>::allocate(const int& proj_id)
  ////////////////////////////////////////////////////////////////////
  //////   
  //////    proj_id -  
  //////    
  //////    Creates a  large allocation of  shared memory used  by the
  //////    allocator.  Returns a pointer to the newly allocated page,
  //////    parameter  indicates if  the page  is newly  allocated and
  //////    therefore NOT  initialized, or if the page  was already in
  //////    use and simply attached.
  //////    
  //////    
  //////    
  ////////////////////////////////////////////////////////////////////

  // void* shared::allocate(allocator_key_t key_val) {
  template
  memory_index_t shared::allocate(const int& proj_id) {
// 			alloc_addr>::allocate() {
    // alloc_key - string describing key name, identifies which 
    //             shared memory segment.
    // returns  a  pointer  to  the newly  allocated  page,  parameter
    // indicates  if the  page is  newly allocated  and  therefore NOT
    // initialized,  or if  the page  was  already in  use and  simply
    // attached.
    // All shared memory blocks have the shared memory header class
    // instantiated at the beginning of their memory space.

	// Variables

	//   initialized - describes  whether share memory segment has
	//                 header installed.
	//   mapped - describes whether the process has already mapped
	//            this  shared  memory  segment  into  its virtual
	//            memory.
	

	// Possible senarios at this point:

	//    1. Brand new shared memory segment, just created for the
	//       first time.  Needs to be initiallized and mapped.
	//    2. Existing shared memory segment:
	//       a. Process has already  mapped this segment, retrieve
	//          pointer from stored mapping
	//       b. 1st time process is accessing this shared segment.
	//          The segment needs to be mapped into this process's
	//          shared memory space.


    void* ptr = (void*) -1;
    bool initialized = false;
    struct stat stat;
    int fd;
    
    
    // open shared memory
    // first try to open shared memory as if it already exists
    // if that fails, create the shared memory and initialize it
    std::stringstream alloc_key_ss;
    // create the shared segment key
    alloc_key_ss << alloc_key << "_" << proj_id;
#ifdef DEBUG
    std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : alloc_key_ss.str().data() : "
	      << alloc_key_ss.str().data() << std::endl;
#endif
    if ((fd = shm_open(alloc_key_ss.str().data(), O_RDWR, S_IRWXU | S_IRWXG)) == -1) {
      // Opening existing shared memory failed.  Create it.
      fd = shm_open(alloc_key_ss.str().data(), O_RDWR|O_CREAT, S_IRWXU | S_IRWXG);
      // now needs to be mapped
      // truncate it to required size
      if (ftruncate(fd, (off_t)segment_size) != -1) {
	// get info on the shared memory segment file descriptor
	fstat(fd, &stat);
	// After  truncating the  file descriptor,  see if  the shared
	// memory is already  mapped?  If so, return a  pointer to the
	// existing mapped shared memory; otherwise, map it.

	// convert the shared memory's requested address
	// from its char string to an number.
	std::string stringval(alloc_addr);
	std::istringstream sin(stringval);
	int x;
	sin >> std::hex  >> x;
	
	// add an offset based on the proj_id to the memory address
	x = x + segment_size*(proj_id-1);
#ifdef DEBUG
	std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : alloc_addr : "
		  << alloc_addr << std::endl;
	std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : x : "
		  << std::hex << x << std::dec << std::endl;
#endif
	// map shared memory
	ptr =
	  mmap((void*) x, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	// mmap((void*) x, stat.st_size, PROT_READ | PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
	if (ptr != MAP_FAILED) {
	  //	    if (msync((void*) x, stat.st_size, MS_SYNC | MS_INVALIDATE) != -1) {
	  // Install the memory header in the allocated shared memory
	  // ref_count will be set to 0 by the constructor
	  memset(ptr,0,stat.st_size);
	  new(ptr)shared_memory_header_t(num_of_pages,
					 page_size,
					 alloc_key_ss.str().data(),
					 (int) ptr,
					 stat.st_size,
					 proj_id);  // shmid
	  // store off the successful mapping for future retrieval
#ifdef DEBUG
	  std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : " <<
	    "Inserting map val alloc_key_ss.str() : "
		    << alloc_key_ss.str() << std::endl;
#endif
	  mappings.insert(std::pair >(alloc_key_ss.str(),
							 std::pair(ptr,stat)));
	}  else { // if (ptr != MAP_FAILED)
	  // create an error message to throw with the exception
	  std::stringstream s;
	  s << "Error: " << __FILE__ << ':' << __LINE__ << ':'
	    << " memory map mmap failed : " << strerror(errno) <<
	    std::endl;
	  std::clog << s;
	  throw (Alloc_Exception::mmap_exception(s.str()));
	}  // if (ptr != MAP_FAILED)
      } else {  // if (ftruncate(fd, (off_t)segment_size) != -1)
	// create an error message to throw with the exception
	std::stringstream s;
	s << "Error: " << __FILE__ << ':' << __LINE__ << ':'
	  << " shared memory ftruncate: " << strerror(errno) <<
	  std::endl;
	std::clog << s;
	throw (Alloc_Exception::shm_exception(s.str()));
      }  // if (ftruncate(fd, (off_t)segment_size) != -1)
    } else {  // if ((int fd = shm_open(alloc_key, O_RDWR, S_IRWXU | S_IRWXG)) == -1)
      // shared memory already exists.  See if it has been mapped into
      // this process's memory?
      std::map >::iterator it = mappings.find(alloc_key_ss.str());
      if (it != mappings.end()) {
	// shared memory segment has already been mapped into this segment
	initialized = true;
	ptr = it->second.first;
      } else {  // if (it != mappings.end())
	// shared memory segment has not yet been mapped
	// memory map it
	fstat(fd, &stat);
	// convert the shared memory's requested address
	// from its char string to an number.
	std::string stringval(alloc_addr);
	std::istringstream sin(stringval);
	int x;
	sin >> std::hex  >> x;

	// add an offset based on the proj_id to the memory address
	x = x + segment_size*(proj_id-1);
#ifdef DEBUG
	std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : alloc_addr : "
		  << alloc_addr << std::endl;
	std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : x : "
		  << std::hex << x << std::dec << std::endl;
#endif
	ptr =
	  mmap((void*) x, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	// mmap((void*) x, stat.st_size, PROT_READ | PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
	if (ptr == MAP_FAILED) {
	  // create an error message to throw with the exception
	  std::stringstream s;
	  s << "Error: " << __FILE__ << ':' << __LINE__ << ':'
	    << " memory map mmap failed : " << strerror(errno) <<
	    std::endl;
	  std::clog << s;
	  throw (Alloc_Exception::mmap_exception(s.str()));
	}  // if (ptr == MAP_FAILED)
	// store off the successful mapping for future retrieval
#ifdef DEBUG
	std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : " <<
	  "Inserting map val alloc_key_ss.str().data() : "
		  << alloc_key_ss.str().data() << std::endl;
#endif
	mappings.insert(std::pair >(alloc_key_ss.str(),
						       std::pair(ptr,stat)));
	// Do not touch the  reference count, ref_count.  Only objects
	// which allocate  and deallocate  memory from the  segment to
	// the allocator  touch the reference counter.   If the shared
	// object deconstructs  and the ref_count  is 0, it  takes the
	// whole  segement  down   with  it.   Otherwise  the  segment
	// persists.
#ifdef DEBUG
	shared_memory_header_t* smh = static_cast(ptr);
	int ref_count = smh->get_ref_count();
	std::cerr << __FILE__ << ":" << __LINE__ << " shared<>::allocate() : ref_count : "
		  << ref_count << std::endl;
#endif
      }  // if (it != mappings.end())

    } // if ((int fd = shm_open(alloc_key, O_RDWR, S_IRWXU | S_IRWXG)) == -1)


    mem_ptr = ptr;
    return memory_index_t(ptr, proj_id, initialized);
    
  };  // shared<>::allocate(const int& proj_id) 
  
  
  ////////////////////////////////////////////////////////////////////
  //////    shared<>::free(memory_index_t mem_idx)
  ////////////////////////////////////////////////////////////////////
  //////   
  //////    mem_idx -  contains description of shared  memory block to
  //////               be freed by the function.
  //////    
  //////    Release allocated shared memory segment
  //////    
  //////    
  //////    
  ////////////////////////////////////////////////////////////////////
  
  
  // free()
  template
  bool shared::free() {
    
    bool return_flag = true;
#ifdef DEBUG
    std::cerr << __FILE__ << ':' << __LINE__ << ": " << "shared<>::free()" << std::endl;
#endif
    return return_flag;
  }; // bool shared::free(memory_index_t mem_idx)

};  // namespace mem_space

#endif // SHARED_MEMORY_H