gcc 3.x custom allocators
Rob Savoye
rob@welcomehome.org
Fri Jan 10 18:41:00 GMT 2003
On Fri, Jan 10, 2003 at 12:12:54PM -0600, Benjamin Kosnik wrote:
> I've enclosed some weak example code, based on the custom allocators a
> la the pool allocator in TCPL 19.4.2 A User-Defined Allocator.
I don't see an attachement. :-)
> I don't suppose you can post a full example of what you are trying to
> do, one that works with 2.95.3? Then I could try to port it for you.
I attached the 2 files that implement the code I'm using now with
2.95.3 (on Linux), plus a DejaGnu test case that exercises the Memory
class. This code is *way ugly*, as it was a "proof-of-concept" for a
customer last year, who now has me making this work for real. I made my
changes in existing 10 year old code... Part of this is I've now ripped
out all the ugly semaphore code, and made it into a standalone class. I'm also
changing to using POSIX shared memory from the older SVR4 style. There
are two similar allocators in the header file. One is for the containers
used in the memory manager, and the other is for the application itself.
> It would be really nice to have a complete, usable example in the
> libstdc++ docs for this. It would be really, really nice to have docs
> for custom allocators that used: 1) libhoard, 2) persistent allocation,
> 3) shared segments like you are trying to do.
For sure. Putting data structures in shared memory is a pretty common
real-time application trick. This was the first time I'd done this 100% in
C++ though. I use this code to create a std::list in the shared memory that's
a queue of messages between 2 tasks. What's libhoard ?
The ultimate solution would work with both GCC 3 and 2, but I can live with
ifdefs. I've found tons of examples, it's just that they all only work
with libstdc++-v2.
- rob -
-------------- next part --------------
/*
* test the C++ API for the shared memory class
*/
#include <signal.h>
#include <iostream.h>
#include <string>
#include <queue>
#include <list>
#include <stack>
#include <vector>
#include <cstddef>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/stat.h>
#include "Memory.H"
#include "Lock.H"
#include "test.h"
#include "dejagnu.h"
using namespace std;
TestState runtest;
#if defined(__GNU_LIBRARY__) && !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
static void cntrlc_handler (int);
int verbosity;
int proc_tests (string procname);
int start_proc (string procname);
static void usage ();
bool waitforgdb = false;
int
main(int argc, char *argv[])
{
Memory tm, tm2, tmp, *memp, *memp2;
void *mema;
void *ptr, *ptr2;
char buf[1024];
union semun semopts;
// FIXME: for now, this is a scratch area.
char *PPP = new char [12300];
int c, procpid;
string procname;
while ((c = getopt (argc, argv, "hvsp:")) != -1) {
switch (c) {
case 'h':
usage ();
break;
case 's':
waitforgdb = true;
break;
case 'p':
procpid = atoi(optarg);
break;
case 'v':
verbosity++;
break;
default:
usage ();
break;
}
}
// get the file name from the command line
if (optind < argc) {
procname = argv[optind];
// cout << "Will use \"" << procname << "\" for demo " << endl;
}
// test the default constructor
if (tm.getMemSize() == 0 &&
tm.getMemAlloced() == 0 &&
tm.getSemHandle() == 0)
runtest.pass ("Memory::Memory()");
else
runtest.fail ("Memory::Memory()");
// Initialize this shared memory segment
tm.initMem(CNTRL_SKEY+100);
if (tm.getMemSize() == 0)
runtest.pass ("Memory::initMem()");
else
runtest.fail ("Memory::initMem()");
// try to get some semaphores
tm.initializeSemaphore(QSEMKEY+10);
if (tm.getSemKey() == QSEMKEY+10 && tm.getSemHandle() > 0)
runtest.pass ("Memory::initializeSemaphore(key_t)");
else
runtest.fail ("Memory::initializeSemaphore(key_t)");
// see if we can aquire some memory
mema = tm.acquireMemory(QSEMKEY, QMEMSIZE);
if (tm.getMemAddr() != 0 &&
tm.getMemSize() >= QMEMSIZE &&
tm.getSemKey() == QSEMKEY+10)
runtest.pass ("Memory::acquireMemory(key_t, int)");
else
runtest.fail ("Memory::acquireMemory(key_t, int)");
// Ah... can we actually use our semaphore ?
tm.Lock();
if (semctl(tm.getSemHandle(), 0, GETPID, semopts) > 0 &&
semctl(tm.getSemHandle(), 0, GETVAL, semopts) == 1)
runtest.pass ("Lock Semaphore");
else
runtest.fail ("Lock Semaphore");
// now unlock it
tm.Unlock();
if (semctl(tm.getSemHandle(), 0, GETVAL, semopts) == 0)
runtest.pass ("Unlock Semaphore");
else
runtest.fail ("Unlock Semaphore");
// create a new shared memory segment, which should be unique
tm2.acquireMemory(QSEMKEY+1, QMEMSIZE/2);
tm2.initializeSemaphore(QSEMKEY+1);
if (tm.getMemAddr() != tm2.getMemAddr() &&
tm.getSemKey() != tm2.getSemKey() &&
tm.getSemHandle() != tm2.getSemHandle())
runtest.pass ("Multiple segments");
else
runtest.fail ("Multiple segments");
tmp.initializeSemaphore(QSEMKEY+10);
if (tmp.getSemKey() == tm.getSemKey() &&
tmp.getSemHandle() == tm.getSemHandle())
runtest.pass ("Memory::initializeSemaphore(key_t): existing semaphore set");
else
runtest.fail ("Memory::initializeSemaphore(key_t): existing semaphore set");
// create a new shared memory segment, which should attach to the first segment
tmp.acquireMemory(QSEMKEY, QMEMSIZE);
if (tmp.getMemAddr() == tm.getMemAddr() &&
tmp.getMemSize() >= tm.getMemSize() &&
tmp.getSemKey() == tm.getSemKey())
runtest.pass ("Memory::acquireMemory(key_t, int): existing memory segment");
else
runtest.fail ("Memory::acquireMemory(key_t, int): existing memory segment");
// We want to test the version actually running in shared memory, so grab the pointer
// to the tm object's clone at the head of the shared memory segment
memp = tm.getMemBase();
ptr = memp->getMemory(32);
if (ptr != 0)
runtest.pass ("Memory::getMemory(int)");
else
runtest.fail ("Memory::getMemory(int)");
// Ah... but can we write to it without crashing ?
cerr << "Writing \"Hello World\" to " << (void *)ptr << endl;
memcpy(ptr, "Hello World\n", 12);
if (memcmp(ptr, "Hello World\n", 12) == 0)
runtest.pass ("Memory Block usable");
else
runtest.fail ("Memory Block usable");
// Allocate some memory, then free it. The following allocation should be in the same
// memory block we just freed, because the new one is smaller.
ptr = memp->getMemory(128);
// memp->Dump();
memp->releaseMemory(ptr);
ptr2 = memp->getMemory(64);
if (ptr == ptr2)
runtest.pass ("Reallocate freed Memory Block");
else
runtest.fail ("Reallocate freed Memory Block");
// This will create a new memory block, cause it's larger than any of the freed blocks
ptr = memp->getMemory(256);
if (ptr != ptr2)
runtest.pass ("Instantiate new Memory Block");
else
runtest.fail ("Instantiate new Memory Block");
// memp->Dump();
cerr << "=========== Trying to create a list in the shared memory =================" << endl;
list<int, ShmMem<int> > *lp;
lp = new ((char *)ptr) list<int, ShmMem<int> >;
lp->push_front(9999);
if (lp->front() == 9999)
runtest.pass ("Instantiate STL Container in Memory Block");
else
runtest.fail ("Instantiate STL Container in Memory Block");
cerr << lp->front() << endl;
// memp->Dump();
lp->push_front(1111);
if (lp->front() == 1111)
runtest.pass ("Instantiate another STL Container in Memory Block");
else
runtest.fail ("Instantiate another STL Container in Memory Block");
cerr << lp->front() << endl;
cerr << lp->back() << endl;
lp->push_front(2222);
lp->push_back(3333);
// Make this list a global symbol, which we'll test for in childproc
memp->setGlobalSymbol(0, lp);
memp->Dump();
cerr << "------------- Trying to create another list in the shared memory ------------------" << endl;
memp2 = tm2.getMemBase();
ptr = memp2->getMemory(64);
list<int, ShmMem<int> > *sl;
sl = new ((char *)ptr) list<int, ShmMem<int> >;
sl->push_front(101010);
if (sl->front() == 101010)
runtest.pass ("Instantiate another STL Container in another Memory Block");
else
runtest.fail ("Instantiate another STL Container in another Memory Block");
#if 0
if (tm.getMemAddr() == tmp.getMemAddr() &&
tm.getSemKey() == tmp.getSemKey())
runtest.pass ("Attach to existing segment");
else
runtest.fail ("Attach to existing segment");
// now we want to create STL objects in the shared memory segment.
// FIXME: this hardcoded hack for _heap needs to go away
// _heap = (char *)tm.getMemAddr();
// see if the items were actually allocated from shared memory
if (((void *)(&ll.front()) > tm.getMemAddr()) &&
((void *)(&ll.front()) < (char *)tm.getMemAddr()+QMEMSIZE))
runtest.pass ("STL in shared memory");
else
runtest.fail ("STL in shared memory");
#if 0
list<int> l;
l.push_front(22);
l.push_front(1);
l.push_back(333);
// see if the items were actually allocated from heap still memory
if (((void *)(&l.front()) > (void *)main))
runtest.pass ("STL in heap memory");
else
runtest.fail ("STL in heap memory");
#endif
#endif
// tm.Dump();
// tm2.Dump();
cout << endl << endl;
// CntrlDump();
// memp->Dump();
int pid;
// Run the memory tests between two processes
if (procname.size() == 0)
pid = start_proc("./childproc");
else
pid = proc_tests (procname);
// sleep so the child process has time to run, and we have time to debug it. We setup a
// handler for ^C, so we can get out of this sleep when we're done.
if (waitforgdb) {
struct sigaction act1, act2, act3;
act2.sa_handler = cntrlc_handler;
sigaction (SIGINT, &act2, NULL);
sleep(300);
} else {
sleep(1);
}
cerr << endl;
// Kill the child process we started
// kill (pid, SIGQUIT);
}
static void
usage ()
{
cerr << "This program tests shared memory between two proccesses." << endl;
cerr << "Usage: tcxxmem [hp] filename" << endl;
cerr << "-h\tHelp" << endl;
cerr << "-s\tStall" << endl;
cerr << "-p\tPID" << endl;
exit (-1);
}
// Run the memory tests between two processes
int
start_proc (string procname)
{
struct stat procstats;
char *cmd_line[5];
pid_t childpid;
int ret = 0;
// See if the file actually exists, otherwise we can't spawn it
if (stat(procname.c_str(), &procstats) == -1) {
cerr << "Invalid filename \"" << procname << "\"" <<endl;
perror(procname.c_str());
return -1;
}
// setup a command line. By default, argv[0] is the name of the process
memset(cmd_line, 0, sizeof(char *)*5);
cmd_line[0] = new char(50);
strcpy(cmd_line[0], procname.c_str());
if (waitforgdb) {
cmd_line[1] = new char(3);
strcpy(cmd_line[1], "-s");
}
// fork ourselves silly
childpid = fork();
// childpid is a positive integer, if we are the parent, and fork() worked
if (childpid > 0) {
cerr << "Forked sucessfully, child process PID is " << childpid << endl;
return childpid;
}
// childpid is -1, if the fork failed, so print out an error message
if (childpid == -1) {
/* fork() failed */
perror(procname.c_str());
return -1;
}
// If we are the child, exec the new process, then go away
if (childpid == 0) {
// Start the desired executable
cout << "Starting " << procname << " with " << cmd_line[0] << endl;
ret = execv(procname.c_str(), cmd_line);
perror(procname.c_str());
exit(0);
}
}
// Run the memory tests between two processes
int
proc_tests (string procname)
{
return start_proc(procname);
}
void
cntrlc_handler (int sig)
{
cerr << "Got a ^C !" << endl;
}
-------------- next part --------------
//-------------------------------------------------------------------------
// Class Name: Memory
// Description:
// This class provides access to and management of shared memory. The
// two acquire methods allow creation of and access to a shared memory
// segment identified by the key. The AcquireMemory method will
// initialize the pointers to the memory and zero out all available
// memory. It is necessary to attach after the acquire is performed/
// If the memory needs to be a certain value, use the
// getMemory(int,char) version. The memory will be automatically
// detached and release when the memory object is destructed.
//
// Modified: by: What:
//-------------------------------------------------------------------------
#ifndef MEMORY_H_
#define MEMORY_H_
#include "config.h"
#include <stdlib.h>
#include <list>
#include <vector>
#include <map>
#include <sys/types.h>
#include "ctas_defs.h"
#include "ctas_debug.h"
#ifdef HAVE_SEM_INIT
#include <semaphore.h> /* POSIX semaphores */
#else // SysV style semaphores
#include <sys/ipc.h>
#include <sys/sem.h>
// #include <linux/sem.h>
#endif
const int NUM_GLOBAL_SYMBOLS = 10;
const int NUM_OF_SEMS = 10;
const key_t SEMKEY = 0x3562;
const key_t CNTRL_SKEY = 0xf0b0;
#define CNTRL_BLK_SIZE 10 // enough to hold 30 shared memory blocks
#define LISTMEM_SIZE 409600 // enough to hold 198 memory segments
#ifdef HAVE_SEM_INIT
typedef sem_t SemHandle_t;
#else
typedef int SemHandle_t;
#endif
typedef enum {UNUSED, ALLOCATED, FREE} memstate;
// Accumulate info so later we can print out stats, and see how we're doing
// We use a structure, rather than a class to try to keep this from impacting
// the performance.
struct memstats {
unsigned int freed_hits;
unsigned int freed_bytes;
unsigned int control_bytes;
unsigned int heap_hits;
unsigned int heap_bytes;
unsigned int bytes_allocated;
};
class MemSegment {
private:
memstate state;
void *addr;
size_t size;
int used;
public:
MemSegment(void)
{
state = FREE;
addr = 0;
size = 0;
used = 0;
}
MemSegment(void *x)
{
state = FREE;
addr = x;
size = 0;
used = 0;
}
MemSegment(int x)
{
state = FREE;
addr = 0;
size = x;
used = 0;
}
MemSegment(void *x, size_t sz)
{
addr = x;
size = sz;
state = ALLOCATED;
used = 1;
}
~MemSegment(void) {}
MemSegment & operator = (MemSegment &);
void *getAddr (void)
{
return addr;
}
void setAddr (void *x)
{
addr = x;
}
void setSize (size_t x)
{
size = x;
}
size_t getSize (void)
{
return size;
}
void setState (memstate x)
{
state = x;
}
memstate getState (void)
{
return state;
}
// these keep track of how many times we are allocated
void incrUsed(void) { used++ ; }
int getUsed (void ) { return used; }
void Dump(void)
{
if (addr != 0) {
cerr << "\tSegment is " << size << " bytes at " << hex << addr << dec ;
if (state == FREE)
cerr << " FREED ";
else
cerr << " ALLOCATED ";
cerr << used << " times" << endl;
} else {
cerr << "\tFIXME: Bad MemSegment data!" << endl;
}
}
};
extern void *_start;
// forward declare Memory, so we can use it in this class. It gets defined
// later in this file, but without this, there is a circular dependency.
class Memory;
template <typename T, typename TFASallocType = allocator<T> >
class ShmMem
{
public:
TFASallocType alloc;
typedef typename TFASallocType::size_type size_type;
typedef typename TFASallocType::difference_type difference_type;
typedef typename TFASallocType::pointer pointer;
typedef typename TFASallocType::const_pointer const_pointer;
typedef typename TFASallocType::reference reference;
typedef typename TFASallocType::value_type value_type;
typedef typename TFASallocType::const_reference const_reference;
template <typename U> struct rebind {
typedef ShmMem<U,
typename TFASallocType::template rebind<U>::other> other;
};
ShmMem(void) { }
~ShmMem() {}
ShmMem(const ShmMem& x)
: alloc(x.alloc) {}
template <typename U>
ShmMem(const ShmMem<U,
typename TFASallocType::template rebind<U>::other>& x)
: alloc(x.alloc) {}
void construct(pointer p, const value_type &val)
{
new (p) T(val);
DBG_MSG(DBG_INFO, "constructing from shared memory 0x%x", (unsigned long)p);
}
void destroy(pointer p)
{
p->~T();
}
pointer allocate(size_type sz, const void* vp = 0)
{
pointer p;
int i;
// If our control block has been allocated, then look for a segment. We start from
// the back of the list, because it makes it easy to use the this pointer to see
// which segment we are in.
if (_cntrl_base) {
for (i=CNTRL_BLK_SIZE; i >= 0; --i) {
// for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0) {
// We can use where we are coming in from (this), to look between the shared
// memory segments we've previously created to see which one we're located in.
#ifdef MEM_DEBUG
#if 0
cerr << "Comparing " << (void *)this << " to "
<< _cntrl_base[i] << "\t" << ((Memory *)this > _cntrl_base[i]) << endl;
// << (void *)((char *)_cntrl_base[i] + _cntrl_base[i]->getMemSize()) << endl;
#endif
#endif
#if 1
if ((Memory *)this > _cntrl_base[i] ) {
#else
if ((Memory *)this > _cntrl_base[i] &&
(char *)this < ((char *)_cntrl_base[i] + _cntrl_base[i]->getMemSize()) ) {
#endif
// cerr << (void *)this << " is in shared segment " << _cntrl_base[i] << endl;
break;
}
}
} // for i
// _cntrl_base[i]->Dump();
if (_cntrl_base[i] != 0)
p = (pointer)_cntrl_base[i]->getMemory(sizeof(pointer)*sz);
else
DBG_MSG(DBG_EROR, "Subscript %d invalid, points to bogus block\n", i);
#ifdef MEM_DEBUG
cerr << "Allocated " << sizeof(pointer)*sz << " bytes of memory from segment " << i << " at "
<< (void*)p << " normally" << endl;
#endif
}
return p;
}
// Deallocate previously allocated memory
void deallocate(pointer p, size_type sz)
{
int i;
// If our control block has been allocated, then look for a segment
if (_cntrl_base) {
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0) {
// We can use where we are coming in from (this), to look between the shared
// memory segments we've previously created to see which one we're located in.
#ifdef MEM_DEBUG
#if 0
cerr << "Comparing " << (void *)this << " to "
<< _cntrl_base[i] << "\t"
<< (void *)((char *)_cntrl_base[i] + _cntrl_base[i]->getMemSize()) << endl;
#endif
#endif
if ((Memory *)this > _cntrl_base[i] &&
(char *)this < ((char *)_cntrl_base[i] + _cntrl_base[i]->getMemSize()) ) {
// cerr << (void *)this << " is in shared segment " << _cntrl_base[i] << endl;
break;
}
}
} // for i
// cerr << "FIXME: deallocate " << sz << " 0x" << hex << (unsigned long)p << dec << endl;
_cntrl_base[i]->releaseMemory(p);
// operator delete(p);
}
}
};
// Define a custom allocator for the memory manager itself
template <typename T, typename TFASControlType = allocator<T> >
class ShmControlMem
{
public:
TFASControlType alloc;
typedef typename TFASControlType::size_type size_type;
typedef typename TFASControlType::difference_type difference_type;
typedef typename TFASControlType::pointer pointer;
typedef typename TFASControlType::const_pointer const_pointer;
typedef typename TFASControlType::reference reference;
typedef typename TFASControlType::value_type value_type;
typedef typename TFASControlType::const_reference const_reference;
template <typename U> struct rebind {
typedef ShmControlMem<U,
typename TFASControlType::template rebind<U>::other> other;
};
ShmControlMem(void) { }
~ShmControlMem() {}
ShmControlMem(const ShmControlMem& x)
: alloc(x.alloc) {}
template <typename U>
ShmControlMem(const ShmControlMem<U,
typename TFASControlType::template rebind<U>::other>& x)
: alloc(x.alloc) {}
void construct(pointer p, const value_type &val)
{
new (p) T(val);
DBG_MSG(DBG_INFO, "constructing from control block at 0x%x", (unsigned long)p);
}
void destroy(pointer p)
{
p->~T();
}
// Allocate a block of memory. This has to search through the block of shared memory to
// see which one we want to allocate memory in.
pointer allocate(size_type sz, const void* vp = 0)
{
pointer p;
int i;
// If our control block has been allocated, then look for a segment
if (_cntrl_base) {
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0) {
// We can use where we are coming in from (this), to look between the shared
// memory segments we've previously created to see which one we're located in.
if ((char *)_cntrl_base[i]->getRawMem() == (char *)this) {
#ifdef MEM_DEBUG
// cerr << (void *)this << " is in shared segment " << _cntrl_base[i] << endl;
#endif
break;
}
#ifdef MEM_DEBUG
#if 0
cerr << "Comparing " << (void *)this << " to "
<< _cntrl_base[i] << "\t"
<< (void *)((char *)_cntrl_base[i] + _cntrl_base[i]->getMemSize()) << endl;
#endif
#endif
if ((Memory *)this > _cntrl_base[i] &&
(char *)this < ((char *)_cntrl_base[i] + _cntrl_base[i]->getMemSize()) ) {
#ifdef MEM_DEBUG
// DBG_MSG(DBG_INFO, "0x%x is in shared segment 0x%x" << (void *)this, _cntrl_base[i]);
#endif
break;
}
}
} // for i
if (_cntrl_base[i]->getHeapAddr() <= ((char *)_cntrl_base[i]->getMemAddr() + _cntrl_base[i]->getMemAlloced())) {
p = (pointer)_cntrl_base[i]->getHeapAddr(); // Get the current heap pointer
_cntrl_base[i]->setHeapAddr(p+sz); // Increment the heap pointer
#ifdef MEM_STATS
// FIXME: this size is based on seeing what memory has gotten allocated,
// and shouldn't be hardcoded here.
_cntrl_base[i]->IncrControlBytes(sizeof(MemSegment));
#endif
} else {
DBG_MSG(DBG_EROR, "FIXME: Out of control block memory!");
// return (pointer)-1;
}
// _cntrl_base[i]->Unlock(); // Relinquish exclusive control to the memory segment
#ifdef MEM_DEBUG
cerr << "Allocated " << sz << " object from control block memory" << " at "
<< (void*)p << endl;
#endif
return p;
}
}
// deallocate a memory block we created with allocate()
void deallocate(pointer p, size_type sz)
{
DBG_MSG(DBG_WARN, "FIXME: custom deallocate isn't implemented!!!!, can't delete 0x%x", (unsigned long)p);
}
};
// Define a typedef to make it easier to use this long data types.
typedef list<MemSegment, ShmControlMem<MemSegment> > ShmControlList_t;
// typedef map<int, void *> FindAddr_t;
// typedef vector<void *, ShmMem<void *> > FindAddr_t;
typedef map<int, void *, ShmMem<pair<int, void *> > > FindAddr_t;
extern Memory **_cntrl_base;
extern void **_findaddr;
// A class for handling shared memory segments. This takes a key and a size (in
// bytes) for the segment. If it works, it then stores the base address of the
// segment in _shmAddr,
class Memory {
public:
Memory(void);
Memory(int);
Memory(key_t, int);
~Memory(void);
void *acquireMemory(key_t, int); // acquires shared memory
void *acquireMemory(key_t, int, void *); // acquires shared memory
bool attachMemory(void); // attach to acquired shared memory
SemHandle_t initializeSemaphore(int); // initialize a semaphore for the segment
bool isSharedMemoryLow(void);
bool isSharedMemoryOut(void);
void *getMemory(int);
void *getMemory(int, char);
void releaseMemory(void *);
void Lock(void) { Lock(_semHandle); }
void Lock(SemHandle_t);
void Unlock(void) { Unlock(_semHandle); }
void Unlock(SemHandle_t);
Memory *getMemBase (void) { return membase; }
void setMemBase (Memory *x) { membase = x; }
long getMemSize (void) { return _shmSize; }
void setMemFd (int x) { _sharedMemFd = x; }
int getMemFd (void) { return _sharedMemFd; }
void setMemSize (int x) { _shmSize = x; }
char *getMemAddr (void) { return _shmAddr; }
void setMemAddr (void *x) { _shmAddr = (char *)x; }
long getMemAlloced (void) { return _shmAlloced; }
void setMemAlloced (long x) { _shmAlloced = x; }
void incrMemAlloced (long x) { _shmAlloced += x; }
long getSemFd (void) { return _sharedMemFd; }
SemHandle_t getSemHandle (void) { return _semHandle; }
void setSemHandle (SemHandle_t x) { _semHandle = x; }
key_t getSemKey (void) { return _semKey; }
void setSemKey (key_t key) { _semKey = key; }
key_t getShmKey (void) { return _shmKey; }
void setShmKey (key_t key) { _shmKey = key; }
void *baseAddressGet(void);
void Dump(void);
void DumpStats(void);
void *initMem(void) { initMem(CNTRL_SKEY); }
void *initMem(key_t);
void *getListAddr (void) { return (void *)rawmem; }
void setListAddr (void *ptr) { (void *)rawmem = ptr; }
// void setFindAddr (void *ptr) { findaddr = ptr; }
void clearList (void) { rawmem->clear(); }
Memory & operator = (Memory &);
void setHeapAddr (void *x) { _heap = (char *)x; }
void incrHeapAddr (int x) { _heap += x; }
char *getHeapAddr (void) { return _heap; }
ShmControlList_t *getRawMem (void ) { return rawmem; }
// void *getGlobalSymbol (int key) { return findaddr[key]; };
// void setGlobalSymbol (int key, void *addr) { findaddr[key] = addr; }
void *getGlobalSymbol (int key);
void setGlobalSymbol (int key, void *addr);
#ifdef MEM_STATS
void IncrControlBytes(int x) { stats.control_bytes += x; }
#endif
void setMemOwner (pid_t pid) { _memoryOwner = pid; }
pid_t getMemOwner (void) { return _memoryOwner; }
private:
Memory *membase;
long _shmAlloced; // total bytes allocated from this segment
long _shmSize; // the size of the shared memory segment
int _sharedMemFd;
char *_shmAddr; // the base address of the shared memory
SemHandle_t _semHandle; // the handle for the shared memory segment
key_t _semKey; // the semaphore key
key_t _shmKey; // the shared memory key
pid_t _memoryOwner;
int _lowMemLevel;
int _memOutLevel;
static const float LOW_MEM_RATIO;
struct memstats stats;
char *_heap;
#if 0
FindAddr_t *findaddr; // keep track of address for the segments
#else
FindAddr_t *fooby;
#endif
ShmControlList_t *rawmem;
};
extern void CntrlDump (void);
#if 0
inline bool Memory::isSharedMemoryLow()
{
if (_shmAlloced >= (_shmSize * LOW_MEM_RATIO))
return true;
else
return false;
}
inline bool Memory::isSharedMemoryOut()
{
if (_shmAlloced < _shmSize)
return true;
else
return false;
}
#endif
#endif
-------------- next part --------------
//-------------------------------------------------------------------------
// Class Name: Memory
//
// Description:
// This class provides management of shared memory. Memory is managed
// using two double-linked lists. One list is linked by address, the
// other is linked by size. This class includes trash compaction where
// adjacent memory locations are merged to create a larger memory space.
// All links are maintained as offsets to the base of the shared memory
// segment allowing the system to give different base addresses of the
// attach. The five inline functions provide the necessary conversions
// from offsets to actual addresses.
//
// Modified: by: What:
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string>
#include <iostream.h>
#include <sys/time.h>
#include <ulimit.h>
#include <sys/resource.h>
#include <list>
#include "config.h"
#include "Memory.H"
#include "ra_defs.h"
// #include "Lock.H"
#include "ctas_debug.h"
#include "ctas_defs.h"
#include <map>
// Solaris needs to use this as a flag to shmat() to attach to an already allocated
// shared memory segment.
#ifndef SHM_SHARE_MMU
#define SHM_SHARE_MMU 0
#endif
#if defined(__GNU_LIBRARY__) && !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
// Set the low mem ratio which is used to signal when the shared
// memory is getting low, and when shared memory is exhausted.
const float Memory::LOW_MEM_RATIO = .30; // FIXME: was 0.20
#ifndef HAVE_SEM_INIT
key_t semkey = 13666;
#endif
Memory **_cntrl_base;
void **_findaddr;
void
CntrlDump (void)
{
int i;
if (_cntrl_base) {
cerr << "Control Block contains: " ;
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] == 0 ) {
cerr << i << " shared memory segments" << endl;
break;
}
}
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0) {
cerr << "\tMemory segment at " << hex << _cntrl_base[i] << dec
<< ", size is " << (_cntrl_base[i])->getMemSize()
<< ", shmkey is " << (void *)(_cntrl_base[i])->getShmKey() << endl;
}
}
#if 0
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0 ) {
_cntrl_base[i]->Dump();
}
}
#endif
}
}
void *
Memory::getGlobalSymbol (int key)
{
if (key < NUM_GLOBAL_SYMBOLS) {
cerr << "Getting the Global Symbol for key " << key << " which has the address of " << _findaddr[key] << endl;
return _findaddr[key];
} else {
return (void *)0;
}
}
void
Memory::setGlobalSymbol (int key, void *addr)
{
if (_findaddr[key] == 0 && key < NUM_GLOBAL_SYMBOLS) {
cerr << "Setting the Global Symbol for key " << key << " at address " << addr << endl;
_findaddr[key] = addr;
} else {
cerr << "Specified Global Symbol key " << key << " already used, forcably setting to 1." << endl;
_findaddr[key+1] = addr;
}
}
void
Memory::Dump (void)
{
int i;
cerr << "Memory header data is: " << endl;
cerr << "\t" << "_ShmAddr is " << hex << (void *)_shmAddr << dec << endl;
cerr << "\t" << "_ShmSize is " << _shmSize << endl;
cerr << "\t" << "_semHandle is " << _semHandle << endl;
cerr << "\t" << "_semKey is " << _semKey << endl;
cerr << "\t" << "_shmAlloced is " << _shmAlloced << endl;
cerr << "\t" << "rawmem is " << (void *)rawmem << endl;
cerr << "\t" << "_heap is " << (void *)_heap << endl;
if (rawmem) {
cerr << "Memory contains " << rawmem->size() << " elements" << endl;
if (rawmem->size()) {
ShmControlList_t::iterator pos;
for (pos = rawmem->begin(); pos != rawmem->end(); pos++) {
if (pos->getAddr() != 0) {
#if 1
pos->Dump();
#endif
} else {
cerr << "\tFIXME: Bad MemSegment data!" << endl;
}
}
}
} else {
cerr << "FIXME: Rawmem doesn't exist !" << endl;
}
cerr << "Global Symbols are: " << endl;
for (i= 0; i<10; i++) {
if (_findaddr[i] != 0)
cerr << "\t" << _findaddr[i] << endl;
}
#ifdef MEM_STATS
DumpStats();
#endif
}
void
Memory::DumpStats(void)
{
cerr << endl << "Memory Manager statistics: " << endl;
cerr << "\tTotal number of bytes requested: " << stats.bytes_allocated << endl;
cerr << "\tTotal number of bytes in segment: " << _shmSize << endl;
cerr << "\tPercentage of bytes allocated from segment:\t"
<< ((float)_shmAlloced/_shmSize)*100.0 << "%" << endl << endl;
cerr << "\tTotal number of bytes allocated from control block: " << stats.control_bytes << endl;;
cerr << "\tPercentage of bytes allocated from control block:\t"
<< ((float)stats.control_bytes/LISTMEM_SIZE)*100.0 << "%" << endl << endl;
cerr << "\tNumber of hits to freed memory blocks: " << stats.freed_hits << endl;
cerr << "\tNumber of bytes allocated from freed memory blocks: "
<< stats.freed_bytes << endl;
cerr << "\tPercentage of hits from freed memory blocks:\t"
<< ((float)stats.freed_hits/(stats.heap_hits + stats.freed_hits)) * 100.0 << "%" << endl;
cerr << "\tPercentage of bytes allocated from freed blocks: "
<< ((float)stats.freed_bytes/stats.bytes_allocated) * 100.0 << "%"
<< endl << endl;
cerr << "\tNumber of new memory blocks from heap: " << stats.heap_hits << endl;
cerr << "\tNumber of bytes allocated from heap: " << stats.heap_bytes << endl;
cerr << "\tPercentage of allocations from heap vs freed:\t"
<< ((float)stats.heap_hits/(stats.heap_hits + stats.freed_hits)) * 100.0 << "%" << endl;
cerr << "\tPercentage of bytes allocated from heap: "
<< ((float)_shmAlloced/_shmSize) * 100.0 << "%"
<< endl << endl;
}
//-------------------------------------------------------------------------
// Function: Memory
// Description: This is the constructor, it insures that the class
// variables are cleared and initializes the header size to be
// a multiple of 4 (rounds up)
// Pre: None
// Post: None
// Input: None
// InOut: None
// Return: None
// Global: None
//-------------------------------------------------------------------------
Memory::Memory(void)
{
_memoryOwner = 0;
_sharedMemFd = 0;
_shmAddr = 0;
_shmSize = 0;
_semHandle = 0;
_semKey = 0;
_shmKey = 0;
_shmAlloced = 0;
membase = 0;
rawmem = 0;
fooby = 0;
_heap = 0;
#ifdef MEM_STATS
stats.freed_hits = 0;
stats.freed_bytes = 0;
stats.control_bytes = 0;
stats.heap_hits = 0;
stats.heap_bytes = 0;
stats.bytes_allocated = 0;
#endif
_lowMemLevel = 0;
_memOutLevel = 0;
}
void *
Memory::initMem(key_t key)
{
char *mema;
int fd;
shmid_ds shmInfo;
int size = CNTRL_BLK_SIZE * sizeof(Memory *) + LISTMEM_SIZE;
if (initializeSemaphore(key+10) < 0) {
cerr << "Can't initialize the semaphores for key " << key << endl;
}
// Initialize the control block
if (_cntrl_base == 0) {
cerr << "Initializing Control block" << endl;
if ((fd = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666)) < 0) {
DBG_MSG(DBG_EROR, "Shmget failed with (%d) %s\n", errno, strerror(errno));
if (errno == EEXIST) {
if ((fd = shmget(key, size, 0666)) < 0) {
DBG_MSG(DBG_EROR, "Shmget failed again with (%d) %s\n", errno, strerror(errno));
} else {
DBG_MSG(DBG_INFO, "shmget(0x%x, %d, %d) %d\n", key, size, 0666, fd);
}
}
}
shmctl(fd, IPC_STAT, &shmInfo);
// Check if there are any processes attached to this segment.
if (shmInfo.shm_nattch == 0) {
cerr << "Control Block shared memory id is " << fd << endl;
// No processes attached, so attempt to remove existing
// segment, and allocate a new one.
shmctl(fd, IPC_RMID, &shmInfo);
fd = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666);
if ((mema = (char *)shmat(fd, 0, 0)) == (char *)-1) {
cerr << "FIXME: shmat() error: " << strerror(errno) << endl;
} else {
_cntrl_base = (Memory **)mema;
memset(_cntrl_base, 0, CNTRL_BLK_SIZE*sizeof(Memory *));
_findaddr = (void **)(char *)_cntrl_base + (CNTRL_BLK_SIZE * sizeof(Memory *));
}
} else {
cerr << "Process PID " << shmInfo.shm_cpid << " already attached!" << endl;
if ((mema = (char *)shmat(fd, 0, 0)) == (char *)-1) {
cerr << "FIXME: shmat() error: " << strerror(errno) << endl;
} else {
_cntrl_base = (Memory **)mema;
_findaddr = (void **)(char *)_cntrl_base + (CNTRL_BLK_SIZE * sizeof(Memory *));
}
}
// mema = acquireMemory(CNTRL_SKEY, CNTRL_BLK_SIZE * sizeof(Memory *) + LISTMEM_SIZE);
// cerr << "Control block base is " << hex << cntrl << dec << endl;
// If there is data behind the pointer, then we have already initialized this
// segment, so we just want to grab the pointer, not stomp it by doing a new[].
}
cerr << "Control block base is at " << (void *)_cntrl_base << endl;
cerr << "Findaddr base is at " << (void *)_findaddr << endl;
return _cntrl_base;
}
// Aquire memory of a specified size using the default semaphore key
Memory::Memory(int size)
{
acquireMemory(_semKey, size);
}
// Aquire memory of a specified size using the specified semaphore key
Memory::Memory(key_t key, int size)
{
acquireMemory(key, size);
}
Memory &
Memory::operator = (Memory &x)
{
cerr << "Copying one Memory class to another" << endl;
// rawmem = x.getList();
_shmAlloced = x.getMemAlloced();
_shmSize = x.getMemSize();
_shmAddr = x.getMemAddr();
_semHandle = x.getSemHandle();
_semKey = x.getSemKey();
_sharedMemFd = x.getSemFd();
// Membase should point to ourself
membase->setMemBase(this);
}
MemSegment &
MemSegment::operator = (MemSegment &x)
{
// cerr << "Copying one Memory Segment class to another" << endl;
state = x.getState();
addr = x.getAddr();
size = x.getSize();
}
//-------------------------------------------------------------------------
// Function: ~Memory
// Description: This is the destructor. It begins by detaching from the
// memory, then, if this object created the memory, it will
// release the memory.
// Pre: None
// Post: None
// Input: None
// InOut: None
// Return: None
// Global: None
//-------------------------------------------------------------------------
Memory::~Memory(void)
{
int i;
pid_t pid;
// destroy semaphore
#ifdef SEM_DEBUG // FIXME: just for debugging semop()
DBG_MSG(DBG_INFO, "About to destroy all shared memory segments.\n");
#endif
// DumpStats();
// detach memory
shmdt((char *)_shmAddr);
#if 0
// remove our entry from the control block list of memory segments
for (i=0; i<CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] == (Memory *)_shmAddr) {
_cntrl_base[i] = 0;
break;
}
// _findaddr[i] = 0;
}
#endif
// We can only safely destroy memory segments and semaphores if we originally created them
if (_memoryOwner == getpid()) {
cerr << "Nuking the semaphores associated with handle " << _semHandle << endl;
semctl(_semHandle, 0, IPC_RMID, 0);
cerr << "Nuking the shared memory segment " << (void *)_shmAddr << " associated with PID " << _memoryOwner << "\t" << (void *)_shmKey << endl;
shmctl(_sharedMemFd, IPC_RMID, NULL);
}
_memoryOwner = 0;
_sharedMemFd = 0;
_shmAddr = 0;
_semHandle = 0;
_shmKey = 0;
}
//-------------------------------------------------------------------------
// Function: acquireMemory
// Description: This method will acquire shared memory and provide access.
// The memory will be zeroed out after acquisition. If the
// memory has been acquired, an error will be printed, but
// it will be treated as if just acquired. This method will
// initialize the memory management pointers and set the
// ownership flag
// Pre: None
// Post: Shared memory initialized and ready to be attached to
// Input: Key - Is A Key Identifying The Shared Memory To Access
// Size - Size In Bytes
// Inout: None
// Return: Boolean Return...True Indicates Success, False Is A Failure
// Errno Would Have The Failure Reason
//-------------------------------------------------------------------------
void *
Memory::acquireMemory(key_t key, int size)
{
#if 1
acquireMemory(key, size, (void *)0);
#else
acquireMemory(key, size, (void *)0x40f03000); // HACK ALERT!!!!!!!
#endif
}
void *
Memory::acquireMemory(key_t key, int size, void *addr)
{
int i;
shmid_ds shmInfo;
const int shmflg = 0660 | IPC_CREAT | IPC_EXCL;
//#ifdef MEM_DEBUG
DBG_MSG(DBG_INFO, "About to acquire a shared memory segment for key 0x%x of size %d.\n", key, size);
//#endif
// Add some space for the control structures in memory
size += LISTMEM_SIZE + sizeof(Memory);
// Get enough memory to hold the Memory class stashed at the bottom of memory, and the
// control block area.
_sharedMemFd = shmget(key, size, shmflg); // gets shared memory
DBG_MSG(DBG_INFO, "shmget(0x%x, %d, %d) %d\n", key, size, shmflg, _sharedMemFd);
if (_sharedMemFd < 0) { // Error creating shared memory segment.
// Print the shared memory failure (from the errno string).
DBG_MSG(DBG_EROR, "Shmget failed with (%d) %s\n", errno, strerror(errno));
if (errno == EEXIST) {
// Shared memory segment exists, so check if another
// process is using this segment.
// Get the shared memory id for this segment. Solaris doesn't seem to like
// the permissions
_sharedMemFd = shmget(key, size, 0);
DBG_MSG(DBG_INFO, "shmget(0x%x, %d, 0) %d\n", key, size, shmflg, _sharedMemFd);
}
DBG_MSG(DBG_INFO, "SHM_ATTACH: %d\n", _sharedMemFd);
// Get info on this segment.
if (shmctl(_sharedMemFd, IPC_STAT, &shmInfo) == 0) {
// Check if there are any processes attached to this segment.
if (shmInfo.shm_nattch == 0) {
// No processes attached, so attempt to remove existing
// segment, and allocate a new one.
shmctl(_sharedMemFd, IPC_RMID, &shmInfo);
_sharedMemFd = shmget(key, size, shmflg);
if (_sharedMemFd < 0) {
// Could not remove the segment, so connect to the
// existing segment.
_sharedMemFd = shmget(key, size, 0660);
cerr << "FIXME: " << __PRETTY_FUNCTION__ << endl;
}
} else { // Try to attach to an existing segment
if (shmInfo.shm_cpid == getpid()) {
DBG_MSG(DBG_INFO, "Attached by ourself, pid is %d.\n", shmInfo.shm_cpid);
// See if we already have this segment
for (i=0; i<CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0) {
if (_cntrl_base[i]->getShmKey() == key) {
cerr << "Segment " << (void *)_cntrl_base[i]->getMemAddr() << " already in control block" << endl;
_shmAddr = _cntrl_base[i]->getMemAddr();
membase = (Memory *)_shmAddr;
return _shmAddr;
}
}
}
shmctl(_sharedMemFd, IPC_RMID, &shmInfo);
} else {
DBG_MSG(DBG_INFO, "Attached by other process, pid is %d\n", shmInfo.shm_cpid);
// _sharedMemFd = shmget(key, size, 0666);
if (_sharedMemFd < 0) {
// Could not remove the segment, so connect to the
// existing segment.
_sharedMemFd = shmget(key, size, 0660);
cerr << "FIXME: " << __PRETTY_FUNCTION__ << endl;
}
}
}
}
}
#if 0
} else {
DBG_MSG(DBG_HALT,
"Shared memory segment %d is in use.\n"
"Segment created by process: %ld\n"
"Segment last attached by process: %ld\n\n"
"Is there another RA or PFS running on this machine?\n",
_sharedMemFd, shmInfo.shm_cpid, shmInfo.shm_lpid);
}
} else {
DBG_MSG(DBG_HALT,
"Cannot get status info on the shared memory segment.\n");
}
} else {
DBG_MSG(DBG_HALT,
"Unknown error trying to create shared memory segment.\n");
}
// Get info on this segment.
shmctl(_sharedMemFd, IPC_STAT, &shmInfo);
// Check if there are any processes attached to this segment.
cerr << "# of attached processes " << shmInfo.shm_nattch << endl;
#endif
_shmKey = key;
_shmSize = size;
_shmAlloced = _shmAlloced + LISTMEM_SIZE;
// _heap = _shmAddr + _shmAlloced + sizeof(ShmControlList_t) + sizeof(long);
if ((_shmAddr = (char *)shmat(_sharedMemFd, addr, 0660|SHM_SHARE_MMU)) == (char *)-1) {
cerr << "SHMAT error: " << strerror(errno) << endl;
return (void *)0;
}
DBG_MSG(DBG_INFO, "\t: Shared Memory address is 0x%x for key 0x%x\n", _shmAddr, key);
// The first time this gets called by initMem(), this global variable isn't set yet
if (_cntrl_base) {
#if 0
// We are already attached, so just return the pointer.
if (shmInfo.shm_nattch >= 2) {
membase = (Memory *)_shmAddr;
cerr << "\tMembase set to " << hex << (void *)membase << dec << endl;
}
#endif
if (!membase) {
int pad = sizeof(long); // padding for one word to hold a pointer
_memoryOwner = getpid();
// FIXME: don't stomp on the data if we're already allocated
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] != 0) {
// FIXME: attach everything!
// _shmAddr = (char *)shmat(_sharedMemFd, _cntrl_base[i], 0);
if (_cntrl_base[i] == (Memory *)_shmAddr) {
cerr << "Shared Memory Segment already exists!" << endl;
membase = _cntrl_base[i];
_memoryOwner = membase->getMemOwner();
rawmem = membase->getRawMem();
_heap = membase->getHeapAddr();
return _cntrl_base[i];
}
}
}
// Instantiate a Memory object with the base address set to the top of this
// memory segment.
membase = new(_shmAddr) Memory;
memset(membase, 0, size);
cerr << "Memory base for this segment " << (void *)membase << ", key is " << (void *)key<< endl;
// Setup the new Memory class to have the same values we do
membase->setMemFd(_sharedMemFd);
membase->setSemHandle(_semHandle);
membase->setSemKey(_semKey);
membase->setShmKey(_shmKey);
membase->setMemAddr(_shmAddr);
membase->setMemOwner(_memoryOwner);
membase->setMemSize(size);
// After the Memory class, we reserve a block of memory for the std::list
// itself to allocate from. This pointer starts right after rawmem.
membase->setHeapAddr(_shmAddr + _shmAlloced + sizeof(ShmControlList_t) + pad);
membase->setMemAlloced(_shmAlloced + LISTMEM_SIZE);
// Membase should point to ourself
membase->setMemBase(membase);
// Add this segment to the master control list
for (i=0; i < CNTRL_BLK_SIZE; i++) {
if (_cntrl_base[i] == 0) {
_cntrl_base[i] = (Memory *)_shmAddr;
break;
}
}
// Instantiate a pointer to the memory block list. We stick this at the address
// right after the Memory class header for this segment
if (!rawmem) {
rawmem = new (_shmAddr + pad + _shmAlloced + LISTMEM_SIZE) ShmControlList_t;
// rawmem = new (membase->getHeapAddr() + LISTMEM_SIZE) ShmControlList_t;
#ifdef MEM_DEBUG
cerr << "\tRawmem instantiated at " << hex << (void *)rawmem << dec << endl;
cerr << "\t_heap instantiated at " << hex << (void *)membase->getHeapAddr() << dec << endl;
#endif
membase->setListAddr(rawmem);
}
}
}
// rawmem = new ((void *)((char *)_shmAddr+sizeof(Memory))) list<MemSegment, ShmMem<MemSegment> >;
// Since we have two queues, we split the shared memory between them evenly.
// There for, the low memory limit is 1 half, of the queue's memory space,
// hence 1/4 of the entire amount of memory. We run out of memory, when we
// use up one half of the whole shared memory segment.
_lowMemLevel = size/4;
_memOutLevel = size/2;
return _shmAddr;
}
#if 0
void
Memory::getLimits()
{
struct rlimit rlp;
// Get the Data segment size
getrlimit(RLIMIT_DATA, &rlp);
if (rlp.rlim_max == -1) {
DBG_MSG(DBG_INFO, "Max Data Segment size is unlimited\n");
} else {
DBG_MSG(DBG_INFO, "Max Data Segment size is %d (0x%x)\n", rlp.rlim_max,
rlp.rlim_max);
}
DBG_MSG(DBG_INFO, "Current Data Segment size is %d (0x%x)\n", rlp.rlim_cur,
rlp.rlim_cur);
// Get the Stack size
getrlimit(RLIMIT_STACK, &rlp);
if (rlp.rlim_max == 0xffffffff) {
DBG_MSG(DBG_INFO, "Max Stack size is unlimited\n");
} else {
DBG_MSG(DBG_INFO, "Max Stack size is %d (0x%x)\n", rlp.rlim_max,
rlp.rlim_max);
}
DBG_MSG(DBG_INFO, "Current Stack size is %d (0x%x)\n", rlp.rlim_cur,
rlp.rlim_cur);
}
#endif
//-------------------------------------------------------------------------
// Function: attachMemory
// Description: This method will attach to an acquired shared memory
// location
// Pre: Shared memory acquired (_sharedMemFd set)
// Post: Shared memory attached and ready to be allocated
// Input: None
// Inout: None
// Return: Boolean Return...True Indicates Success, False Is A Failure
//-------------------------------------------------------------------------
bool
Memory::attachMemory(void)
{
void *addr;
#ifdef MEM_DEBUG
DBG_MSG(DBG_INFO, "About to attach to an acquired shared memory segment.\n");
#endif
_sharedMemFd = shmget(_shmKey, _shmSize, 0); // sets the file descriptor
if (_sharedMemFd == -1) {
DBG_MSG(DBG_WARN, "SHM_ERR\n"); // errno will be decoded for reason
return false;
}
//Memory not acquired ???
if (_sharedMemFd < 0) {
DBG_MSG(DBG_WARN, "SHM_NFD\n");
return false; // and return
}
_shmAddr = (char *)shmat(_sharedMemFd, 0, 0);
if ((long)_shmAddr == -1) { // Don't check for just negative
DBG_MSG(DBG_WARN, "SHM_ERR\n"); // Error logged and reason is in
return false; // errno
} else {
// Since we have two queues, we split the shared memory between them evenly.
// There for, the low memory limit is 1 half, of the queue's memory space,
// hence 1/4 of the entire amount of memory. We run out of memory, when we
// use up one half of the whole shared memory segment.
_lowMemLevel = _shmSize/4;
_memOutLevel = _shmSize/2;
return true;
}
}
//-------------------------------------------------------------------------
// Function: getMemory
// Description: The heart of this class, this method will return a pointer
// to a block of memory meeting the size request. If the Null
// pointer is returned, there was not sufficient memory to
// meet the request. The logic of the method is that it walks
// the size queue until a block of memory meets its size
// requirements. The left over memory is returned to the free
// pool and a pointer to the requested memory is returned to
// the caller.
// Pre: An attachment to the memory must have occurred
// _baseAddress set
// Post: None
// Input: size in bytes
// InOut: None
// Return: A pointer to the allocated block.
//-------------------------------------------------------------------------
void *
Memory::getMemory(int size)
{
ShmControlList_t::iterator pos2;
MemSegment ms, sm, tmp;
void *p;
// Round size up to wordsize, since most microprocessors are word based these days
if (size % sizeof(long) != 0)
{
size += sizeof(long)- size%sizeof(long);
}
#ifdef MEM_DEBUG
cerr << __PRETTY_FUNCTION__ << ": Getting " << size << " bytes " << endl;
#endif
#ifdef MEM_STATS
stats.bytes_allocated += size;
#endif
Lock(); // get exclusive control of this memory segment
if (rawmem->size() > 0) {
// Loop through all the memory blocks, looking for a freed up one
// of a useable size.
#ifdef MEM_DEBUG
cerr << "Checking for a previously freed memory block: ";
#endif
for (pos2 = rawmem->begin(); pos2 != rawmem->end(); pos2++) {
// cerr << pos2->getSize() << "\t" << pos2->getState() << endl;
if ((pos2->getSize() >= size) && (pos2->getState() == FREE)) {
// Mark this segment as allocated memory
pos2->setState(ALLOCATED);
#ifdef MEM_STATS
pos2->incrUsed();
#endif
p = pos2->getAddr();
#ifdef MEM_DEBUG
cerr << "\tfound at " << p << endl;
#endif
Unlock(); // relinquish exclusive control of this memory segment
#ifdef MEM_STATS
stats.freed_hits++;
stats.freed_bytes += size;
#endif
return p;
}
#ifdef MEM_DEBUG
// cout << ".";
#endif
}
#ifdef MEM_DEBUG
cerr << "\tNo FREED blocks found" << endl;
#endif
}
// We got here, cause we don't have a freed memory block
// cerr << "Instantiating a memory block" << endl;
#if 0
// FIXME: if we leave this check in here, the pause casues corruption of
// the queue later on.
// make sure we have memory left. Unfortunately, getMemory() can't fail,
// unless somebody fixes TFAS to do error trapping. So we just wait for
// a while, and hope like mad the other process frees up some memory.
if ((_shmAlloced + size) >= (_shmSize * 0.80)) {
DBG_MSG(DBG_EROR, "FIXME: Not enough memory left!!!!\n");
sleep(10); // FIXME: wait for the other process
// return 0;
}
#endif
if ((_shmAlloced + size) >= _shmSize) {
DBG_MSG(DBG_HALT, "FIXME: No memory left!!!!\n");
}
p = _shmAddr + _shmAlloced;
_shmAlloced += size + 8;
ms.setAddr(p);
ms.setSize(size);
ms.setState(ALLOCATED);
#ifdef MEM_STATS
ms.incrUsed();
#endif
rawmem->push_back(ms);
Unlock(); // relinquish exclusive control of this memory segment
#ifdef MEM_STATS
stats.heap_bytes += size;
stats.heap_hits ++;
#endif
#ifdef MEM_DEBUG
cerr << "Instantiated a memory block of " << size << " bytes at address " << (void *)p << endl;
#endif
return p;
#if 0
// If we didn't allocate memory from an exisiting FREED block, then
// allocate some from the shared memory heap.
if (_shmAlloced+size > _shmSize)
{
DBG_MSG(DBG_WARN, "Not enough memory left!");
} else {
DBG_MSG(DBG_INFO, "Allocating memory...");
// MemSegment ms((char *)_shmAddr+_shmAlloced, size);
_shmAlloced += size; // update the total of allocated bytes
// rawmem.push_back(ms);
}
return (void *)_shmAddr+_shmAlloced-size);
#endif
}
//-------------------------------------------------------------------------
// Function: getMemory
// Description: This method is the second getMemory method. The difference
// between this one and the one above, is that this one also
// has a fill character. The previous version is called, and
// if successfull, the memory is filled with the specified
// character.
// Pre: An attachment to the memory must have occurred
// _baseAddress set
// Post: None
// Input: size in bytes
// fillChar the file character
// InOut: None
// Return: A pointer to the allocated block.
//-------------------------------------------------------------------------
void *
Memory::getMemory(int size, char fillChar)
{
void *address;
address = getMemory(size);
if (address)
memset(address, fillChar, size);
return address;
}
//-------------------------------------------------------------------------
// Function: releaseMemory
// Description: This method returns allocated memory. This is done by
// accessing the memories header, linking it into the address
// queue, merging it with adjacent memory if possible and
// linking it in the size queue. The shared memory is locked
// from before linking it into the address queue until after
// it is linked in the size queue.
// Pre: None
// Post: None
// Input: trueAddress address of beginning of allocated memory
// InOut: None
// Return: None
//-------------------------------------------------------------------------
void
Memory::releaseMemory(void *trueAddress)
{
int address;
ShmControlList_t::iterator pos;
Lock(); // get exclusive control of this memory segment
// Loop through all the memory segments for the one that points to
// the segment we want to release.
for (pos = rawmem->begin(); pos != rawmem->end(); pos++) {
if(pos->getAddr() == trueAddress) {
// Mark this segment as free memory
pos->setState(FREE);
#ifdef MEM_DEBUG
DBG_MSG(DBG_INFO, "Releasing %d bytes of memory at 0x%x\n", pos->getSize(), trueAddress);
#endif
}
}
Unlock(); // relinquish exclusive control of this memory segment
return;
}
//-------------------------------------------------------------------------
// Function: compactMemory
// Description: This method checks to see if the memory we are freeing
// touches either the previous or next data spaces. If they
// do touch, the touched item(s) are merged in with this item,
// removed from the size queue, but linked into the address
// queue. It is possible to link with one or both of the
// adjacent areas. What this routine does is to cut back on
// fragmentation of memory and ensures that the largest blocks
// possible are available.
// Pre: None
// Post: None
// Input: address address of beginning of the header of the
// allocated memory we are freeing
// InOut: None
// Return: None
//-------------------------------------------------------------------------
#ifndef NEW_QUE
long
Memory::compactMemory(long address)
{
#ifdef MEM_DEBUG
DBG_MSG(DBG_INFO,
"About to compact memory at 0x%x in the shared memory segment.\n",
address);
#endif
}
#endif
//-------------------------------------------------------------------------
// Function: initializeSemaphore
// Description: This method initializes a semaphore, theLock, which resides
// in a pre-initialized shared memory. theLock is used to
// determine which process has the accessibility to the shared
// memory at the instance.
// Pre: None
// Post: Semaphore initialized and ready to be opened
// Input: None
// InOut: None
// Return: None
//-------------------------------------------------------------------------
SemHandle_t
Memory::initializeSemaphore(int semKey)
{
union semun semopts;
#ifdef SEM_DEBUG // FIXME: just for debugging semop()
DBG_MSG(DBG_INFO, "About to get %d semaphores for semLock (key 0x%x).\n", NUM_OF_SEMS, semKey);
#endif
_semKey = semKey;
if ((_semHandle = semget((key_t)semKey, NUM_OF_SEMS, IPC_CREAT | 0666 | IPC_EXCL)) < 0) {
if (errno == EEXIST) {
if ((_semHandle = semget(semKey, NUM_OF_SEMS, 0666)) < 0) {
}
}
}
#ifdef SEM_DEBUG // FIXME: just for debugging semop()
DBG_MSG(DBG_INFO, "Handle for the shared memory semaphore is %d.\n", _semHandle);
cerr << "# of currently attached semaphores for key " << semKey << " is "
<< semctl(_semHandle, 0, GETVAL, semopts) << endl;
#endif
if (_semHandle == -1) {
_semHandle = 0;
cerr << strerror(errno) << "\t" << errno << endl;
#if 0
switch (errno) {
case EACCES:
DBG_MSG(DBG_WARN, "user doesn't have access permission.\n");
break;
#if 0
case EEXISTS:
DBG_MSG(DBG_WARN, "semaphore set already exists.\n");
break;
#endif
case EIDRM:
DBG_MSG(DBG_WARN, "semaphore set is marked to be deleted.\n");
break;
case ENOENT:
DBG_MSG(DBG_WARN, "no semaphore set exists, set IPC_CREAT.\n");
break;
case ENOMEM:
DBG_MSG(DBG_WARN, "not enough memory\n");
break;
case ENOSPC:
DBG_MSG(DBG_WARN, "too many semaphores open.\n");
break;
case ENOSYS:
DBG_MSG(DBG_WARN, "sem_init() not supported\n");
break;
default:
DBG_MSG(DBG_WARN, "failed to initialize semaphore in CustQueLock\n");
break;
}
exit(1); // This is fatal.
#endif
}
return _semHandle;
}
bool
Memory::isSharedMemoryLow(void)
{
// if (_shmAlloced >= (_shmSize - (_shmSize * LOW_MEM_RATIO))) {
if (_shmAlloced >= (_shmSize * 0.8)) {
DBG_MSG(DBG_WARN, "FIXME: \n\t%d bytes allocated, %d bytes left.\n", _shmAlloced, _shmSize - _shmAlloced);
return true;
} else {
return false;
}
}
bool
Memory::isSharedMemoryOut(void)
{
if (_shmAlloced > _shmSize)
return true;
else
return false;
}
//-------------------------------------------------------------------------
// Function: Lock
// Description: This method returns when the lock is acquired. If the lock
// is not available, a wait is performed and then it is tried
// again.
// Pre: None
// Post: Lock is set to 1
// Input: None
// InOut: theLock set to 1 when done
// Return: None
// Global: None
//-------------------------------------------------------------------------
void
Memory::Lock(SemHandle_t theHandle)
{
int ret;
static struct timeval tval;
tval.tv_sec = 0L;
tval.tv_usec = 10000; /* sleep for 10000 microseconds */
if (theHandle < 1)
{
DBG_MSG(DBG_EROR, "%s: semaphore handle bogus! %d.\n", __FUNCTION__, theHandle);
return;
}
#ifdef SEM_DEBUG // FIXME: just for debugging semop()
DBG_MSG(DBG_INFO, "About to lock the semaphore for handle %d\n", theHandle);
#endif
// Until Sun fixes its sem_wait() use sem_trywait() instead
#ifdef HAVE_SEM_INIT
// Both sem_trywait() and semop() set errno to EAGAIN if the semaphore
// is zero, otherwise they decrement the semaphore.2
while(sem_trywait(theHandle) < 0)
#else
struct sembuf sem_trylock = {0, 1, IPC_NOWAIT};
while ((ret = semop (theHandle, &sem_trylock, 1)) < 0)
#endif
{
#ifndef HAVE_SEM_INIT
DBG_MSG(DBG_INFO, "%s: semop() returned %d, errno(%d) is %s\n",
__FUNCTION__, ret, errno, strerror(errno));
#endif
select(0, NULL, NULL, NULL, &tval);
}
#ifdef SEM_DEBUG
#ifndef HAVE_SEM_INIT
DBG_MSG(DBG_INFO, "\tLocked the semaphore for handle %d\n", theHandle);
#endif
#endif
return;
}
//-------------------------------------------------------------------------
// Function: Unlock
// Description: This method releases the lock
// Pre: Lock is set to 1 (probably)
// Post: Lock is set to 0 (probably)
// Input: None
// InOut: theHandle set to 0 when done
// Return: None
// Global: None
//-------------------------------------------------------------------------
void
Memory::Unlock(SemHandle_t theHandle)
{
int ret;
if (theHandle < 1)
{
DBG_MSG(DBG_EROR, "%s: semaphore handle is bogus! %d.\n", __FUNCTION__, theHandle);
return;
}
#ifdef SEM_DEBUG // FIXME: just for debugging semop()
#ifndef HAVE_SEM_INIT
DBG_MSG(DBG_INFO, "About to unlock the semaphore for handle %d\n", theHandle);
#endif
#endif
#ifdef HAVE_SEM_INIT
sem_post(&theHandle);
#else
struct sembuf sem_lock = {0, -1, 0};
if ((ret = semop(theHandle, &sem_lock, 1)) < 0)
DBG_MSG(DBG_INFO, "%s: semop() returned %d, errno(%d) is %s\n",
__FUNCTION__, ret, errno, strerror(errno));
#endif
#ifdef SEM_DEBUG
#ifndef HAVE_SEM_INIT
DBG_MSG(DBG_INFO, "\tUnlocked the semaphore for handle %d\n", theHandle);
#endif
#endif
return;
}
More information about the Libstdc++
mailing list