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 

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& alloc_key,
	       const int& 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 = &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 = &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& tries = 10;
      bool initialized = false;
      for (int i = 0; ((i < tries) && (!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; isem_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(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