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 <fcntl.h> // for shm_open()
#include <sys/mman.h> // for shm_open()
#include <sys/types.h> // for shared memory shmget(),shmat(),ftok(),semop
#include <unistd.h> // for fork(),getpid(),getppid(), shm_open()
#include <sys/sem.h> // for semop
}
#include <iostream>
#include <string>
#include <fstream> // for std::ofstream to()
#include <sstream> // string stream for stringstream
#include <cerrno>
#include <cstdlib>
// #include <vector>
#include <map>
#include <alloc_exceptions.h>
#include <allocator_bit_vector.h>
#if defined(__GNU_LIBRARY__) &amt;&amt; !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#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&amt; alloc_key,
const int&amt; proj_id);
// destructor
virtual ~locks();
// copy constructor
locks(const locks&amt; t);
// assignment operator
locks&amt; operator=(const locks&amt; t);
// equality operator
bool operator==(const locks&amt; 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&amt; 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&amt; operator=(const memory_index_t&amt; t) {
if (this != &amt;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&amt; 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&amt; operator=(const map_index_t&amt; t) {
// Shows example of superclass assignment
if (this != &amt;t) { // avoid self assignment: t=t
memory_index_t::operator=((memory_index_t&amt;) 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<unsigned char*>(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&amt; 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&amt; operator=(const shared_memory_header_t&amt; t) {
if (this != &amt;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<allocator_key_t alloc_key, allocator_addr_t alloc_addr = 0>
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<std::string,
std::pair<void*,struct stat> > 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<alloc_key, alloc_addr>&amt; t);
// assignment operator
shared<alloc_key, alloc_addr>&amt; operator=(const shared<alloc_key, alloc_addr>&amt; 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&amt; 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<shared_memory_header_t*>(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<allocator_key_t alloc_key, allocator_addr_t alloc_addr = 0>
// int shared<alloc_key, alloc_addr>::shmid[shared::num_of_segments] = {0};
// template<allocator_key_t alloc_key, allocator_addr_t alloc_addr = 0>
// void* shared<alloc_key, alloc_addr>::buffers[shared::num_of_segments] = {0};
template<allocator_key_t alloc_key, allocator_addr_t alloc_addr = 0>
unsigned char shared<alloc_key, alloc_addr>::bit_vector_buff[shared::bit_vec_size] = {0};
template<allocator_key_t alloc_key, allocator_addr_t alloc_addr = 0>
std::map<std::string,
std::pair<void*,struct stat> > shared<alloc_key, alloc_addr>::mappings;
// constructor
template<allocator_key_t alloc_key, allocator_addr_t alloc_addr>
shared<alloc_key, alloc_addr>::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<bit_vec_size; i++) {
// shmid[i]=-1;
// buffers[i]=0;
bit_vector_buff[i]=0;
}
mem_ptr = 0;
// finish initializing the other buffers to num_of_segments
// for (int i=bit_vec_size+1; i<num_of_segments; i++) {
// shmid[i]=-1;
// buffers[i]=0;
// }
}; // constructor
// destructor
template<allocator_key_t alloc_key, allocator_addr_t alloc_addr>
shared<alloc_key, alloc_addr>::~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<shared_memory_header_t*>(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<allocator_key_t,
// std::pair<void*,struct stat> >::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<allocator_key_t alloc_key, allocator_addr_t alloc_addr>
shared<alloc_key, alloc_addr>::shared(const shared<alloc_key, alloc_addr>&amt; 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<allocator_key_t alloc_key, allocator_addr_t alloc_addr>
shared<alloc_key,
alloc_addr>&amt; shared<alloc_key,
alloc_addr>::operator=(const shared<alloc_key,
alloc_addr>&amt; t) {
if (this != &amt;t) { // avoid self assignment: t=t
mem_ptr = t.mem_ptr;
}
return *this;
}; // assignment operator
////////////////////////////////////////////////////////////////////
////// shared<>::allocate(const int&amt; 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<allocator_key_t alloc_key,
allocator_addr_t alloc_addr>
memory_index_t shared<alloc_key,
alloc_addr>::allocate(const int&amt; 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, &amt;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<std::string,
std::pair<void*,struct stat> >(alloc_key_ss.str(),
std::pair<void*,struct stat>(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<std::string,
std::pair<void*,struct stat> >::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, &amt;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<std::string,
std::pair<void*,struct stat> >(alloc_key_ss.str(),
std::pair<void*,struct stat>(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<shared_memory_header_t*>(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&amt; 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<allocator_key_t alloc_key, allocator_addr_t alloc_addr>
bool shared<alloc_key, alloc_addr>::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