# Define the compiler environment
Env = Rscons::Environment.new do |env|
- env.build_dir('source/','build/obj/source')
+ env.build_dir('source','build/obj/source')
env["CFLAGS"] += ['-Wall', '-Wextra', '-Werror']
env['CPPPATH'] += Dir['source/**/']
end
TestEnv = Env.clone do |env|
env.build_dir('source','build/obj/test_source')
env.build_dir('tests','build/obj/tests/source')
- env['CFLAGS'] += ['-DLEAK_DETECTION']
+ #env['CFLAGS'] += ['-DLEAK_DETECT_LEVEL=1']
env['CPPPATH'] += Dir['tests/']
end
#include "mem.h"
#include <stdlib.h>
-#ifdef LEAK_DETECTION
+#ifdef LEAK_DETECT_LEVEL
#include <stdbool.h>
#include <stdio.h>
#endif
intptr_t val;
} box_t;
-/* If LEAK_DETECTION is turned on then we need to disable the mem_allocate macro
+/* If leak detection is turned on then we need to disable the mem_allocate macro
* for the duration of this file. This allows us to use the original
* mem_allocate function without modification in mem_allocate_ld */
-#ifdef LEAK_DETECTION
+#if (LEAK_DETECT_LEVEL == 2)
#undef mem_allocate
+void* mem_allocate(size_t size, destructor_t p_destruct_fn);
#endif
-void* mem_allocate(size_t size, destructor_t p_destruct_fn)
-{
- obj_t* p_obj = (obj_t*)malloc(sizeof(obj_t) + size);
- p_obj->refcount = 1;
- p_obj->p_finalize = p_destruct_fn;
- return (void*)(p_obj+1);
-}
+#if (LEAK_DETECT_LEVEL > 0)
+bool Handler_Registered = false;
-#ifdef LEAK_DETECTION
+#if (LEAK_DETECT_LEVEL == 2)
typedef struct block_t {
void* p_obj;
const char* p_file;
} block_t;
block_t* Live_Blocks = NULL;
+#elif (LEAK_DETECT_LEVEL == 1)
+size_t Num_Allocations = 0;
+#endif
-bool Handler_Registered = false;
-
-static void print_live_objects(void) {
+static void summarize_leaks(void) {
+ #if (LEAK_DETECT_LEVEL == 2)
bool leak_detected = false;
block_t* p_curr = Live_Blocks;
/* Print out all the live blocks and where they were allocated from */
}
if(leak_detected)
puts("Memory leak(s) detected!");
+ #elif (LEAK_DETECT_LEVEL == 1)
+ if(Num_Allocations > 0) {
+ puts("Warning: Memory leak(s) detected!");
+ printf("\nFor more details set the LEAK_DETECT_LEVEL build option to 2 or run the executable in valgrind.\n");
+ }
+ #endif
}
+#if (LEAK_DETECT_LEVEL == 2)
void* mem_allocate_ld(size_t size, destructor_t p_destruct_fn, const char* p_file, int line)
{
/* Allocate the object through the ordinary method */
* unfreed objects before the program quits */
if(!Handler_Registered)
{
- atexit(print_live_objects);
+ atexit(summarize_leaks);
Handler_Registered = true;
}
return p_obj;
}
#endif
+#endif
-void* mem_retain(void* p_obj)
-{
- obj_t* p_hdr = (((obj_t*)p_obj)-1);
- p_hdr->refcount += 1;
- return p_obj;
-}
-
-#ifdef LEAK_DETECTION
+#if (LEAK_DETECT_LEVEL == 2)
static void deregister_block(void* p_obj)
{
block_t* p_prev = NULL;
}
#endif
-void mem_release(void* p_obj)
+void* mem_allocate(size_t size, destructor_t p_destruct_fn)
{
- obj_t* p_hdr = (((obj_t*)p_obj)-1);
- p_hdr->refcount -= 1;
- if(p_hdr->refcount < 1)
+ obj_t* p_obj = (obj_t*)malloc(sizeof(obj_t) + size);
+ p_obj->refcount = 1;
+ p_obj->p_finalize = p_destruct_fn;
+ #if (LEAK_DETECT_LEVEL == 1)
+ Num_Allocations++;
+ /* If we haven't already, register an exit handler that will printout the
+ * unfreed objects before the program quits */
+ if(!Handler_Registered)
{
- #ifdef LEAK_DETECTION
- deregister_block(p_obj);
- #endif
- if(p_hdr->p_finalize)
- {
- p_hdr->p_finalize(p_obj);
- }
- free(p_hdr);
+ atexit(summarize_leaks);
+ Handler_Registered = true;
}
+ #endif
+ return (void*)(p_obj+1);
}
int mem_num_references(void* p_obj)
return p_hdr->refcount;
}
-void mem_autorelease(void* p_obj)
+void* mem_retain(void* p_obj)
{
- (void)p_obj;
+ obj_t* p_hdr = (((obj_t*)p_obj)-1);
+ p_hdr->refcount += 1;
+ return p_obj;
}
-void mem_releaseall(void)
+void mem_release(void* p_obj)
{
+ obj_t* p_hdr = (((obj_t*)p_obj)-1);
+ p_hdr->refcount -= 1;
+ if(p_hdr->refcount < 1)
+ {
+ #if (LEAK_DETECT_LEVEL == 2)
+ deregister_block(p_obj);
+ #endif
+ if(p_hdr->p_finalize)
+ {
+ p_hdr->p_finalize(p_obj);
+ }
+ free(p_hdr);
+ }
}
void* mem_box(intptr_t val)
/** A function pointer for object destructors */
typedef void (*destructor_t)(void* p_val);
-/* If debug is disabled, disable leak detection as well */
-#ifdef NDEBUG
-#undef LEAK_DETECTION
+/** Unless otherwise specified, no leak detection will occur */
+#ifndef LEAK_DETECT_LEVEL
+#define LEAK_DETECT_LEVEL 0
#endif
-#ifndef LEAK_DETECTION
+#if (LEAK_DETECT_LEVEL < 2)
/**
* @brief Allocates a new reference counted object of the given size which will
* be destructed with the given function before it's memory is reclaimed.
*/
void mem_release(void* p_obj);
-/**
- * @brief Schedules an object to be released at a later time when more convenient.
- *
- * @param p_obj The object to be released.
- */
-void mem_autorelease(void* p_obj);
-
-/**
- * @brief Release all objects scheduled to be released reclaiming their memory
- * if necessary.
- */
-void mem_releaseall(void);
-
/**
* @brief Create a reference counted box holding the given value so that it can
* be placed in a container.