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


// file: shared_memory.cc
// author: Marc Bumble
// June 1, 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.

#include <shared_memory.h>

namespace mem_space {

////////////////////////////////////////////////////////////////////
////// 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 lock individual elements
////// with semaphores from that 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.
//////
////////////////////////////////////////////////////////////////////

// constructor
locks::locks(const mem_space::allocator_key_t&amt; alloc_key,
const int&amt; proj_id) {

std::string key_file(alloc_key);
// create a file for System V ftok from alloc_key
key_file = std::string("/tmp") + key_file;
// create the empty shm key file, used by ftok()
std::ofstream to(key_file.data());
if (!to) perror("Cannot open key file");
to.close();

// set the semaphore key
key_t mykey = ftok(key_file.c_str(),proj_id);
if (mykey == -1) {
std::clog << "Error:" << __FILE__ << ':' << __LINE__ << ':'
<< " ftok unable to create sem key: " << strerror(errno)
<< ": " << key_file << std::endl;
// create an error message to throw with the exception
std::stringstream s;
s << __FILE__ << ':' << __LINE__ << ':'
<< " project id out of range (1 <= proj_id < 128).";
throw (Alloc_Exception::ftok_exception(s.str()));
}
semid = semget(mykey, num_in_array, IPC_CREAT | IPC_EXCL | 0666);
// std::clog << "Status:" << __FILE__ << ':' << __LINE__ << ':'
// << " semid: " << semid << " " << strerror(errno)
// << std::endl;
if (semid >= 0) {
// first to create the semaphores
// need to initialize the semaphore set
union semun arg;
struct semid_ds seminfo;
arg.buf = &amt;seminfo;
// set all semaphores to 1 unit avail
// binary locks, ie one process can sieze the lock at a time for each page.
short unsigned sem_vals[num_in_array];
for (int i = 0; i < num_in_array; i++) {
sem_vals[i] = 1;
// std::clog << "i = " << i << std::endl;
}
arg.array = sem_vals;
if (semctl(semid, 0, SETALL, arg) == -1) {
std::cerr << "File: " << __FILE__ << ':' << __LINE__ << ':'<< "locks::make_lock()";
std::cerr << ": semaphore creation error: " << strerror(errno) << std::endl;
std::stringstream s;
s << __FILE__ << ':' << __LINE__ << ':'
<< " semaphore creation error.";
throw (Alloc_Exception::lock_creation_exception(s.str()));
}
} else {
// The semaphore set already exists
// Do not initialize, just attach and wait until the semaphores are initialized
union semun arg;
struct semid_ds seminfo;
arg.buf = &amt;seminfo;
// wait at most for 10 tries to see if the semaphore get
// initialized by the process which created it. If within 10
// tests, the process does not get initialized, then fail.
const int&amt; tries = 10;
bool initialized = false;
for (int i = 0; ((i < tries) &amt;&amt; (!initialized)); i++) {
// retrieve information on the semaphores
semctl(semid,0,IPC_STAT,arg);
if (arg.buf->sem_otime != 0) {
initialized = true;
}
sleep(1);
}
if (!initialized) {
std::cerr << "File: " << __FILE__ << ':' << __LINE__ << ':'<< "locks::locks()";
std::cerr << ": semaphore initialization error: " << strerror(errno) << std::endl;
std::stringstream s;
s << __FILE__ << ':' << __LINE__ << ':'
<< " semaphore initialization error.";
throw (Alloc_Exception::lock_creation_exception(s.str()));
}
}
}; // constructor

// destructor
locks::~locks() {
// union semun semctl_arg;
// for (int i=0; i<num_in_array; i++) {
// int semctlresult = semctl(semid, i, IPC_RMID, semctl_arg);
#ifdef DEBUG
std::cerr << __FILE__ << ":" << __LINE__ << " ~locks()" << std::endl;
#endif
int semctlresult = semctl(semid, 0, IPC_RMID);
if ((semctlresult!=0)&amt;&amt;(errno!=EINVAL)) {
std::cerr << __FILE__ << ':' << __LINE__ << " locks::~locks(): ";
std::cerr << " semid: " << semid << "semctlresult: ";
std::cerr << semctlresult << " " << strerror(errno) << std::endl;
}
#ifdef SHARED_MEMORY_MESG
std::cerr << "semid: " << semid << " shmctlresult: ";
std::cerr << semctlresult << " " << strerror(errno) << std::endl;
#endif
// }
}; // destructor

// copy consqtructor
locks::locks(const locks&amt; t) {
semid=t.semid;
}; // copy constructor

// assignment operator
locks&amt; locks::operator=(const locks&amt; t) {
if (this != &amt;t) { // avoid self assignment: t=t
semid=t.semid;
} // if (this != &amt;t)
return *this;
}; // assignment operator

// equality operator
bool locks::operator==(const locks&amt; t) {
bool flag=false;
if (semid==t.semid) {
flag=true;
}
return flag;
}; // equality operator

// lock print
void locks::print() const {
// print the current semaphore lock values
struct semid_ds seminfo;
union semun arg;

arg.buf = &amt;seminfo;
semctl(semid, 0, IPC_STAT, arg);
// int nsems = arg.buf->sem_nsems;
unsigned short sem_vals[num_in_array];
arg.array = sem_vals;
semctl(semid, 0, GETALL, arg);
std::cerr << std::endl;
std::cerr << "locks::print()" << std::endl;
std::cerr << "semid = " << semid << std::endl;
std::cerr << "semval[] = ";
for (int i = 0; i < num_in_array; i++) {
if (!(i>25)) {
std::cerr << std::endl;
std::cerr << " ";
}
std::cerr << ":" << sem_vals[i];
}
std::cerr << std::endl;
};

//
void locks::lock(int page_num) {
// only set one lock at a time
struct sembuf sem_buf_array[num_in_array];
sem_buf_array[0].sem_num = page_num > num_in_array;
sem_buf_array[0].sem_op = -1; // take the lock, subtract it away.
sem_buf_array[0].sem_flg = SEM_UNDO;
// std::cerr << __FILE__ << ':' << __LINE__ << ':'<< " locks::lock()";
// std::cerr << "semid: " << semid << std::endl;
int result = semop(semid, sem_buf_array, 1); // operate on 1 lock at a time
if (result!=0) {
std::stringstream s;
std::cerr << __FILE__ << ':' << __LINE__ << ':'<< " locks::lock()";
std::cerr << ": semaphore lock: " << strerror(errno) << " result: " << result << std::endl;
s << ": semaphore lock errno: " << strerror(errno) << " result: " << result;
s << " lock attempt error." << " semid: " << semid << " page_num: " << page_num;
std::cerr << s .str()<< std::endl;
throw (Alloc_Exception::locking_exception(s.str()));
}
}; // void lock(int page_num)

//
void locks::unlock(int page_num) {
// only clear one lock at a time
struct sembuf sem_buf_array[1];
sem_buf_array[0].sem_num = page_num > num_in_array;
sem_buf_array[0].sem_op = 1; // release the lock, add it back
sem_buf_array[0].sem_flg = SEM_UNDO;
int result = semop(semid, sem_buf_array, 1); // operate on 1 lock at a time
if (result!=0) {
std::stringstream s;
std::cerr << __FILE__ << ':' << __LINE__ << ':'<< " locks::unlock()";
std::cerr << ": semaphore unlock: " << strerror(errno) << " result: " << result << std::endl;
s << ": semaphore unlock errno: " << strerror(errno) << " result: " << result;
s << " unlock attempt error." << " semid: " << semid << " page_num: " << page_num;
std::cerr << s .str()<< std::endl;
throw (Alloc_Exception::locking_exception(s.str()));
}
}; // void unlock(int page_num)




////////////////////////////////////////////////////////////////////
////// 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.
//////
////////////////////////////////////////////////////////////////////



int shared_memory_header_t::get_page_offset(int page_num) {

// returns the offset from the beginning of the segment to the
// page indicated by page_num. Just calculates size offset in
// terms of the number of bytes. If shm (shared memory header)
// points to the beginning of the allocated shared memory segment,
// then the actual page address can be interepreted using the
// following calls:
//
// int start_page_offset = smh->get_page_offset(start_page4);
// unsigned char* addr = reinterpret_cast<unsigned char*>(smh) +
// start_page_offset;
//
// addr will then contain the correct memory address. This
// approach is required because the shared memory segment is
// memory mapped to each process differently, so an offset from
// the beginning of the segment address is required.

return sizeof(shared_memory_header_t) + bit_vec_size +
page_num*page_size;
}; // get_page_offset()


} // namespace mem_space