/* imemory.c */
/*
* Vis5D system for visualizing five dimensional gridded data sets.
* Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul,
* Dave Santek, and Andre Battaiola.
*
* 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.
*
* As a special exception to the terms of the GNU General Public
* License, you are permitted to link Vis5D with (and distribute the
* resulting source and executables) the LUI library (copyright by
* Stellar Computer Inc. and licensed for distribution with Vis5D),
* the McIDAS library, and/or the NetCDF library, where those
* libraries are governed by the terms of their own licenses.
*
* 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 "../config.h"
#include <assert.h>
#ifdef stellar
# include <malloc.h>
# include <memory.h>
#else
# include <stdlib.h>
# include <string.h>
#endif
#include <stdio.h>
#include "globals.h"
#include "imemory.h"
#include "misc.h"
#include "sync.h"
/* must be consistent with imemory.h */
#define NULL_TYPE 0
struct mem {
int size;
struct mem *prev;
struct mem *next;
short int free, magic;
#ifdef DEBUG_MEM
int type;
#endif
};
#define MEMSIZ sizeof(struct mem)
#define MAGIC 0x1234
static void i_check_memory( Irregular_Context itx );
/********************************************/
/**** Private functions ***/
/********************************************/
/*
* Allocate a block of memory.
* Input: b - number of bytes to allocate
* permanent - if non-zero do a permanent allocation
* type - type of block (see list in memory.h)
* Return: address of block or NULL if unable to make allocation
*/
static void *i_alloc( Irregular_Context itx, int b, int permanent, int type )
{
int bytes;
struct mem *pos, *new;
#ifdef DEBUG_MEM
printf("Allocate( %d )\n", b);
#endif
/* round up bytes to multiple of sizeof(struct mem) */
if (b<MEMSIZ) {
bytes = MEMSIZ;
}
else {
bytes = ( (b+MEMSIZ-1) / MEMSIZ ) * MEMSIZ;
}
/*
* If we want to make a permanent allocation, try to do it at tail
* of memory list.
*/
if (permanent) {
if (itx->tail->size >= bytes) {
#ifdef DEBUG_MEM
printf("permanent allocation of %d bytes. old tail->size=%d",
bytes, itx->tail->size );
#endif
itx->tail->size -= bytes;
itx->memory_used += bytes;
#ifdef DEBUG_MEM
printf(". new tail->size=%d\n", itx->tail->size );
#endif
return (char *) itx->tail + MEMSIZ + itx->tail->size;
}
/* couldn't allocate from tail; just do a normal allocation */
}
/*
* Find a block of memory large enough to make the allocation from.
*/
pos = NULL;
if (itx->guess) {
/* Try a guess */
#ifdef DEBUG_MEM
assert( itx->guess->magic==MAGIC );
assert( itx->guess->free==1);
#endif
if (itx->guess->free && itx->guess->size >= bytes + MEMSIZ) {
/* a good guess! */
#ifdef DEBUG_MEM
printf("Good guess!\n");
#endif
pos = itx->guess;
itx->guess = NULL;
}
}
if (pos==NULL) {
/* Find first fit */
#ifdef DEBUG_MEM
int tries = 0;
#endif
for (pos=itx->head; pos; pos=pos->next) {
if (pos->free && pos->size == bytes) {
/* found exact fit! */
break;
}
else if (pos->free && pos->size >= bytes + MEMSIZ) {
/* found a block to split */
break;
}
#ifdef DEBUG_MEM
tries++;
#endif
}
#ifdef DEBUG_MEM
printf("%d tries\n", tries);
#endif
}
if (!pos) {
/* couldn't find block large enough, return NULL */
itx->guess = NULL;
return NULL;
}
if (pos->size == bytes) {
/* found a block of exact size! */
pos->free = 0;
itx->memory_used += bytes;
if (itx->guess==pos)
itx->guess = NULL;
#ifdef DEBUG_MEM
pos->type = type;
printf("exact fit 0x%x 0x%x\n", (int)pos, (int)(pos+1));
i_check_memory( itx );
#endif
return (void *) (pos+1);
}
else {
/*
* 'pos' points to a block of sufficient size. Split it into two
* pieces, and return the address of the first part
*/
new = (struct mem *) ( (char *) pos + MEMSIZ + bytes );
/* init new fragment node */
new->size = pos->size - bytes - MEMSIZ;
new->prev = pos;
new->next = pos->next;
new->free = 1;
new->magic = MAGIC;
/* tail pointer */
if (pos->next)
pos->next->prev = new;
else
itx->tail = new;
/* links to pos */
pos->next = new;
pos->size = bytes;
pos->free = 0;
itx->memory_used += bytes + MEMSIZ;
/* reset guess */
if (!itx->guess) {
itx->guess = new;
}
#ifdef DEBUG_MEM
pos->type = type;
printf("big fit 0x%x 0x%x\n", (int)pos, (int)(pos+1));
i_check_memory( itx );
#endif
return (void *) (pos+1);
}
}
/*
* Deallocate a block of memory.
* Input: addr - the address of the block to deallocate
* b - number of bytes in the block or -1 if unknown
*/
static void i_dealloc( Irregular_Context itx, void *addr, int b )
{
int bytes;
struct mem *pos, *pred, *succ;
#ifdef DEBUG_MEM
printf("Deallocate( 0x%x, %d )\n", (int)addr, b );
#endif
if (addr==NULL) {
printf("Warning: deallocate(NULL)\n");
return;
}
pos = (struct mem *) ( (char *) addr - MEMSIZ );
#ifdef DEBUG_MEM
/* Sanity Checks: */
assert( pos->magic==MAGIC );
assert( pos->free==0 );
#endif
if (b<0) {
bytes = pos->size;
}
else if (b<MEMSIZ) {
bytes = MEMSIZ;
}
else {
/* round up bytes to multiple of sizeof(struct mem) */
bytes = ( (b+MEMSIZ-1) / MEMSIZ ) * MEMSIZ;
if (pos->size!=bytes) {
printf("Warning: wrong number of bytes in deallocate() %d vs %d\n",
pos->size, bytes );
}
}
/* mark as free */
pos->free = 1;
itx->memory_used -= bytes;
/* try to merge this block with successor */
if (pos->next && pos->next->free==1) {
#ifdef DEBUG_MEM
printf("Merge with successor\n");
#endif
succ = pos->next;
pos->size += MEMSIZ + succ->size;
pos->next = succ->next;
pos->free = 1;
if (succ->next)
succ->next->prev = pos;
else
itx->tail = pos;
/* update guess if necessary */
if (succ==itx->guess) {
itx->guess = pos;
}
itx->memory_used -= MEMSIZ;
}
/* try to merge this block with predecessor */
if (pos->prev && pos->prev->free==1) {
#ifdef DEBUG_MEM
printf("Merge with predecessor\n");
#endif
pred = pos->prev;
pred->size += MEMSIZ + pos->size;
pred->next = pos->next;
if (pos->next)
pos->next->prev = pred;
else
itx->tail = pred;
/* update guess if necessary */
if (pos==itx->guess) {
itx->guess = pred;
}
/* update pos */
pos = pred;
itx->memory_used -= MEMSIZ;
}
/* reset guess */
itx->guess = pos;
#ifdef DEBUG_MEM
i_check_memory(itx);
#endif
}
/********************************************/
/*** DEBUGGING FUNCTIONS ***/
/********************************************/
static void i_check_memory( Irregular_Context itx )
{
struct mem *pos, *pred;
pred = NULL;
pos = itx->head;
while (pos) {
if (pos->free!=1 && pos->free!=0) {
printf("bad pos->free %d\n", pos->free);
}
if (pos->magic!=MAGIC) {
printf("bad magic number in node 0x%x\n", (int) pos );
}
if (pos->prev != pred) {
printf("bad pred pointer 0x%x should be 0x%x\n",
(int)pos->prev, (int)pred );
}
if (pos->next==NULL && itx->tail!=pos) {
die("bad tail\n");
}
if (pred && pred->free==1 && pos->free==1) {
die("adjacent free blocks");
}
if (pred) {
if ((char *) pred + pred->size + MEMSIZ != (char *) pos) {
die("Bad size");
}
}
pred = pos;
pos = pos->next;
}
assert( itx->tail->free==1 );
if (itx->guess)
assert( itx->guess->free == 1 );
}
static void i_dump_memory( Irregular_Context itx )
{
struct mem *pos;
pos = itx->head;
while (pos) {
printf("node: 0x%x\n", (int)pos );
printf(" size: %d", pos->size );
printf(" prev: 0x%x", (int)pos->prev );
printf(" next: 0x%x", (int)pos->next );
#ifdef DEBUG_MEM
printf(" type: %d", pos->type );
#endif
printf(" free: %d\n", pos->free );
pos = pos->next;
}
printf("tail = 0x%x\n", (int)itx->tail );
printf("memory used: %d\n", itx->memory_used);
}
/**********************************************************************/
/**** PUBLIC FUNCTIONS ****/
/**********************************************************************/
/*
* Initialize the memory management for a context.
* Input: itx - the vis5d context
* bytes - size of the memory pool.
* Return: 1 = success, 0 = error
*/
int init_irregular_memory( Irregular_Context itx, int bytes )
{
struct mem *m;
assert( bytes==0 || bytes>=1024*1024 );
/*printf("init_memory( %d ) itx = %d\n", bytes, itx->context_index);*/
itx->memory_limit = bytes;
if (bytes) {
m = (struct mem *) malloc( bytes );
if (!m) {
printf("Error: unable to allocate %d bytes of memory.\n", bytes);
printf("Either change MBS in vis5d.h or use -mbs option.\n");
return 0;
}
m->size = bytes - sizeof(struct mem);
m->prev = NULL;
m->next = NULL;
m->free = 1;
m->magic = MAGIC;
itx->mempool = m;
itx->head = itx->tail = itx->guess = m;
itx->memory_used = MEMSIZ;
}
else {
itx->mempool = 0;
itx->memory_used = 0;
}
ALLOC_LOCK( itx->memlock );
ALLOC_LOCK( itx->lrulock );
return 1;
}
/*
* Define a shared memory area to use as the memory pool for a context.
* Input: itx - the vis5d context
* bytes - size of the memory pool.
* Return: 1 = success, 0 = error
*/
int i_init_shared_memory( Irregular_Context itx, void *start, int bytes )
{
struct mem *m;
itx->memory_limit = bytes;
m = start;
m->size = bytes - sizeof(struct mem);
m->prev = NULL;
m->next = NULL;
m->free = 1;
m->magic = MAGIC;
itx->mempool = start;
itx->head = itx->tail = m;
itx->memory_used = MEMSIZ;
ALLOC_LOCK( itx->memlock );
ALLOC_LOCK( itx->lrulock );
return 1;
}
/*
* Reinitialize a memory pool so that it is completely unallocated.
*/
int i_reinit_memory( Irregular_Context itx )
{
struct mem *m;
if (itx->memory_limit) {
m = itx->head;
m->size = itx->memory_limit - sizeof(struct mem);
m->prev = NULL;
m->next = NULL;
m->free = 1;
m->magic = MAGIC;
itx->head = itx->tail = m;
itx->memory_used = MEMSIZ;
}
else {
/* How do we free() all the malloc()s ?? - in case
init_memory given bytes = 0 to flag use malloc */
itx->memory_used = 0;
}
return 1;
}
/*
* Return the amount of available memory.
* Input: itx - the vis5d context
*/
int i_mem_available( Irregular_Context itx )
{
if (itx->memory_limit==0)
return 1024*1024*1024; /* a Gig ought to be enough */
else
return itx->memory_limit - itx->memory_used;
}
/*
* Allocate a block of memory. If there is not enough memory to satisfy
* the request, the least recently used graphics will be deallocated.
* Input: itx - the vis5d context
* bytes - how many bytes to allocate
* Return: address of memory block or NULL if out of memory.
*/
void *i_allocate( Irregular_Context itx, int bytes )
{
assert( bytes>=0 );
if (itx->memory_limit==0) {
/* just malloc */
return (void *) malloc( bytes );
}
else {
void *addr;
int ma, d;
do {
LOCK_ON( itx->memlock );
addr = i_alloc( itx, bytes, 0, NULL_TYPE );
LOCK_OFF( itx->memlock );
if (addr) {
/* all done, return */
return addr;
}
/* We didn't find a free block large enough, */
/* try deallocating some graphics */
ma = i_mem_available(itx);
LOCK_ON( itx->lrulock );
if (ma==i_mem_available(itx)) {
d = i_deallocate_lru(itx);
}
LOCK_OFF( itx->lrulock );
} while (d>0);
/* Couldn't deallocate anything, we're REALLY out of memory */
#ifdef DEBUG_MEM
printf("Allocate %d failed\n", bytes );
i_dump_memory(itx);
#endif
return NULL;
}
}
/*
* Allocate a block of memory. If there is not enough memory to satisfy
* the request, the least recently used graphics will be deallocated.
* Input: itx - the vis5d context
* bytes - how many bytes to allocate
* type - type of block (see list in memory.h)
* Return: address of memory block or NULL if out of memory.
*/
void *i_allocate_type( Irregular_Context itx, int bytes, int type )
{
assert( bytes>=0 );
if (itx->memory_limit==0) {
/* just malloc */
return (void *) malloc( bytes );
}
else {
void *addr;
int ma, d;
do {
LOCK_ON( itx->memlock );
addr = i_alloc( itx, bytes, 0, type );
LOCK_OFF( itx->memlock );
if (addr) {
/* all done, return */
return addr;
}
/* We didn't find a free block large enough, */
/* try deallocating some graphics */
ma = i_mem_available(itx);
LOCK_ON( itx->lrulock );
if (ma==i_mem_available(itx)) {
d = i_deallocate_lru(itx);
}
LOCK_OFF( itx->lrulock );
} while (d>0);
/* Couldn't deallocate anything, we're REALLY out of memory */
#ifdef DEBUG_MEM
printf("Allocate %d failed\n", bytes );
i_dump_memory(itx);
#endif
return NULL;
}
}
/*
* Permanent allocate. Same as allocate, above, but used to allocate
* memory which will NEVER be deallocated.
* Input: itx - the vis5d context
* bytes - number of bytes to allocate
*/
void *i_pallocate( Irregular_Context itx, int bytes )
{
if (itx->memory_limit==0) {
/* just malloc */
return (void *) malloc( bytes );
}
else {
void *addr;
int ma, d;
do {
LOCK_ON( itx->memlock );
addr = i_alloc( itx, bytes, 1, NULL_TYPE );
LOCK_OFF( itx->memlock );
if (addr) {
/* all done, return */
return addr;
}
/* We didn't find a free block large enough, */
/* try deallocating some graphics */
ma = i_mem_available(itx);
LOCK_ON( itx->lrulock );
if (ma==i_mem_available(itx)) {
d = i_deallocate_lru(itx);
}
LOCK_OFF( itx->lrulock );
} while (d>0);
/* Couldn't deallocate anything, we're REALLY out of memory */
#ifdef DEBUG_MEM
printf("Allocate %d failed\n", bytes );
i_dump_memory(itx);
#endif
return NULL;
}
}
/*
* Deallocate a block of memory.
* Input: itx - the vis5d context
* addr - address of block (if NULL, nothing happens)
* bytes - size of block (if <= zero, bytes is ignored)
*/
void i_deallocate( Irregular_Context itx, void *addr, int bytes )
{
LOCK_ON( itx->memlock );
if (addr) {
if (itx->memory_limit==0) {
free( addr );
}
else {
i_dealloc( itx, addr, bytes );
}
}
LOCK_OFF( itx->memlock );
}
syntax highlighted by Code2HTML, v. 0.9.1