Added all Eternity and PrBoom zone heap fixes (caching optimization, fragmentation optimization, savegame heap corruption, INSTRUMENTED C-heap corruption), and added th_delete thinker class to prevent infinite loops in the AI code.

This commit is contained in:
James Haley 2008-06-17 19:27:45 -05:00
parent 26f762b64e
commit 5d5f2a10f2
3 changed files with 301 additions and 235 deletions

View File

@ -66,30 +66,40 @@ void P_InitThinkers(void)
thinkercap.prev = thinkercap.next = &thinkercap;
}
//
// P_UpdateThinker
//
// killough 8/29/98:
//
// We maintain separate threads of friends and enemies, to permit more
// efficient searches.
//
// haleyjd 6/17/08: Import from EE: PrBoom bugfixes, including addition of the
// th_delete thinker class to rectify problems with infinite loops in the AI
// code due to corruption of the th_enemies/th_friends lists when monsters get
// removed at an inopportune moment.
//
void P_UpdateThinker(thinker_t *thinker)
{
register thinker_t *th;
// find the class the thinker belongs to
int class = thinker->function == P_MobjThinker &&
// haleyjd 07/12/03: don't use "class" as a variable name
int tclass = thinker->function == P_RemoveThinkerDelayed ? th_delete :
thinker->function == P_MobjThinker &&
((mobj_t *) thinker)->health > 0 &&
(((mobj_t *) thinker)->flags & MF_COUNTKILL ||
((mobj_t *) thinker)->type == MT_SKULL) ?
((mobj_t *) thinker)->flags & MF_FRIEND ?
th_friends : th_enemies : th_misc;
// Remove from current thread
thinker_t *th = thinker->cnext;
// Remove from current thread, if in one -- haleyjd: from PrBoom
if((th = thinker->cnext) != NULL)
(th->cprev = thinker->cprev)->cnext = th;
// Add to appropriate thread
th = &thinkerclasscap[class];
th = &thinkerclasscap[tclass];
th->cprev->cnext = thinker;
thinker->cnext = th;
thinker->cprev = th->cprev;
@ -133,13 +143,16 @@ static thinker_t *currentthinker;
// remove it, and set currentthinker to one node preceeding it, so
// that the next step in P_RunThinkers() will get its successor.
//
void P_RemoveThinkerDelayed(thinker_t *thinker)
{
if(!thinker->references)
{
thinker_t *next = thinker->next;
(next->prev = currentthinker = thinker->prev)->next = next;
// haleyjd 6/17/08: remove from threaded list now
(thinker->cnext->cprev = thinker->cprev)->cnext = thinker->cnext;
Z_Free(thinker);
}
}
@ -156,13 +169,24 @@ void P_RemoveThinkerDelayed(thinker_t *thinker)
// set the function to P_RemoveThinkerDelayed(), so that later, it will be
// removed automatically as part of the thinker process.
//
void P_RemoveThinker(thinker_t *thinker)
{
thinker->function = P_RemoveThinkerDelayed;
// killough 8/29/98: remove immediately from threaded list
(thinker->cnext->cprev = thinker->cprev)->cnext = thinker->cnext;
// haleyjd 06/17/08: Import from EE:
// NO! Doing this here was always suspect to me, and
// sure enough: if a monster's removed at the wrong time, it gets put
// back into the list improperly and starts causing an infinite loop in
// the AI code. We'll follow PrBoom's lead and create a th_delete class
// for thinkers awaiting deferred removal.
// Old code:
//(thinker->cnext->cprev = thinker->cprev)->cnext = thinker->cnext;
// Move to th_delete class.
P_UpdateThinker(thinker);
}
//

View File

@ -46,6 +46,7 @@ void P_SetTarget(mobj_t **mo, mobj_t *target); // killough 11/98
// killough 8/29/98: threads of thinkers, for more efficient searches
typedef enum {
th_delete, // haleyjd 11/09/06: giant bug fix
th_misc,
th_friends,
th_enemies,

View File

@ -38,6 +38,7 @@ static const char rcsid[] = "$Id: z_zone.c,v 1.13 1998/05/12 06:11:55 killough E
#include "z_zone.h"
#include "doomstat.h"
#include "m_argv.h"
#ifdef DJGPP
#include <dpmi.h>
@ -299,13 +300,25 @@ void *(Z_Malloc)(size_t size, int tag, void **user, const char *file, int line)
start = block;
// haleyjd 06/17/08: import from EE:
// the first if() inside the loop below contains cph's memory
// purging efficiency fix
do
{
if (block->tag >= PU_PURGELEVEL) // Free purgable blocks
{ // replacement is roughly FIFO
// Free purgable blocks; replacement is roughly FIFO
if(block->tag >= PU_PURGELEVEL)
{
start = block->prev;
Z_Free((char *) block + HEADER_SIZE);
block = start = start->next; // Important: resets start
/* cph - If start->next == block, we did not merge with the previous
* If !=, we did, so we continue from start.
* Important: we've reset start!
*/
if(start->next == block)
start = start->next;
else
block = start;
}
if(block->tag == PU_FREE && block->size >= size) // First-fit
@ -313,8 +326,8 @@ void *(Z_Malloc)(size_t size, int tag, void **user, const char *file, int line)
size_t extra = block->size - size;
if(extra >= MIN_BLOCK_SPLIT + HEADER_SIZE)
{
memblock_t *newb = (memblock_t *)((char *) block +
HEADER_SIZE + size);
memblock_t *newb =
(memblock_t *)((char *) block + HEADER_SIZE + size);
(newb->next = block->next)->prev = newb;
(newb->prev = block)->next = newb; // Split up block
@ -385,10 +398,19 @@ allocated:
block->prev = (memblock_t *) &blockbytag[tag];
block->vm = 1;
// haleyjd: cph's virtual memory error fix
#ifdef INSTRUMENTED
virtual_memory += block->size = size + HEADER_SIZE;
#endif
virtual_memory += size + HEADER_SIZE;
// haleyjd 06/17/08: Import from EE:
// Big problem: extra wasn't being initialized for vm
// blocks. This caused the memset used to randomize freed memory when
// INSTRUMENTED is defined to stomp all over the C heap.
block->extra = 0;
#endif
/* cph - the next line was lost in the #ifdef above, and also added an
* extra HEADER_SIZE to block->size, which was incorrect */
block->size = size;
goto allocated;
}
@ -500,19 +522,33 @@ void (Z_FreeTags)(int lowtag, int hightag, const char *file, int line)
if(lowtag <= PU_FREE)
lowtag = PU_FREE+1;
// haleyjd: code inside this do loop has been updated with
// cph's fix for memory wastage
do // Scan through list, searching for tags in range
{
if(block->tag >= lowtag && block->tag <= hightag)
{
memblock_t *prev = block->prev;
memblock_t *prev = block->prev, *cur = block;;
(Z_Free)((char *) block + HEADER_SIZE, file, line);
block = prev->next;
/* cph - be more careful here, we were skipping blocks!
* If the current block was not merged with the previous,
* cur is still a valid pointer, prev->next == cur, and cur is
* already free so skip to the next.
* If the current block was merged with the previous,
* the next block to analyse is prev->next.
* Note that the while() below does the actual step forward
*/
block = (prev->next == cur) ? cur : prev;
}
}
while((block = block->next) != zone);
if(hightag > PU_CACHE)
hightag = PU_CACHE;
for (;lowtag <= hightag; lowtag++)
for(; lowtag <= hightag; ++lowtag)
{
for(block = blockbytag[lowtag], blockbytag[lowtag] = NULL; block;)
{
memblock_t *next = block->next;
@ -545,6 +581,7 @@ void (Z_FreeTags)(int lowtag, int hightag, const char *file, int line)
block = next; // Advance to next block
}
}
}
void (Z_ChangeTag)(void *ptr, int tag, const char *file, int line)
{
@ -617,6 +654,7 @@ void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user,
if(ptr)
{
memblock_t *block = (memblock_t *)((char *)ptr - HEADER_SIZE);
if(p) // haleyjd 06/17/08: allow to return NULL without crashing
memcpy(p, ptr, n <= block->size ? n : block->size);
(Z_Free)(ptr, file, line);
if(user) // in case Z_Free nullified same user
@ -628,8 +666,7 @@ void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user,
void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user,
const char *file, int line)
{
return
(n1*=n2) ? memset((Z_Malloc)(n1, tag, user, file, line), 0, n1) : NULL;
return (n1*=n2) ? memset((Z_Malloc)(n1, tag, user, file, line), 0, n1) : NULL;
}
char *(Z_Strdup)(const char *s, int tag, void **user,
@ -642,9 +679,11 @@ void (Z_CheckHeap)(const char *file, int line)
{
memblock_t *block = zone; // Start at base of zone mem
do // Consistency check (last node treated special)
{
if((block->next != zone &&
(memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next)
|| block->next->prev != block || block->prev->next != block)
(memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next) ||
block->next->prev != block || block->prev->next != block)
{
I_Error("Z_CheckHeap: Block size does not touch the next block\n"
"Source: %s:%d"
#ifdef INSTRUMENTED
@ -654,6 +693,8 @@ void (Z_CheckHeap)(const char *file, int line)
, file, line
#endif
);
}
}
while((block = block->next) != zone);
}