/* Copyright (c) 2009 The Khronos Group Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and/or associated documentation files (the
* "Materials"), to deal in the Materials without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Materials, and to
* permit persons to whom the Materials are furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Materials.
*
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
#ifdef __cplusplus
extern "C"
{
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include "owfmemory.h"
#include "owfdebug.h"
#define MAGIK 0x05EAF00D
#define FENCE1 0xFACED00D
#define FENCE2 0xFACE50FF
#define OFFSET(x,y) ((OWFuint32)&(((x*) 0x1000)->y) - 0x1000)
typedef struct BLOCK_ {
OWFint magik;
struct BLOCK_* next;
const char* file;
OWFint line;
OWFuint32 size;
OWFuint32 nInts;
OWFint memory[2];
} BLOCK;
#ifdef DEBUG
static void
OWF_Memory_Shutdown(void);
#endif
static void
OWF_Memory_LockUnlockManagedBlocks(OWFboolean lock)
{
#ifndef DEBUG
(void) lock;
#endif
#ifdef DEBUG
/* cannot use OWF_MUTEX because it uses managed memory */
static pthread_mutex_t mem_mutex = PTHREAD_MUTEX_INITIALIZER;
if (lock)
{
pthread_mutex_lock(&mem_mutex);
}
else
{
pthread_mutex_unlock(&mem_mutex);
}
#endif
}
static void
OWF_Memory_LockManagedBlocks()
{
OWF_Memory_LockUnlockManagedBlocks(OWF_TRUE);
}
static void
OWF_Memory_UnlockManagedBlocks()
{
OWF_Memory_LockUnlockManagedBlocks(OWF_FALSE);
}
OWF_API_CALL BLOCK*
OWF_Memory_GetSetManagedBlocks(BLOCK* b, OWFboolean set)
{
#ifdef DEBUG
static BLOCK* managed_blocks = NULL;
static OWFboolean initialized = OWF_FALSE;
if (!initialized) {
/* add exit handler (leak report) */
atexit(OWF_Memory_Shutdown);
initialized = OWF_TRUE;
}
if (set)
{
managed_blocks = b;
return b;
}
else
{
return managed_blocks;
}
#else
if (set)
{
b = b; /* dummy code */
}
return NULL;
#endif
}
static BLOCK*
OWF_Memory_GetManagedBlocks()
{
return OWF_Memory_GetSetManagedBlocks(NULL, OWF_FALSE);
}
static void
OWF_Memory_SetManagedBlocks(BLOCK* b)
{
OWF_Memory_GetSetManagedBlocks(b, OWF_TRUE);
}
OWF_API_CALL void*
OWF_Memory_Alloc(const char* file, OWFint line, OWFuint32 size)
{
#define INT_SIZE sizeof(OWFint)
BLOCK* block = NULL;
/* size rounded to nearest sizeof(int)*n + 2*sizeof(int) for fences */
OWFuint32 nInts = 2 + (((size + INT_SIZE-1) &~ (INT_SIZE-1)) >> 2);
OWFuint32 realSize = nInts * INT_SIZE + sizeof(BLOCK);
if (realSize > size) /* not int overflow */
{
block = (BLOCK*) malloc(realSize);
}
if (!block)
{
DPRINT(("Couldn't alloc %u bytes\n", size));
return NULL;
}
/* populate block header */
memset(block, 0, realSize);
block->magik = MAGIK;
block->file = file;
block->line = line;
block->size = size;
block->nInts = nInts;
block->memory[0] = FENCE1;
block->memory[nInts-1] = FENCE2;
OWF_Memory_LockManagedBlocks();
{
/* insert to list at position 0 */
block->next = (BLOCK*) OWF_Memory_GetManagedBlocks();
OWF_Memory_SetManagedBlocks(block);
}
OWF_Memory_UnlockManagedBlocks();
return &block->memory[1];
}
OWF_API_CALL void
OWF_Memory_Free(void* ptr)
{
BLOCK* block = NULL;
if (!ptr) {
return;
}
/* POINTER ARITHMETIC HAZARD */
block = (BLOCK*) ((OWFuint32) ptr - (OWFuint32) OFFSET(BLOCK,memory[1]));
if (!block)
{
DPRINT(("Sanity check failed. Ptr was zero....\n"));
return;
}
OWF_Memory_LockManagedBlocks();
{
BLOCK* temp = NULL;
BLOCK* prev = NULL;
OWFboolean stillExists = OWF_FALSE;
temp = OWF_Memory_GetManagedBlocks();
while (temp)
{
/* the block is on the list? */
if (temp == block && ptr == &temp->memory[1])
{
stillExists = OWF_TRUE;
break;
}
prev = temp;
temp = temp->next;
}
if (stillExists == OWF_TRUE)
{
if (MAGIK != temp->magik)
{
DPRINT(("Possibly corrupt or invalid block, addr = %p\n", ptr));
}
if (block->memory[0] != (OWFint)FENCE1)
{
DPRINT(("Block's start fence corrupted, addr = %p\n", ptr));
}
if (block->memory[block->nInts-1] != (OWFint)FENCE2)
{
DPRINT(("Block's end fence corrupted, addr = %p\n", ptr));
}
/* valid block, unlink & free it */
if (prev)
{
prev->next = temp->next;
}
else
{
OWF_Memory_SetManagedBlocks(temp->next);
}
free(block);
}
else
{
/* possibly already freed, strangled pointer. complain. */
DPRINT(("Block possibly freed already! (block = %p, addr = %p)\n",
(void*) block, ptr));
}
}
OWF_Memory_UnlockManagedBlocks();
}
OWF_API_CALL void
OWF_Memory_BlockDump()
{
BLOCK* block = OWF_Memory_GetManagedBlocks();
/* managed blocks locked when this is executed */
while (block)
{
BLOCK* next = (BLOCK*) block->next;
if (MAGIK == block->magik)
{
DPRINT(("Block: %p\nFile: %s(%d)\nSize: %u bytes\n",
(void*) &block->memory[1],
block->file,
block->line,
block->size));
}
else
{
DPRINT(("Possibly corrupt or invalid block (addr = %p)\n",
(void*) block));
}
block = next;
}
}
#ifdef DEBUG
static void
OWF_Memory_Shutdown()
{
BLOCK* block = NULL;
OWF_Memory_LockManagedBlocks();
{
if (OWF_Memory_GetManagedBlocks())
{
DPRINT(("======================================================================\n"));DPRINT(("MEMORY LEAK REPORT\n"));DPRINT(("======================================================================\n"));
OWF_Memory_BlockDump();
block = OWF_Memory_GetManagedBlocks();
while (block)
{
BLOCK* next = (BLOCK*) block->next;
if (MAGIK == block->magik)
{
free(block);
}
block = next;
}
OWF_Memory_SetManagedBlocks(NULL);
}
}
OWF_Memory_UnlockManagedBlocks();
}
#endif
#ifdef __cplusplus
}
#endif