diff Isolated/tErrorLib.c @ 38:71b465ff0622

Added support files.
author Eric Wing <ewing@anscamobile.com>
date Thu, 28 Apr 2011 16:22:30 -0700
parents
children c07dbd386ded
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Isolated/tErrorLib.c	Thu Apr 28 16:22:30 2011 -0700
@@ -0,0 +1,1264 @@
+/* See header file for license information. */
+
+#include "tErrorLib.h"
+#include <stdlib.h> /* for malloc */
+#include <string.h>
+#include <stdarg.h> /* for vasprintf */
+#include <stdio.h> /* also for vasprintf and for printf family */
+#include <stddef.h> /* size_t */
+
+/**
+ * For string-only based usage, this implementation
+ * still expects an actual error number to be set.
+ * I am defining 1 as that error value. This might be changable,
+ * but it is untested. If you change this value, you must recompile
+ * the entire library. This can really be any integer except what 
+ * TERROR_NOERROR_VALUE (in header) is set to.
+ */
+#define TERROR_ERROR_VALUE 1
+
+#ifdef DONT_USE_VASPRINT
+	#define TERROR_DEFAULT_STRING_LENGTH 128
+	/* Visual Studio doesn't define snprintf but _snprintf */
+	#ifdef _MSC_VER
+		#define snprintf _snprintf
+		#define vsnprintf _vsnprintf
+	#endif
+#endif
+
+
+#if defined(_WIN32) && !defined(__CYGWIN32__)
+		#include <windows.h>
+		#include <winbase.h> /* For CreateMutex(), LockFile() */
+
+		static void* Internal_CreateMutex()
+		{
+			return((void*)CreateMutex(NULL, FALSE, NULL));
+		}
+		static void Internal_DestroyMutex(void* mutex)
+		{
+			if(NULL != mutex)
+			{
+				CloseHandle( (HANDLE)mutex );
+			}
+		}
+		/* This will return true if locking is successful, false if not.
+		 */
+		static int Internal_LockMutex(void* mutex)
+		{
+			return(
+				WaitForSingleObject(
+					(HANDLE)mutex,
+					INFINITE
+				) != WAIT_FAILED
+			);
+		}
+		static void Internal_UnlockMutex(void* mutex)
+		{
+			ReleaseMutex(
+				(HANDLE)mutex
+			);
+		}
+		size_t Internal_PlatformPlatformGetThreadID(void)
+		{
+			return((size_t)GetCurrentThreadId());
+		}
+#else /* Assuming POSIX...maybe not a good assumption. */
+		#include <pthread.h>
+		static void* Internal_CreateMutex()
+		{
+			int ret_val;
+			pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); 
+			if(NULL == m)
+			{
+				return NULL;
+			}
+			ret_val = pthread_mutex_init(m, NULL);
+			if(0 != ret_val)
+			{
+				free(m);
+				return NULL;
+			}
+			return((void*)m);
+		}
+		static void Internal_DestroyMutex(void* mutex)
+		{
+			if(NULL != mutex)
+			{
+				pthread_mutex_destroy((pthread_mutex_t*) (mutex));
+				free(mutex);
+			}
+		}
+		/* This will return true if locking is successful, false if not.
+		 * (This is the opposite of pthread_mutex_lock which returns 
+		 * 0 for success.)
+		 */
+		static int Internal_LockMutex(void* mutex)
+		{
+			return(
+				pthread_mutex_lock(
+					(pthread_mutex_t*)mutex
+				) == 0
+			);
+		}
+		static void Internal_UnlockMutex(void* mutex)
+		{
+			pthread_mutex_unlock(
+				(pthread_mutex_t*)mutex
+			);
+		}
+
+	size_t Internal_PlatformGetThreadID()
+	{
+		/* Basically, we need to convert a pthread_t into an id number. */
+	    return (size_t)pthread_self();
+	}
+#endif
+
+
+/**
+ * Copies a source string, potentially to a target string, and returns 
+ * the pointer to the copied string.
+ * This function is a intended to be an efficient string copy function.
+ * It's purpose is to copy a string into a string with preallocated memory
+ * and avoid dynamic memory allocation if possible. If memory must 
+ * be allocated, then the old string will be destroyed.
+ *
+ * This is only to be used where target_string was created with dynamic 
+ * memory. This function will destroy the memory and allocate new memory
+ * if there is not enough space in the target string.
+ *
+ * @param target_string This is the string you would like to try 
+ * to copy into. If there is not enough space, a new string will
+ * be created and the target_string will be freed. This string 
+ * must have been created dynamically. This may be NULL if you 
+ * wish for this function to dynamically create a new string 
+ * for you.
+ *
+ * @param target_max_buffer_size This is a pointer that points to 
+ * an address containing the size of the preallocated target_string. 
+ * This size is the maximum buffer length which includes the '\\0'
+ * character as part of that count. This pointer may not be NULL.
+ * If you pass in NULL for the target_string (indicating you want 
+ * a new string allocated for you), then the size should be set to 0.
+ * When the function completes, the size will be set to the new 
+ * max buffer size of the string if the string needed to be reallocated.
+ *
+ * @param source_string This is the string you want to copy. If it's NULL,
+ * the target_string will have it's memory freed.
+ *
+ * @return Will return a pointer to the duplicated string. Be aware 
+ * of several things:
+ * - The returned pointer address may not be the same address as the 
+ * target string passed in (due to a possible reallocation).
+ * - If the pointer to the source and target string 
+ * are the same, the pointer to the target string will be returned.
+ * - If the source string is NULL, the target string
+ * will be freed and will return NULL.
+ * - If an error occurs, NULL will be returned.
+ *
+ * Also note that the value at the address target_max_buffer_size points 
+ * to will be filled with the new max buffer size for the string.
+ *
+ * Example:
+ * @code
+ *
+ * int main()
+ * {
+ * 		const char* original1 = "Hello World";
+ * 		const char* original2 = "Smaller";
+ * 		const char* original3 = "Good-Bye World";
+ * 		char* ret_val;
+ * 		char* target = NULL;
+ * 		size_t target_max_buffer_size = 0;
+ *
+ * 		ret_val = CopyDynamicString(target, &target_max_buffer_size, original1);
+ *
+ * 		if(ret_val)
+ * 		{
+ * 			fprintf(stderr, "Target is '%s' with max size = %d\n", ret_val, target_max_buffer_size);
+ * 		}
+ * 		else
+ * 		{
+ * 			fprintf(stderr, "Error in function\n");
+ * 		}
+ *		target = ret_val;
+ *
+ *		ret_val = CopyDynamicString(target, &target_max_buffer_size, original2);
+ * 		fprintf(stderr, "Target is '%s' with max size = %d\n", ret_val, target_max_buffer_size);
+ *
+ * 		target = ret_val; *
+ * 		ret_val = CopyDynamicString(target, &target_max_buffer_size, original3);
+ * 		fprintf(stderr, "Target is '%s' with max size = %d\n", ret_val, target_max_buffer_size);
+ *
+ * 		return 0;
+ * }
+ * @endcode
+ * This outputs:
+ * @code
+ * Target is 'Hello World' with max size = 12
+ * Target is 'Smaller' with max size = 12
+ * Target is 'Good-Bye World' with max size = 15
+ * @endcode
+ */
+static char* Internal_CopyDynamicString(char* target_string, size_t* target_max_buffer_size, const char* source_string)
+{
+	/* If the pointers are the same, no copy is needed. */
+	if(source_string == target_string)
+	{
+		/* I don't feel like asserting if the sizes are the same. */
+		/* Return 1 instead of 0 because maybe this isn't an error?
+		 */
+		return target_string;
+	}
+
+	/* Make sure the size pointer is valid. */
+	if(NULL == target_max_buffer_size)
+	{
+		return NULL;
+	}
+
+	/* Yikes, if the string is NULL, should we make the target string NULL?
+	 * For now, yes, we destroy the string. If you change this, realize that
+	 * their is code that depends on this behavior.
+	 */
+	if(NULL == source_string)
+	{
+		*target_max_buffer_size = 0;
+		free(target_string);
+		target_string = NULL;
+		return NULL;
+	}
+
+	/* If target_string is NULL, the *target_max_buffer_size should also be 0.
+	 * Technically, the user should set this and this would be an error,
+	 * but I'll be nice for now. An alternate implementation might suggest 
+	 * that the size would be the desired size the user wants for a new string.
+	 */
+	if( (NULL == target_string) && (0 != *target_max_buffer_size) )
+	{
+		*target_max_buffer_size = 0;
+	}
+
+	/* If there is not enough preallocated memory in the target string,
+	 * then we need to reallocate enough memory.
+	 */
+	if( *target_max_buffer_size < (strlen(source_string) + 1) )
+	{
+		*target_max_buffer_size = 0;
+		if(NULL != target_string)
+		{
+			free(target_string);
+		}
+		target_string = (char*)calloc( (strlen(source_string) + 1), sizeof(char) );
+		if(NULL == target_string)
+		{
+			return NULL;
+		}
+		*target_max_buffer_size = strlen(source_string) + 1;
+	}
+
+	/* At this point, there should be enough preallocated 
+	 * memory to call strncpy.
+	 */
+	strncpy(target_string, source_string, *target_max_buffer_size);
+
+	return target_string;
+}
+
+/**
+ * This is a structure that contains everything needed for an 
+ * error message entry (per thread). The linked list stuff
+ * is fused in with it because I didn't want to write an entire 
+ * linked list class.
+ */
+typedef struct TErrorMessageStructType
+{
+	size_t threadID; /** ThreadID for associated message. */
+	int errorAvailable; /** 1 if an error has been set and not been checked. */
+	int errorNumber; /**< For the user error number. */
+	char* errorString; /**< For the user error message. */
+	size_t errorMaxStringLength; /**< Max size of string buffer including \\0. */
+	struct TErrorMessageStructType* nextItem; /**< Pointer to next error message in list. */
+} TErrorMessage;
+
+/**
+ * This is a private struct that contains all private data for an
+ * ErrorPool. Currently it is a linked list containing all error message
+ * structs for every thread.
+ */
+typedef struct
+{
+	TErrorMessage* errorMessageListHead; /**< Head of the error list. */
+	TErrorMessage* lastErrorMessage; /**< Points to the last set element in the list for GetLastError. */
+	/* Mutex */
+} TErrorPoolOpaqueData;
+
+/**
+ * This is a private helper function that creates a new TErrorMessage
+ * and initializes all its values.
+ * @return Returns a pointer to a newly allocated and initialized
+ * TErrorMessage or NULL on failure.
+ */
+static TErrorMessage* Internal_CreateErrorMessageStructure()
+{
+	TErrorMessage* new_message;
+	/* Use calloc to create a fully cleared structure,
+	 * so I don't have to set/clear each member.
+	 */
+	new_message = (TErrorMessage*)calloc(1, sizeof(TErrorMessage));
+	if(NULL == new_message)
+	{
+		/* Very bad, but not sure what to do. */
+		return NULL;
+	}
+	new_message->errorNumber = TERROR_NOERROR_VALUE;
+	return new_message;
+}
+
+/**
+ * This is a private helper function that frees a TErrorMessage.
+ *
+ * @param err_mesg The pointer to the TErrorMessage to be freed.
+ */
+static void Internal_FreeErrorMessageStructure(TErrorMessage* err_mesg)
+{
+	if(NULL == err_mesg)
+	{
+		return;
+	}
+	if(NULL != err_mesg->errorString)
+	{
+		free(err_mesg->errorString);
+		err_mesg->errorString = NULL;
+	}
+	err_mesg->nextItem = NULL;
+	free(err_mesg);
+}
+
+/**
+ * This is a private helper function that will search the error pool 
+ * for the last set error message structure in the Linked list.
+ * If the last error message was on a different thread, the error
+ * data will be copied to the current thread's memory and the 
+ * lastErrorMessage pointer will be set to the current thread's message.
+ * (This is because I expect this message to be marked as cleared/read.)
+ * This function does its own mutex locking.
+ *
+ * @param err_pool The error pool to be used.
+ * @return Returns the a pointer to the TErrorMessage if found,
+ * NULL if not found.
+ */
+static TErrorMessage* Internal_GetLastError(TErrorPool* err_pool)
+{
+	size_t thread_id;
+	TErrorMessage* current_thread_err_mesg;
+	TErrorPoolOpaqueData* err_pool_data;
+
+	thread_id = Internal_PlatformGetThreadID();
+	
+	Internal_LockMutex(err_pool->mutexLock);
+	
+	err_pool_data = err_pool->opaqueData;
+
+	if(NULL == err_pool_data->errorMessageListHead)
+	{
+		Internal_UnlockMutex(err_pool->mutexLock);
+		return NULL;
+	}
+
+	/* I think this is actually an assertion failure.
+	 * I do the check here so I don't have to keep checking below.
+	 */
+	if(NULL == err_pool_data->lastErrorMessage)
+	{
+		Internal_UnlockMutex(err_pool->mutexLock);
+		return NULL;
+	}
+
+	/* We need to determine if the lastMessage pointer is pointing 
+	 * to data on the current thread. If it is we can just return it.
+	 * Otherwise, we need to copy the message to the current thread's
+	 * error message memory area.
+	 * We should also update the lastMessage pointer to point
+	 * to this message since it will likely be marked cleared once read.
+	 */
+	if(thread_id == err_pool_data->lastErrorMessage->threadID)
+	{
+		/* Not copy is needed. The last error message already
+		 * points to the memory on the current thread.
+		 * We can short-circuit and return.
+		 */
+		Internal_UnlockMutex(err_pool->mutexLock);
+		return err_pool_data->lastErrorMessage;
+	}
+
+	/* Sigh, I really should have a dedicated linked list structure,
+	 * but I don't feel like writing it right now.
+	 */
+	for(current_thread_err_mesg = err_pool_data->errorMessageListHead; current_thread_err_mesg != NULL; current_thread_err_mesg = current_thread_err_mesg->nextItem)
+	{
+		/* First find the message (memory) for the current thread. */
+		if(thread_id == current_thread_err_mesg->threadID)
+		{
+			/* Now we need to copy the message data from the lastErrorMessage
+			 * to this thread's message (memory).
+			 */
+			current_thread_err_mesg->errorNumber = err_pool_data->lastErrorMessage->errorNumber;
+			current_thread_err_mesg->errorAvailable = err_pool_data->lastErrorMessage->errorAvailable;
+			/* This will copy the string and set the new errorMaxStringLength as needed. */
+			current_thread_err_mesg->errorString = Internal_CopyDynamicString(current_thread_err_mesg->errorString, &current_thread_err_mesg->errorMaxStringLength, err_pool_data->lastErrorMessage->errorString);
+
+
+			/* Finally, change the last error message to point to 
+			 * the current thread since I expect the message to be
+			 * marked cleared and we don't want to accidentally refetched
+			 * the stale, uncleared entry.
+			 */
+			err_pool_data->lastErrorMessage = current_thread_err_mesg;
+
+			Internal_UnlockMutex(err_pool->mutexLock);
+			return current_thread_err_mesg;
+		}
+	}
+	Internal_UnlockMutex(err_pool->mutexLock);
+	return NULL;
+}
+
+/**
+ * This is a private helper function that will search the error pool 
+ * for an error message structure in the Linked list (by thread ID)
+ * and return the pointer if found. This function does its own mutex
+ * locking.
+ * @param err_pool The error pool to be used.
+ * @return Returns the a pointer to the TErrorMessage if found,
+ * NULL if not found.
+ */
+static TErrorMessage* Internal_GetErrorOnCurrentThread(TErrorPool* err_pool)
+{
+	size_t thread_id;
+	TErrorMessage* current_err_mesg;
+	TErrorPoolOpaqueData* err_pool_data;
+
+	thread_id = Internal_PlatformGetThreadID();
+	
+	Internal_LockMutex(err_pool->mutexLock);
+	
+	err_pool_data = err_pool->opaqueData;
+
+	if(NULL == err_pool_data->errorMessageListHead)
+	{
+		Internal_UnlockMutex(err_pool->mutexLock);
+		return NULL;
+	}
+	
+	/* Sigh, I really should have a dedicated linked list structure,
+	 * but I don't feel like writing it right now.
+	 */
+	for(current_err_mesg = err_pool_data->errorMessageListHead; current_err_mesg != NULL; current_err_mesg = current_err_mesg->nextItem)
+	{
+		if(thread_id == current_err_mesg->threadID)
+		{
+			Internal_UnlockMutex(err_pool->mutexLock);
+			return current_err_mesg;
+		}
+	}
+	Internal_UnlockMutex(err_pool->mutexLock);
+	return NULL;
+}
+
+/**
+ * Given a specific TErrorMessage*, will set the lastErrorMessage pointer to
+ * the provided error message.
+ * This function locks.
+ *
+ * @param err_pool The error pool to be used.
+ * @param error_message The error message to set the lastErrorMessage pointer to
+ */
+static void Internal_SetLastErrorMessagePointerToErrorMessage(TErrorPool* err_pool, TErrorMessage* error_message)
+{
+	TErrorPoolOpaqueData* err_pool_data;	
+	Internal_LockMutex(err_pool->mutexLock);
+	err_pool_data = err_pool->opaqueData;
+	err_pool_data->lastErrorMessage = error_message;
+	Internal_UnlockMutex(err_pool->mutexLock);
+}
+
+
+/**
+ * This is a private helper function that creates a new error message
+ * structure for the current thread.
+ * This currently does not check if an error already exists
+ * before creating a new entry. Call GetErrorOnCurrentThread first
+ * to make sure nothing exists or duplicate entries will be created.
+ * This function does its own mutex locking.
+ * 
+ * @param err_pool The error pool to be used.
+ * @return Returns the a pointer to the TErrorMessage if found,
+ * NULL if there was an allocation error.
+ */
+static TErrorMessage* Internal_CreateErrorOnCurrentThread(TErrorPool* err_pool)
+{
+	TErrorMessage* new_err_mesg;
+	TErrorPoolOpaqueData* err_pool_data;
+
+	new_err_mesg = Internal_CreateErrorMessageStructure();
+	if(NULL == new_err_mesg)
+	{
+		/* Serious problem, not sure what to do. */
+		return NULL;
+	}
+	/* Copy the thread id so we can distinguish between entries. */
+	new_err_mesg->threadID = Internal_PlatformGetThreadID();
+
+	Internal_LockMutex(err_pool->mutexLock);
+
+	err_pool_data = err_pool->opaqueData;
+	/* Add the new message to the top of the list by making 
+	 * its next pointer point to the head of the current list.
+	 * (A formal linked list implementation would make this feel 
+	 * less hacky.)
+	 * This also (should) handle the case where errorMessageListHead
+	 * is NULL.
+	 */
+	new_err_mesg->nextItem = err_pool_data->errorMessageListHead;
+	/* Now set the head of the list to the new message.
+	 */
+	err_pool_data->errorMessageListHead = new_err_mesg;
+	
+	Internal_UnlockMutex(err_pool->mutexLock);
+
+	return new_err_mesg;
+}
+
+/**
+ * This is a private helper function that will clean up all the 
+ * error message structures in the list. This function does its 
+ * own locking.
+ * @param err_pool The error pool to be used.
+ */
+static void Internal_FreeErrorMessageList(TErrorPool* err_pool)
+{
+	TErrorMessage* current_message = NULL;
+	TErrorMessage* next_message = NULL;
+	TErrorPoolOpaqueData* err_pool_data;
+
+	Internal_LockMutex(err_pool->mutexLock);
+	
+	err_pool_data = err_pool->opaqueData;
+
+	if(NULL == err_pool_data->errorMessageListHead)
+	{
+		Internal_UnlockMutex(err_pool->mutexLock);
+		return;
+	}
+	
+	/* Sigh, I really should have a dedicated linked list structure,
+	 * but I don't feel like writing it right now.
+	 */
+	for(current_message = err_pool_data->errorMessageListHead;
+		current_message != NULL;
+		current_message = next_message
+	)
+	{
+		next_message = current_message->nextItem;
+		Internal_FreeErrorMessageStructure(current_message);
+	}
+	err_pool_data->errorMessageListHead = NULL;
+	err_pool_data->lastErrorMessage = NULL;
+
+	Internal_UnlockMutex(err_pool->mutexLock);
+}
+
+/* 
+ * API functions start below.
+ *
+ */
+
+
+void TError_DeleteEntryOnCurrentThread(TErrorPool* err_pool)
+{
+	TErrorMessage* prev_message = NULL;
+	TErrorMessage* current_message = NULL;
+	TErrorMessage* next_message = NULL;
+	size_t thread_id;
+	TErrorPoolOpaqueData* err_pool_data;
+
+	thread_id = Internal_PlatformGetThreadID();
+	
+	Internal_LockMutex(err_pool->mutexLock);
+
+	err_pool_data = err_pool->opaqueData;
+
+	if(NULL == err_pool_data->errorMessageListHead)
+	{
+		Internal_UnlockMutex(err_pool->mutexLock);
+		return;
+	}
+	
+	/* Sigh, I really should have a dedicated linked list structure,
+	 * but I don't feel like writing it right now.
+	 */
+	for(current_message = err_pool_data->errorMessageListHead;
+		current_message != NULL;
+		/* I'm not going to increment here because I
+		 * may delete the item below which would probably 
+		 * cause bad things to happen here.
+		 */
+/* 		current_message = current_message->nextItem */
+	)
+	{
+		next_message = current_message->nextItem;
+		
+		if(thread_id == current_message->threadID)
+		{
+			/* Special case, current is only item in list:
+			 * Both next and prev are NULL in this case.
+			 * We should delete the item and set the errorMessageListHead
+			 * to NULL.
+			 */
+			if((NULL == prev_message) && (NULL == next_message))
+			{
+				Internal_FreeErrorMessageStructure(current_message);
+				current_message = NULL;
+				err_pool_data->errorMessageListHead = NULL;
+				err_pool_data->lastErrorMessage = NULL;
+			}
+			/* Special case, current is at head:
+			 * Prev is NULL but next is not NULL in this case.
+			 * We should delete the item and set the errorMessageListHead
+			 * to point to next.
+			 * (The code for the above case would probably work for 
+			 * this case too, but for clarity, this remains.)
+			 */
+			else if(NULL == prev_message)
+			{
+				/* If the current message happened to be the last message
+				 * set, we need to change the lastErrorMessage pointer
+				 * so it is not dangling.
+				 */
+				if(current_message == err_pool_data->lastErrorMessage)
+				{
+					err_pool_data->lastErrorMessage = NULL;
+				}
+				Internal_FreeErrorMessageStructure(current_message);
+				current_message = NULL;
+				err_pool_data->errorMessageListHead = next_message;
+			}
+			/* Special case, current is at tail.
+			 * Prev is not NULL, but next is NULL in this case.
+			 * We should delete the item and set prev->next to NULL.
+			 */
+			else if(NULL == next_message)
+			{
+				/* If the current message happened to be the last message
+				 * set, we need to change the lastErrorMessage pointer
+				 * so it is not dangling.
+				 */
+				if(current_message == err_pool_data->lastErrorMessage)
+				{
+					err_pool_data->lastErrorMessage = NULL;
+				}
+				Internal_FreeErrorMessageStructure(current_message);
+				current_message = NULL;
+				prev_message->nextItem = NULL;
+			}
+			/* Normal case, current is somewhere in the middle of the list.
+			 * The item should be deleted and
+			 * the prev_message->next should connect to 
+			 * the next_message.
+			 */
+			else
+			{
+				/* If the current message happened to be the last message
+				 * set, we need to change the lastErrorMessage pointer
+				 * so it is not dangling.
+				 */
+				if(current_message == err_pool_data->lastErrorMessage)
+				{
+					err_pool_data->lastErrorMessage = NULL;
+				}
+				Internal_FreeErrorMessageStructure(current_message);
+				current_message = NULL;
+				prev_message->nextItem = next_message;
+			}
+		}
+		/* It's not this thread, so increment everything for the next loop. */
+		else
+		{
+			prev_message = current_message;
+			current_message = next_message;
+		}
+	} /* End for-loop */
+
+	Internal_UnlockMutex(err_pool->mutexLock);
+}
+
+
+void TError_GetLinkedVersion(TErrorVersion* ver)
+{
+	/* Check the pointer */
+	if(NULL == ver)
+	{
+		/* Do nothing */
+		return;
+	}
+	ver->major = TERROR_MAJOR_VERSION;
+	ver->minor = TERROR_MINOR_VERSION;
+	ver->patch = TERROR_PATCH_VERSION;
+}
+
+
+#if 0
+/* This is for global initialization, not pool initialization. */
+int TError_Init()
+{
+	/* initialize platform? */
+	/* initialize mutexes? */
+
+}
+#endif
+
+TErrorPool* TError_CreateErrorPool()
+{
+	TErrorPool* err_pool;
+	TErrorPoolOpaqueData* err_pool_data;
+	
+	err_pool = (TErrorPool*)calloc(1, sizeof(TErrorPool));
+	if(NULL == err_pool)
+	{
+		/* Very bad, but not sure what to do here. */
+		return NULL;
+	}
+	err_pool_data = (TErrorPoolOpaqueData*)calloc(1, sizeof(TErrorPoolOpaqueData));
+	if(NULL == err_pool_data)
+	{
+		/* Very bad, but not sure what to do here. */
+		free(err_pool);
+		return NULL;
+	}
+
+	/* Create mutex */
+	err_pool->mutexLock = Internal_CreateMutex();
+	
+	if(NULL == err_pool->mutexLock)
+	{
+		/* Very bad, but not sure what to do here. */
+		free(err_pool_data);
+		free(err_pool);
+		return NULL;
+	}
+	
+	/* Attach the opaque data to the error pool. */
+	err_pool->opaqueData = err_pool_data;
+
+	/* The OpaqueData will hold the error message list, but it is 
+	 * allowed to be NULL for an empty list so we don't have to allocate
+	 * it here.
+	 */
+
+	return err_pool;
+}
+
+/* There better not be any contention when this is called. */
+void TError_FreeErrorPool(TErrorPool* err_pool)
+{
+	if(NULL == err_pool)
+	{
+		return;
+	}
+	/* Free all the error messages for each thread.
+	 * This locks and unlocks as it needs.
+	 */
+	Internal_FreeErrorMessageList(err_pool);
+
+	/* Free opaque data structure. */
+	free(err_pool->opaqueData);
+
+	/* Delete mutex after all locking functions. */
+	Internal_DestroyMutex(err_pool->mutexLock);
+
+	/* Free main data structure. */
+	free(err_pool);
+}
+
+void TError_SetError(TErrorPool* err_pool, int err_num, const char* err_str, ...)
+{
+	va_list argp;
+	va_start(argp, err_str);
+	TError_SetErrorv(err_pool, err_num, err_str, argp);
+	va_end(argp);
+}
+
+void TError_SetErrorv(TErrorPool* err_pool, int err_num, const char* err_str, va_list argp)
+{
+	TErrorMessage* error_message;
+	int ret_num_chars;
+	
+	if(NULL == err_pool)
+	{
+		return;
+	}
+
+	error_message = Internal_GetErrorOnCurrentThread(err_pool);
+	/* If no error message was found, that means we must allocate
+	 * a new entry for this entry.
+	 */
+	if(NULL == error_message)
+	{
+		error_message = Internal_CreateErrorOnCurrentThread(err_pool);
+		/* If this fails, this is bad...not sure what to do though. */
+		if(NULL == error_message)
+		{
+			return;
+		}
+	}
+
+	/*
+	 * I don't think I have to lock here. The [Get|Create]ErrorOnCurrentThread
+	 * functions lock err_pool as they need access. Here, I don't access
+	 * err_pool (which is shared) and error_message should be unique for 
+	 * each thread so I don't think there is any contention. (Remember that
+	 * simultaneous calls to SetError would only happen if they are in 
+	 * different threads.)
+	 * There *might* be a problem with library calls (strncpy, calloc). 
+	 * I'm not sure if the various platforms are reentrant. 
+	 * I guess for now, I will assume they won't bite me.
+	 */
+
+	/* If the err_str is NULL, we need to free our current string
+	 * for consistency. More aggressive optimizations to hold the 
+	 * memory might be considered in the future.
+	 */
+	if(NULL == err_str)
+	{
+		if(NULL != error_message->errorString)
+		{
+			free(error_message->errorString);
+			error_message->errorString = NULL;
+			error_message->errorMaxStringLength = 0;
+		}
+	}
+	/* Else, copy the string */
+	else
+	{
+		/* I am using vasprintf which is a GNU extension so it is not 
+		 * portable. However, vasprintf makes certain things possible
+		 * which would not be otherwise, which is the reason for my 
+		 * use. The main benefit of asprintf/vasprintf is that you can
+		 * create a string using printf style formatters without 
+		 * worrying about the buffer size. sprintf should never be 
+		 * used because of potential buffer overflows. snprintf
+		 * is safer, but you are limited to a fixed size string
+		 * which from time-to-time, I have exceeded unless you make
+		 * the number really big. 
+		 * Furthermore, snprintf itself is not currently terribly portable
+		 * because it is specified only for C99 which some compilers 
+		 * still have not have embraced.
+		 * If you can't use the vasprintf implementation, 
+		 * you must add -DDONT_USE_VASPRINTF to your compile flags.
+		 */
+#ifdef DONT_USE_VASPRINTF
+		/* This implementation uses vsnprintf instead of 
+		 * vasprintf. It is strongly recommended you use
+		 * the vasprintf implmententation instead.
+		 * Never use vsprintf unless you like 
+		 * buffer overflows and security exploits.
+		 */
+		
+		/* If the string was set to NULL, we must reallocate memory first. */
+		if(NULL == error_message->errorString)
+		{
+			error_message->errorString = (char*)calloc(TERROR_DEFAULT_STRING_LENGTH, sizeof(char));
+			if(NULL == error_message->errorString)
+			{
+				/* Very bad...what should I do?
+				 */
+				error_message->errorMaxStringLength = 0;
+			}
+			else
+			{
+				error_message->errorMaxStringLength = TERROR_DEFAULT_STRING_LENGTH;
+			}
+		}
+		/* Because of the "Very Bad" situation directly above, 
+		 * I need to check again to make sure the string isn't NULL.
+		 * This will let the very bad situation continue on so va_end
+		 * can be called and the error_number still has a chance to be set.
+		 */
+		if(NULL != error_message->errorString)
+		{
+			ret_num_chars = vsnprintf(error_message->errorString, 
+				error_message->errorMaxStringLength,
+				err_str,
+				argp
+			);
+		}
+
+#else /* DONT_USE_VASPRINTF */
+		/* You might be wondering why the #ifdef logic assumes 
+		 * asprintf is available instead of requiring an explicit 
+		 * #define for that. The reason is because asprintf is the 
+		 * better option and I want you to realize that you are not 
+		 * using it. Typically, nobody knows or understands the build
+		 * system and/or files get copied into new projects with a 
+		 * entirely new build system, so it is easy to forget to 
+		 * add a -D flag. So if you compile without asprintf,
+		 * you are encouraged to explicitly know this.
+		 */
+		/* There may be a slight performance advantage to using snprintf
+		 * over asprintf depending how asprintf is written. But this 
+		 * implementation will completely destroy and reallocate a 
+		 * string regardless if a string is set to NULL, so there will
+		 * actually be no performance gains for these cases. 
+		 * (This could be optimized but some additional bookkeeping 
+		 * might be needed which might not be worth the effort and 
+		 * code clutter.)
+		 * As for memory allocation safety, because new messages for 
+		 * different threads must be allocated dynamically, there is no
+		 * way for this library to use purely static memory.
+		 * So I don't believe there is anything to be gained using
+		 * snprintf over asprintf and you lose out on arbitrary lengthed
+		 * error messages.
+		 * If memory allocation must be minimized, I recommend just 
+		 * using the error number interface by itself which 
+		 * will always keep the strings at NULL, and don't mess 
+		 * with the asprintf/sprintf code.
+		 */
+
+		ret_num_chars = vasprintf(&error_message->errorString, err_str, argp);
+		/* vasprintf returns -1 as an error */
+		if(-1 == ret_num_chars)
+		{
+			/* Very bad, but not sure what to do here. */
+			if(NULL != error_message->errorString)
+			{
+				free(error_message->errorString);
+				error_message->errorString = NULL;
+				error_message->errorMaxStringLength = 0;
+				/* Don't return here. Still need to va_end, and
+				 * there is a chance that the err_num might work.
+				 * Plus the availability needs to be set.
+				 */
+			}
+		}
+		/* else vasprint returns the number of characters in the string
+		 * not including the \0 character.
+		 */
+		else
+		{
+			/* I actually don't know how much memory vasprintf allocated
+			 * for the string. But it is at least ret_num_chars+1, so
+			 * I will use that as my max string length (which is 
+			 * mainly used by CopyDynamicString() for efficiency 
+			 * which is becoming less used in this code).
+			 */
+			error_message->errorMaxStringLength = ret_num_chars+1;
+		}	
+#endif /* DONT_USE_VASPRINTF */
+	}
+
+	/* I'm allowing for a user to explicitly clear an error message by 
+	 * clearing both attributes.
+	 */
+	if((TERROR_NOERROR_VALUE == err_num) && (NULL == err_str))
+	{
+		error_message->errorNumber = TERROR_NOERROR_VALUE;
+		error_message->errorAvailable = 0;
+	}
+	/* This is the normal case, copy the error number
+	 * and mark the error as unread.
+	 */
+	else
+	{
+		error_message->errorNumber = err_num;
+		error_message->errorAvailable = 1;
+	}
+
+	/* Now that the data is set, we also want to denote that this
+	 * thread is the last error message. We need to lock for this
+	 * since the lastError pointer is shared across threads.
+	 */
+	Internal_SetLastErrorMessagePointerToErrorMessage(err_pool, error_message);
+}
+
+void TError_SetErrorNoFormat(TErrorPool* err_pool, int err_num, const char* err_str)
+{
+	TErrorMessage* error_message;
+	if(NULL == err_pool)
+	{
+		return;
+	}
+
+	error_message = Internal_GetErrorOnCurrentThread(err_pool);
+	/* If no error message was found, that means we must allocate
+	 * a new entry for this entry.
+	 */
+	if(NULL == error_message)
+	{
+		error_message = Internal_CreateErrorOnCurrentThread(err_pool);
+		/* If this fails, this is bad...not sure what to do though. */
+		if(NULL == error_message)
+		{
+			return;
+		}
+	}
+
+	/*
+	 * I don't think I have to lock here. The [Get|Create]ErrorOnCurrentThread
+	 * functions lock err_pool as they need access. Here, I don't access
+	 * err_pool (which is shared) and error_message should be unique for 
+	 * each thread so I don't think there is any contention. (Remember that
+	 * simultaneous calls to SetError would only happen if they are in 
+	 * different threads.)
+	 * There *might* be a problem with library calls (strncpy, calloc). 
+	 * I'm not sure if the various platforms are reentrant. 
+	 * I guess for now, I will assume they won't bite me.
+	 */
+	error_message->errorNumber = err_num;
+	/* This will copy the string and set the new errorMaxStringLength as needed. */
+	error_message->errorString = Internal_CopyDynamicString(error_message->errorString, &error_message->errorMaxStringLength, err_str);
+	/* I'm allowing for a user to explicitly clear an error message by 
+	 * clearing both attributes.
+	 */
+	if((TERROR_NOERROR_VALUE == err_num) && (NULL == err_str))
+	{
+		error_message->errorAvailable = 0;
+	}
+	else
+	{
+		error_message->errorAvailable = 1;
+	}
+	
+	/* Now that the data is set, we also want to denote that this
+	 * thread is the last error message. We need to lock for this
+	 * since the lastError pointer is shared across threads.
+	 */
+	Internal_SetLastErrorMessagePointerToErrorMessage(err_pool, error_message);
+}
+
+void TError_SetErrorNum(TErrorPool* err_pool, int err_num)
+{
+	TError_SetErrorNoFormat(err_pool, err_num, NULL);
+}
+
+void TError_SetErrorStr(TErrorPool* err_pool, const char* err_str, ...)
+{
+	va_list argp;
+	va_start(argp, err_str);
+	if(NULL == err_str)
+	{
+		TError_SetErrorv(err_pool, TERROR_NOERROR_VALUE, err_str, argp);
+	}
+	else
+	{
+		TError_SetErrorv(err_pool, TERROR_ERROR_VALUE, err_str, argp);
+	}
+	va_end(argp);
+}
+
+void TError_SetErrorStrv(TErrorPool* err_pool, const char* err_str, va_list argp) 
+{
+	if(NULL == err_str)
+	{
+		TError_SetErrorv(err_pool, TERROR_NOERROR_VALUE, err_str, argp);
+	}
+	else
+	{
+		TError_SetErrorv(err_pool, TERROR_ERROR_VALUE, err_str, argp);
+	}
+}
+
+/* If a NULL string is set, then it is presumed no error actually occurred
+ * and this is a reset. So the err_num will be implicitly set to 0. Otherwise
+ * the err_num will be set to 1 (for internal consistency and conventions).
+ */
+void TError_SetErrorStrNoFormat(TErrorPool* err_pool, const char* err_str)
+{
+	if(NULL == err_str)
+	{
+		TError_SetErrorNoFormat(err_pool, TERROR_NOERROR_VALUE, err_str);
+	}
+	else
+	{
+		TError_SetErrorNoFormat(err_pool, TERROR_ERROR_VALUE, err_str);
+	}
+}
+
+/* This currently returns 0 as a "no error found" value.
+ * This could potentially conflict with a user. For now, users 
+ * shouldn't use 0 to represent an error. If this becomes a 
+ * problem, we could introduce a magic number like -999999 and 
+ * define TERROR_NO_ERROR_FOUND.
+ */
+int TError_GetErrorNumOnCurrentThread(TErrorPool* err_pool)
+{
+	TErrorMessage* error_message;
+
+	error_message = Internal_GetErrorOnCurrentThread(err_pool);
+	
+	/* If no error message was found for the thread. */
+	if(NULL == error_message)
+	{
+		return 0;
+	}
+	/* If an error message was found for the thread, but 
+	 * it has already been read/cleared.
+	 */
+	if(0 == error_message->errorAvailable)
+	{
+		return 0;
+	}
+	/* We found a legitimate error message, clear it and return it. */
+	error_message->errorAvailable = 0;
+	return error_message->errorNumber;
+}
+
+const char* TError_GetErrorStrOnCurrentThread(TErrorPool* err_pool)
+{
+	TErrorMessage* error_message;
+
+	error_message = Internal_GetErrorOnCurrentThread(err_pool);
+	
+	/* If no error message was found for the thread. */
+	if(NULL == error_message)
+	{
+		return 0;
+	}
+	/* If an error message was found for the thread, but 
+	 * it has already been read/cleared.
+	 */
+	if(0 == error_message->errorAvailable)
+	{
+		return 0;
+	}
+	/* We found a legitimate error message, clear it and return it. */
+	error_message->errorAvailable = 0;
+	return error_message->errorString;
+}
+
+TErrorStatus TError_GetErrorOnCurrentThread(TErrorPool* err_pool)
+{
+	TErrorMessage* error_message;
+	TErrorStatus error_container;
+	error_container.errorNumber = TERROR_NOERROR_VALUE;
+	error_container.errorString = NULL;
+	
+	error_message = Internal_GetErrorOnCurrentThread(err_pool);
+	
+	/* If no error message was found for the thread. */
+	if(NULL == error_message)
+	{
+		return error_container;
+	}
+	/* If an error message was found for the thread, but 
+	 * it has already been read/cleared.
+	 */
+	if(0 == error_message->errorAvailable)
+	{
+		return error_container;
+	}
+	/* We found a legitimate error message, clear it and return it. */
+	error_message->errorAvailable = 0;
+	
+	error_container.errorNumber = error_message->errorNumber;
+	error_container.errorString = error_message->errorString;
+	return error_container;
+}
+
+/* This function is for alternative usage where you just want one error 
+ * for all threads. The backend will still work the same, but when you
+ * call this function, it will look up the last set error, copy (with locking)
+ * the last error to the current thread's memory, and return the object.
+ * As always, since the returned object is only accessed on this thread, you
+ * don't have to worry about locking.
+ */
+TErrorStatus TError_GetLastError(TErrorPool* err_pool)
+{
+
+	// Lock the error pool to get the lastMessage pointer
+	// if the lastMessage pointer is pointing to data on the current thread,
+	// we can just return it.
+	// Otherwise, we need to copy the message 
+
+	TErrorMessage* error_message;
+	TErrorStatus error_container;
+	error_container.errorNumber = TERROR_NOERROR_VALUE;
+	error_container.errorString = NULL;
+	
+	error_message = Internal_GetLastError(err_pool);
+	
+	/* If no error message was found for the thread. */
+	if(NULL == error_message)
+	{
+		return error_container;
+	}
+	/* If an error message was found for the thread, but 
+	 * it has already been read/cleared.
+	 */
+	if(0 == error_message->errorAvailable)
+	{
+		return error_container;
+	}
+	/* We found a legitimate error message, clear it and return it. */
+	error_message->errorAvailable = 0;
+	
+	error_container.errorNumber = error_message->errorNumber;
+	error_container.errorString = error_message->errorString;
+	return error_container;
+}
+
+/* This currently returns 0 as a "no error found" value.
+ * This could potentially conflict with a user. For now, users 
+ * shouldn't use 0 to represent an error. If this becomes a 
+ * problem, we could introduce a magic number like -999999 and 
+ * define TERROR_NO_ERROR_FOUND.
+ */
+int TError_GetLastErrorNum(TErrorPool* err_pool)
+{
+	TErrorMessage* error_message;
+
+	error_message = Internal_GetLastError(err_pool);
+	
+	/* If no error message was found for the thread. */
+	if(NULL == error_message)
+	{
+		return 0;
+	}
+	/* If an error message was found for the thread, but 
+	 * it has already been read/cleared.
+	 */
+	if(0 == error_message->errorAvailable)
+	{
+		return 0;
+	}
+	/* We found a legitimate error message, clear it and return it. */
+	error_message->errorAvailable = 0;
+	return error_message->errorNumber;
+}
+
+const char* TError_GetLastErrorStr(TErrorPool* err_pool)
+{
+	TErrorMessage* error_message;
+
+	error_message = Internal_GetLastError(err_pool);
+	
+	/* If no error message was found for the thread. */
+	if(NULL == error_message)
+	{
+		return 0;
+	}
+	/* If an error message was found for the thread, but 
+	 * it has already been read/cleared.
+	 */
+	if(0 == error_message->errorAvailable)
+	{
+		return 0;
+	}
+	/* We found a legitimate error message, clear it and return it. */
+	error_message->errorAvailable = 0;
+	return error_message->errorString;
+}
+
+
+