38
|
1 /* See header file for license information. */
|
|
2
|
|
3 #include "tErrorLib.h"
|
|
4 #include <stdlib.h> /* for malloc */
|
|
5 #include <string.h>
|
|
6 #include <stdarg.h> /* for vasprintf */
|
|
7 #include <stdio.h> /* also for vasprintf and for printf family */
|
|
8 #include <stddef.h> /* size_t */
|
|
9
|
|
10 /**
|
|
11 * For string-only based usage, this implementation
|
|
12 * still expects an actual error number to be set.
|
|
13 * I am defining 1 as that error value. This might be changable,
|
|
14 * but it is untested. If you change this value, you must recompile
|
|
15 * the entire library. This can really be any integer except what
|
|
16 * TERROR_NOERROR_VALUE (in header) is set to.
|
|
17 */
|
|
18 #define TERROR_ERROR_VALUE 1
|
|
19
|
|
20 #ifdef DONT_USE_VASPRINT
|
|
21 #define TERROR_DEFAULT_STRING_LENGTH 128
|
|
22 /* Visual Studio doesn't define snprintf but _snprintf */
|
|
23 #ifdef _MSC_VER
|
|
24 #define snprintf _snprintf
|
|
25 #define vsnprintf _vsnprintf
|
|
26 #endif
|
|
27 #endif
|
|
28
|
|
29
|
|
30 #if defined(_WIN32) && !defined(__CYGWIN32__)
|
|
31 #include <windows.h>
|
|
32 #include <winbase.h> /* For CreateMutex(), LockFile() */
|
|
33
|
|
34 static void* Internal_CreateMutex()
|
|
35 {
|
|
36 return((void*)CreateMutex(NULL, FALSE, NULL));
|
|
37 }
|
|
38 static void Internal_DestroyMutex(void* mutex)
|
|
39 {
|
|
40 if(NULL != mutex)
|
|
41 {
|
|
42 CloseHandle( (HANDLE)mutex );
|
|
43 }
|
|
44 }
|
|
45 /* This will return true if locking is successful, false if not.
|
|
46 */
|
|
47 static int Internal_LockMutex(void* mutex)
|
|
48 {
|
|
49 return(
|
|
50 WaitForSingleObject(
|
|
51 (HANDLE)mutex,
|
|
52 INFINITE
|
|
53 ) != WAIT_FAILED
|
|
54 );
|
|
55 }
|
|
56 static void Internal_UnlockMutex(void* mutex)
|
|
57 {
|
|
58 ReleaseMutex(
|
|
59 (HANDLE)mutex
|
|
60 );
|
|
61 }
|
|
62 size_t Internal_PlatformPlatformGetThreadID(void)
|
|
63 {
|
|
64 return((size_t)GetCurrentThreadId());
|
|
65 }
|
|
66 #else /* Assuming POSIX...maybe not a good assumption. */
|
|
67 #include <pthread.h>
|
|
68 static void* Internal_CreateMutex()
|
|
69 {
|
|
70 int ret_val;
|
|
71 pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
|
|
72 if(NULL == m)
|
|
73 {
|
|
74 return NULL;
|
|
75 }
|
|
76 ret_val = pthread_mutex_init(m, NULL);
|
|
77 if(0 != ret_val)
|
|
78 {
|
|
79 free(m);
|
|
80 return NULL;
|
|
81 }
|
|
82 return((void*)m);
|
|
83 }
|
|
84 static void Internal_DestroyMutex(void* mutex)
|
|
85 {
|
|
86 if(NULL != mutex)
|
|
87 {
|
|
88 pthread_mutex_destroy((pthread_mutex_t*) (mutex));
|
|
89 free(mutex);
|
|
90 }
|
|
91 }
|
|
92 /* This will return true if locking is successful, false if not.
|
|
93 * (This is the opposite of pthread_mutex_lock which returns
|
|
94 * 0 for success.)
|
|
95 */
|
|
96 static int Internal_LockMutex(void* mutex)
|
|
97 {
|
|
98 return(
|
|
99 pthread_mutex_lock(
|
|
100 (pthread_mutex_t*)mutex
|
|
101 ) == 0
|
|
102 );
|
|
103 }
|
|
104 static void Internal_UnlockMutex(void* mutex)
|
|
105 {
|
|
106 pthread_mutex_unlock(
|
|
107 (pthread_mutex_t*)mutex
|
|
108 );
|
|
109 }
|
|
110
|
|
111 size_t Internal_PlatformGetThreadID()
|
|
112 {
|
|
113 /* Basically, we need to convert a pthread_t into an id number. */
|
|
114 return (size_t)pthread_self();
|
|
115 }
|
|
116 #endif
|
|
117
|
|
118
|
|
119 /**
|
|
120 * Copies a source string, potentially to a target string, and returns
|
|
121 * the pointer to the copied string.
|
|
122 * This function is a intended to be an efficient string copy function.
|
|
123 * It's purpose is to copy a string into a string with preallocated memory
|
|
124 * and avoid dynamic memory allocation if possible. If memory must
|
|
125 * be allocated, then the old string will be destroyed.
|
|
126 *
|
|
127 * This is only to be used where target_string was created with dynamic
|
|
128 * memory. This function will destroy the memory and allocate new memory
|
|
129 * if there is not enough space in the target string.
|
|
130 *
|
|
131 * @param target_string This is the string you would like to try
|
|
132 * to copy into. If there is not enough space, a new string will
|
|
133 * be created and the target_string will be freed. This string
|
|
134 * must have been created dynamically. This may be NULL if you
|
|
135 * wish for this function to dynamically create a new string
|
|
136 * for you.
|
|
137 *
|
|
138 * @param target_max_buffer_size This is a pointer that points to
|
|
139 * an address containing the size of the preallocated target_string.
|
|
140 * This size is the maximum buffer length which includes the '\\0'
|
|
141 * character as part of that count. This pointer may not be NULL.
|
|
142 * If you pass in NULL for the target_string (indicating you want
|
|
143 * a new string allocated for you), then the size should be set to 0.
|
|
144 * When the function completes, the size will be set to the new
|
|
145 * max buffer size of the string if the string needed to be reallocated.
|
|
146 *
|
|
147 * @param source_string This is the string you want to copy. If it's NULL,
|
|
148 * the target_string will have it's memory freed.
|
|
149 *
|
|
150 * @return Will return a pointer to the duplicated string. Be aware
|
|
151 * of several things:
|
|
152 * - The returned pointer address may not be the same address as the
|
|
153 * target string passed in (due to a possible reallocation).
|
|
154 * - If the pointer to the source and target string
|
|
155 * are the same, the pointer to the target string will be returned.
|
|
156 * - If the source string is NULL, the target string
|
|
157 * will be freed and will return NULL.
|
|
158 * - If an error occurs, NULL will be returned.
|
|
159 *
|
|
160 * Also note that the value at the address target_max_buffer_size points
|
|
161 * to will be filled with the new max buffer size for the string.
|
|
162 *
|
|
163 * Example:
|
|
164 * @code
|
|
165 *
|
|
166 * int main()
|
|
167 * {
|
|
168 * const char* original1 = "Hello World";
|
|
169 * const char* original2 = "Smaller";
|
|
170 * const char* original3 = "Good-Bye World";
|
|
171 * char* ret_val;
|
|
172 * char* target = NULL;
|
|
173 * size_t target_max_buffer_size = 0;
|
|
174 *
|
|
175 * ret_val = CopyDynamicString(target, &target_max_buffer_size, original1);
|
|
176 *
|
|
177 * if(ret_val)
|
|
178 * {
|
|
179 * fprintf(stderr, "Target is '%s' with max size = %d\n", ret_val, target_max_buffer_size);
|
|
180 * }
|
|
181 * else
|
|
182 * {
|
|
183 * fprintf(stderr, "Error in function\n");
|
|
184 * }
|
|
185 * target = ret_val;
|
|
186 *
|
|
187 * ret_val = CopyDynamicString(target, &target_max_buffer_size, original2);
|
|
188 * fprintf(stderr, "Target is '%s' with max size = %d\n", ret_val, target_max_buffer_size);
|
|
189 *
|
|
190 * target = ret_val; *
|
|
191 * ret_val = CopyDynamicString(target, &target_max_buffer_size, original3);
|
|
192 * fprintf(stderr, "Target is '%s' with max size = %d\n", ret_val, target_max_buffer_size);
|
|
193 *
|
|
194 * return 0;
|
|
195 * }
|
|
196 * @endcode
|
|
197 * This outputs:
|
|
198 * @code
|
|
199 * Target is 'Hello World' with max size = 12
|
|
200 * Target is 'Smaller' with max size = 12
|
|
201 * Target is 'Good-Bye World' with max size = 15
|
|
202 * @endcode
|
|
203 */
|
|
204 static char* Internal_CopyDynamicString(char* target_string, size_t* target_max_buffer_size, const char* source_string)
|
|
205 {
|
|
206 /* If the pointers are the same, no copy is needed. */
|
|
207 if(source_string == target_string)
|
|
208 {
|
|
209 /* I don't feel like asserting if the sizes are the same. */
|
|
210 /* Return 1 instead of 0 because maybe this isn't an error?
|
|
211 */
|
|
212 return target_string;
|
|
213 }
|
|
214
|
|
215 /* Make sure the size pointer is valid. */
|
|
216 if(NULL == target_max_buffer_size)
|
|
217 {
|
|
218 return NULL;
|
|
219 }
|
|
220
|
|
221 /* Yikes, if the string is NULL, should we make the target string NULL?
|
|
222 * For now, yes, we destroy the string. If you change this, realize that
|
|
223 * their is code that depends on this behavior.
|
|
224 */
|
|
225 if(NULL == source_string)
|
|
226 {
|
|
227 *target_max_buffer_size = 0;
|
|
228 free(target_string);
|
|
229 target_string = NULL;
|
|
230 return NULL;
|
|
231 }
|
|
232
|
|
233 /* If target_string is NULL, the *target_max_buffer_size should also be 0.
|
|
234 * Technically, the user should set this and this would be an error,
|
|
235 * but I'll be nice for now. An alternate implementation might suggest
|
|
236 * that the size would be the desired size the user wants for a new string.
|
|
237 */
|
|
238 if( (NULL == target_string) && (0 != *target_max_buffer_size) )
|
|
239 {
|
|
240 *target_max_buffer_size = 0;
|
|
241 }
|
|
242
|
|
243 /* If there is not enough preallocated memory in the target string,
|
|
244 * then we need to reallocate enough memory.
|
|
245 */
|
|
246 if( *target_max_buffer_size < (strlen(source_string) + 1) )
|
|
247 {
|
|
248 *target_max_buffer_size = 0;
|
|
249 if(NULL != target_string)
|
|
250 {
|
|
251 free(target_string);
|
|
252 }
|
|
253 target_string = (char*)calloc( (strlen(source_string) + 1), sizeof(char) );
|
|
254 if(NULL == target_string)
|
|
255 {
|
|
256 return NULL;
|
|
257 }
|
|
258 *target_max_buffer_size = strlen(source_string) + 1;
|
|
259 }
|
|
260
|
|
261 /* At this point, there should be enough preallocated
|
|
262 * memory to call strncpy.
|
|
263 */
|
|
264 strncpy(target_string, source_string, *target_max_buffer_size);
|
|
265
|
|
266 return target_string;
|
|
267 }
|
|
268
|
|
269 /**
|
|
270 * This is a structure that contains everything needed for an
|
|
271 * error message entry (per thread). The linked list stuff
|
|
272 * is fused in with it because I didn't want to write an entire
|
|
273 * linked list class.
|
|
274 */
|
|
275 typedef struct TErrorMessageStructType
|
|
276 {
|
|
277 size_t threadID; /** ThreadID for associated message. */
|
|
278 int errorAvailable; /** 1 if an error has been set and not been checked. */
|
|
279 int errorNumber; /**< For the user error number. */
|
|
280 char* errorString; /**< For the user error message. */
|
|
281 size_t errorMaxStringLength; /**< Max size of string buffer including \\0. */
|
|
282 struct TErrorMessageStructType* nextItem; /**< Pointer to next error message in list. */
|
|
283 } TErrorMessage;
|
|
284
|
|
285 /**
|
|
286 * This is a private struct that contains all private data for an
|
|
287 * ErrorPool. Currently it is a linked list containing all error message
|
|
288 * structs for every thread.
|
|
289 */
|
|
290 typedef struct
|
|
291 {
|
|
292 TErrorMessage* errorMessageListHead; /**< Head of the error list. */
|
|
293 TErrorMessage* lastErrorMessage; /**< Points to the last set element in the list for GetLastError. */
|
|
294 /* Mutex */
|
|
295 } TErrorPoolOpaqueData;
|
|
296
|
|
297 /**
|
|
298 * This is a private helper function that creates a new TErrorMessage
|
|
299 * and initializes all its values.
|
|
300 * @return Returns a pointer to a newly allocated and initialized
|
|
301 * TErrorMessage or NULL on failure.
|
|
302 */
|
|
303 static TErrorMessage* Internal_CreateErrorMessageStructure()
|
|
304 {
|
|
305 TErrorMessage* new_message;
|
|
306 /* Use calloc to create a fully cleared structure,
|
|
307 * so I don't have to set/clear each member.
|
|
308 */
|
|
309 new_message = (TErrorMessage*)calloc(1, sizeof(TErrorMessage));
|
|
310 if(NULL == new_message)
|
|
311 {
|
|
312 /* Very bad, but not sure what to do. */
|
|
313 return NULL;
|
|
314 }
|
|
315 new_message->errorNumber = TERROR_NOERROR_VALUE;
|
|
316 return new_message;
|
|
317 }
|
|
318
|
|
319 /**
|
|
320 * This is a private helper function that frees a TErrorMessage.
|
|
321 *
|
|
322 * @param err_mesg The pointer to the TErrorMessage to be freed.
|
|
323 */
|
|
324 static void Internal_FreeErrorMessageStructure(TErrorMessage* err_mesg)
|
|
325 {
|
|
326 if(NULL == err_mesg)
|
|
327 {
|
|
328 return;
|
|
329 }
|
|
330 if(NULL != err_mesg->errorString)
|
|
331 {
|
|
332 free(err_mesg->errorString);
|
|
333 err_mesg->errorString = NULL;
|
|
334 }
|
|
335 err_mesg->nextItem = NULL;
|
|
336 free(err_mesg);
|
|
337 }
|
|
338
|
|
339 /**
|
|
340 * This is a private helper function that will search the error pool
|
|
341 * for the last set error message structure in the Linked list.
|
|
342 * If the last error message was on a different thread, the error
|
|
343 * data will be copied to the current thread's memory and the
|
|
344 * lastErrorMessage pointer will be set to the current thread's message.
|
|
345 * (This is because I expect this message to be marked as cleared/read.)
|
|
346 * This function does its own mutex locking.
|
|
347 *
|
|
348 * @param err_pool The error pool to be used.
|
|
349 * @return Returns the a pointer to the TErrorMessage if found,
|
|
350 * NULL if not found.
|
|
351 */
|
|
352 static TErrorMessage* Internal_GetLastError(TErrorPool* err_pool)
|
|
353 {
|
|
354 size_t thread_id;
|
|
355 TErrorMessage* current_thread_err_mesg;
|
|
356 TErrorPoolOpaqueData* err_pool_data;
|
|
357
|
|
358 thread_id = Internal_PlatformGetThreadID();
|
|
359
|
|
360 Internal_LockMutex(err_pool->mutexLock);
|
|
361
|
|
362 err_pool_data = err_pool->opaqueData;
|
|
363
|
|
364 if(NULL == err_pool_data->errorMessageListHead)
|
|
365 {
|
|
366 Internal_UnlockMutex(err_pool->mutexLock);
|
|
367 return NULL;
|
|
368 }
|
|
369
|
|
370 /* I think this is actually an assertion failure.
|
|
371 * I do the check here so I don't have to keep checking below.
|
|
372 */
|
|
373 if(NULL == err_pool_data->lastErrorMessage)
|
|
374 {
|
|
375 Internal_UnlockMutex(err_pool->mutexLock);
|
|
376 return NULL;
|
|
377 }
|
|
378
|
|
379 /* We need to determine if the lastMessage pointer is pointing
|
|
380 * to data on the current thread. If it is we can just return it.
|
|
381 * Otherwise, we need to copy the message to the current thread's
|
|
382 * error message memory area.
|
|
383 * We should also update the lastMessage pointer to point
|
|
384 * to this message since it will likely be marked cleared once read.
|
|
385 */
|
|
386 if(thread_id == err_pool_data->lastErrorMessage->threadID)
|
|
387 {
|
|
388 /* Not copy is needed. The last error message already
|
|
389 * points to the memory on the current thread.
|
|
390 * We can short-circuit and return.
|
|
391 */
|
|
392 Internal_UnlockMutex(err_pool->mutexLock);
|
|
393 return err_pool_data->lastErrorMessage;
|
|
394 }
|
|
395
|
|
396 /* Sigh, I really should have a dedicated linked list structure,
|
|
397 * but I don't feel like writing it right now.
|
|
398 */
|
|
399 for(current_thread_err_mesg = err_pool_data->errorMessageListHead; current_thread_err_mesg != NULL; current_thread_err_mesg = current_thread_err_mesg->nextItem)
|
|
400 {
|
|
401 /* First find the message (memory) for the current thread. */
|
|
402 if(thread_id == current_thread_err_mesg->threadID)
|
|
403 {
|
|
404 /* Now we need to copy the message data from the lastErrorMessage
|
|
405 * to this thread's message (memory).
|
|
406 */
|
|
407 current_thread_err_mesg->errorNumber = err_pool_data->lastErrorMessage->errorNumber;
|
|
408 current_thread_err_mesg->errorAvailable = err_pool_data->lastErrorMessage->errorAvailable;
|
|
409 /* This will copy the string and set the new errorMaxStringLength as needed. */
|
|
410 current_thread_err_mesg->errorString = Internal_CopyDynamicString(current_thread_err_mesg->errorString, ¤t_thread_err_mesg->errorMaxStringLength, err_pool_data->lastErrorMessage->errorString);
|
|
411
|
|
412
|
|
413 /* Finally, change the last error message to point to
|
|
414 * the current thread since I expect the message to be
|
|
415 * marked cleared and we don't want to accidentally refetched
|
|
416 * the stale, uncleared entry.
|
|
417 */
|
|
418 err_pool_data->lastErrorMessage = current_thread_err_mesg;
|
|
419
|
|
420 Internal_UnlockMutex(err_pool->mutexLock);
|
|
421 return current_thread_err_mesg;
|
|
422 }
|
|
423 }
|
|
424 Internal_UnlockMutex(err_pool->mutexLock);
|
|
425 return NULL;
|
|
426 }
|
|
427
|
|
428 /**
|
|
429 * This is a private helper function that will search the error pool
|
|
430 * for an error message structure in the Linked list (by thread ID)
|
|
431 * and return the pointer if found. This function does its own mutex
|
|
432 * locking.
|
|
433 * @param err_pool The error pool to be used.
|
|
434 * @return Returns the a pointer to the TErrorMessage if found,
|
|
435 * NULL if not found.
|
|
436 */
|
|
437 static TErrorMessage* Internal_GetErrorOnCurrentThread(TErrorPool* err_pool)
|
|
438 {
|
|
439 size_t thread_id;
|
|
440 TErrorMessage* current_err_mesg;
|
|
441 TErrorPoolOpaqueData* err_pool_data;
|
|
442
|
|
443 thread_id = Internal_PlatformGetThreadID();
|
|
444
|
|
445 Internal_LockMutex(err_pool->mutexLock);
|
|
446
|
|
447 err_pool_data = err_pool->opaqueData;
|
|
448
|
|
449 if(NULL == err_pool_data->errorMessageListHead)
|
|
450 {
|
|
451 Internal_UnlockMutex(err_pool->mutexLock);
|
|
452 return NULL;
|
|
453 }
|
|
454
|
|
455 /* Sigh, I really should have a dedicated linked list structure,
|
|
456 * but I don't feel like writing it right now.
|
|
457 */
|
|
458 for(current_err_mesg = err_pool_data->errorMessageListHead; current_err_mesg != NULL; current_err_mesg = current_err_mesg->nextItem)
|
|
459 {
|
|
460 if(thread_id == current_err_mesg->threadID)
|
|
461 {
|
|
462 Internal_UnlockMutex(err_pool->mutexLock);
|
|
463 return current_err_mesg;
|
|
464 }
|
|
465 }
|
|
466 Internal_UnlockMutex(err_pool->mutexLock);
|
|
467 return NULL;
|
|
468 }
|
|
469
|
|
470 /**
|
|
471 * Given a specific TErrorMessage*, will set the lastErrorMessage pointer to
|
|
472 * the provided error message.
|
|
473 * This function locks.
|
|
474 *
|
|
475 * @param err_pool The error pool to be used.
|
|
476 * @param error_message The error message to set the lastErrorMessage pointer to
|
|
477 */
|
|
478 static void Internal_SetLastErrorMessagePointerToErrorMessage(TErrorPool* err_pool, TErrorMessage* error_message)
|
|
479 {
|
|
480 TErrorPoolOpaqueData* err_pool_data;
|
|
481 Internal_LockMutex(err_pool->mutexLock);
|
|
482 err_pool_data = err_pool->opaqueData;
|
|
483 err_pool_data->lastErrorMessage = error_message;
|
|
484 Internal_UnlockMutex(err_pool->mutexLock);
|
|
485 }
|
|
486
|
|
487
|
|
488 /**
|
|
489 * This is a private helper function that creates a new error message
|
|
490 * structure for the current thread.
|
|
491 * This currently does not check if an error already exists
|
|
492 * before creating a new entry. Call GetErrorOnCurrentThread first
|
|
493 * to make sure nothing exists or duplicate entries will be created.
|
|
494 * This function does its own mutex locking.
|
|
495 *
|
|
496 * @param err_pool The error pool to be used.
|
|
497 * @return Returns the a pointer to the TErrorMessage if found,
|
|
498 * NULL if there was an allocation error.
|
|
499 */
|
|
500 static TErrorMessage* Internal_CreateErrorOnCurrentThread(TErrorPool* err_pool)
|
|
501 {
|
|
502 TErrorMessage* new_err_mesg;
|
|
503 TErrorPoolOpaqueData* err_pool_data;
|
|
504
|
|
505 new_err_mesg = Internal_CreateErrorMessageStructure();
|
|
506 if(NULL == new_err_mesg)
|
|
507 {
|
|
508 /* Serious problem, not sure what to do. */
|
|
509 return NULL;
|
|
510 }
|
|
511 /* Copy the thread id so we can distinguish between entries. */
|
|
512 new_err_mesg->threadID = Internal_PlatformGetThreadID();
|
|
513
|
|
514 Internal_LockMutex(err_pool->mutexLock);
|
|
515
|
|
516 err_pool_data = err_pool->opaqueData;
|
|
517 /* Add the new message to the top of the list by making
|
|
518 * its next pointer point to the head of the current list.
|
|
519 * (A formal linked list implementation would make this feel
|
|
520 * less hacky.)
|
|
521 * This also (should) handle the case where errorMessageListHead
|
|
522 * is NULL.
|
|
523 */
|
|
524 new_err_mesg->nextItem = err_pool_data->errorMessageListHead;
|
|
525 /* Now set the head of the list to the new message.
|
|
526 */
|
|
527 err_pool_data->errorMessageListHead = new_err_mesg;
|
|
528
|
|
529 Internal_UnlockMutex(err_pool->mutexLock);
|
|
530
|
|
531 return new_err_mesg;
|
|
532 }
|
|
533
|
|
534 /**
|
|
535 * This is a private helper function that will clean up all the
|
|
536 * error message structures in the list. This function does its
|
|
537 * own locking.
|
|
538 * @param err_pool The error pool to be used.
|
|
539 */
|
|
540 static void Internal_FreeErrorMessageList(TErrorPool* err_pool)
|
|
541 {
|
|
542 TErrorMessage* current_message = NULL;
|
|
543 TErrorMessage* next_message = NULL;
|
|
544 TErrorPoolOpaqueData* err_pool_data;
|
|
545
|
|
546 Internal_LockMutex(err_pool->mutexLock);
|
|
547
|
|
548 err_pool_data = err_pool->opaqueData;
|
|
549
|
|
550 if(NULL == err_pool_data->errorMessageListHead)
|
|
551 {
|
|
552 Internal_UnlockMutex(err_pool->mutexLock);
|
|
553 return;
|
|
554 }
|
|
555
|
|
556 /* Sigh, I really should have a dedicated linked list structure,
|
|
557 * but I don't feel like writing it right now.
|
|
558 */
|
|
559 for(current_message = err_pool_data->errorMessageListHead;
|
|
560 current_message != NULL;
|
|
561 current_message = next_message
|
|
562 )
|
|
563 {
|
|
564 next_message = current_message->nextItem;
|
|
565 Internal_FreeErrorMessageStructure(current_message);
|
|
566 }
|
|
567 err_pool_data->errorMessageListHead = NULL;
|
|
568 err_pool_data->lastErrorMessage = NULL;
|
|
569
|
|
570 Internal_UnlockMutex(err_pool->mutexLock);
|
|
571 }
|
|
572
|
|
573 /*
|
|
574 * API functions start below.
|
|
575 *
|
|
576 */
|
|
577
|
|
578
|
|
579 void TError_DeleteEntryOnCurrentThread(TErrorPool* err_pool)
|
|
580 {
|
|
581 TErrorMessage* prev_message = NULL;
|
|
582 TErrorMessage* current_message = NULL;
|
|
583 TErrorMessage* next_message = NULL;
|
|
584 size_t thread_id;
|
|
585 TErrorPoolOpaqueData* err_pool_data;
|
|
586
|
|
587 thread_id = Internal_PlatformGetThreadID();
|
|
588
|
|
589 Internal_LockMutex(err_pool->mutexLock);
|
|
590
|
|
591 err_pool_data = err_pool->opaqueData;
|
|
592
|
|
593 if(NULL == err_pool_data->errorMessageListHead)
|
|
594 {
|
|
595 Internal_UnlockMutex(err_pool->mutexLock);
|
|
596 return;
|
|
597 }
|
|
598
|
|
599 /* Sigh, I really should have a dedicated linked list structure,
|
|
600 * but I don't feel like writing it right now.
|
|
601 */
|
|
602 for(current_message = err_pool_data->errorMessageListHead;
|
|
603 current_message != NULL;
|
|
604 /* I'm not going to increment here because I
|
|
605 * may delete the item below which would probably
|
|
606 * cause bad things to happen here.
|
|
607 */
|
|
608 /* current_message = current_message->nextItem */
|
|
609 )
|
|
610 {
|
|
611 next_message = current_message->nextItem;
|
|
612
|
|
613 if(thread_id == current_message->threadID)
|
|
614 {
|
|
615 /* Special case, current is only item in list:
|
|
616 * Both next and prev are NULL in this case.
|
|
617 * We should delete the item and set the errorMessageListHead
|
|
618 * to NULL.
|
|
619 */
|
|
620 if((NULL == prev_message) && (NULL == next_message))
|
|
621 {
|
|
622 Internal_FreeErrorMessageStructure(current_message);
|
|
623 current_message = NULL;
|
|
624 err_pool_data->errorMessageListHead = NULL;
|
|
625 err_pool_data->lastErrorMessage = NULL;
|
|
626 }
|
|
627 /* Special case, current is at head:
|
|
628 * Prev is NULL but next is not NULL in this case.
|
|
629 * We should delete the item and set the errorMessageListHead
|
|
630 * to point to next.
|
|
631 * (The code for the above case would probably work for
|
|
632 * this case too, but for clarity, this remains.)
|
|
633 */
|
|
634 else if(NULL == prev_message)
|
|
635 {
|
|
636 /* If the current message happened to be the last message
|
|
637 * set, we need to change the lastErrorMessage pointer
|
|
638 * so it is not dangling.
|
|
639 */
|
|
640 if(current_message == err_pool_data->lastErrorMessage)
|
|
641 {
|
|
642 err_pool_data->lastErrorMessage = NULL;
|
|
643 }
|
|
644 Internal_FreeErrorMessageStructure(current_message);
|
|
645 current_message = NULL;
|
|
646 err_pool_data->errorMessageListHead = next_message;
|
|
647 }
|
|
648 /* Special case, current is at tail.
|
|
649 * Prev is not NULL, but next is NULL in this case.
|
|
650 * We should delete the item and set prev->next to NULL.
|
|
651 */
|
|
652 else if(NULL == next_message)
|
|
653 {
|
|
654 /* If the current message happened to be the last message
|
|
655 * set, we need to change the lastErrorMessage pointer
|
|
656 * so it is not dangling.
|
|
657 */
|
|
658 if(current_message == err_pool_data->lastErrorMessage)
|
|
659 {
|
|
660 err_pool_data->lastErrorMessage = NULL;
|
|
661 }
|
|
662 Internal_FreeErrorMessageStructure(current_message);
|
|
663 current_message = NULL;
|
|
664 prev_message->nextItem = NULL;
|
|
665 }
|
|
666 /* Normal case, current is somewhere in the middle of the list.
|
|
667 * The item should be deleted and
|
|
668 * the prev_message->next should connect to
|
|
669 * the next_message.
|
|
670 */
|
|
671 else
|
|
672 {
|
|
673 /* If the current message happened to be the last message
|
|
674 * set, we need to change the lastErrorMessage pointer
|
|
675 * so it is not dangling.
|
|
676 */
|
|
677 if(current_message == err_pool_data->lastErrorMessage)
|
|
678 {
|
|
679 err_pool_data->lastErrorMessage = NULL;
|
|
680 }
|
|
681 Internal_FreeErrorMessageStructure(current_message);
|
|
682 current_message = NULL;
|
|
683 prev_message->nextItem = next_message;
|
|
684 }
|
|
685 }
|
|
686 /* It's not this thread, so increment everything for the next loop. */
|
|
687 else
|
|
688 {
|
|
689 prev_message = current_message;
|
|
690 current_message = next_message;
|
|
691 }
|
|
692 } /* End for-loop */
|
|
693
|
|
694 Internal_UnlockMutex(err_pool->mutexLock);
|
|
695 }
|
|
696
|
|
697
|
|
698 void TError_GetLinkedVersion(TErrorVersion* ver)
|
|
699 {
|
|
700 /* Check the pointer */
|
|
701 if(NULL == ver)
|
|
702 {
|
|
703 /* Do nothing */
|
|
704 return;
|
|
705 }
|
|
706 ver->major = TERROR_MAJOR_VERSION;
|
|
707 ver->minor = TERROR_MINOR_VERSION;
|
|
708 ver->patch = TERROR_PATCH_VERSION;
|
|
709 }
|
|
710
|
|
711
|
|
712 #if 0
|
|
713 /* This is for global initialization, not pool initialization. */
|
|
714 int TError_Init()
|
|
715 {
|
|
716 /* initialize platform? */
|
|
717 /* initialize mutexes? */
|
|
718
|
|
719 }
|
|
720 #endif
|
|
721
|
|
722 TErrorPool* TError_CreateErrorPool()
|
|
723 {
|
|
724 TErrorPool* err_pool;
|
|
725 TErrorPoolOpaqueData* err_pool_data;
|
|
726
|
|
727 err_pool = (TErrorPool*)calloc(1, sizeof(TErrorPool));
|
|
728 if(NULL == err_pool)
|
|
729 {
|
|
730 /* Very bad, but not sure what to do here. */
|
|
731 return NULL;
|
|
732 }
|
|
733 err_pool_data = (TErrorPoolOpaqueData*)calloc(1, sizeof(TErrorPoolOpaqueData));
|
|
734 if(NULL == err_pool_data)
|
|
735 {
|
|
736 /* Very bad, but not sure what to do here. */
|
|
737 free(err_pool);
|
|
738 return NULL;
|
|
739 }
|
|
740
|
|
741 /* Create mutex */
|
|
742 err_pool->mutexLock = Internal_CreateMutex();
|
|
743
|
|
744 if(NULL == err_pool->mutexLock)
|
|
745 {
|
|
746 /* Very bad, but not sure what to do here. */
|
|
747 free(err_pool_data);
|
|
748 free(err_pool);
|
|
749 return NULL;
|
|
750 }
|
|
751
|
|
752 /* Attach the opaque data to the error pool. */
|
|
753 err_pool->opaqueData = err_pool_data;
|
|
754
|
|
755 /* The OpaqueData will hold the error message list, but it is
|
|
756 * allowed to be NULL for an empty list so we don't have to allocate
|
|
757 * it here.
|
|
758 */
|
|
759
|
|
760 return err_pool;
|
|
761 }
|
|
762
|
|
763 /* There better not be any contention when this is called. */
|
|
764 void TError_FreeErrorPool(TErrorPool* err_pool)
|
|
765 {
|
|
766 if(NULL == err_pool)
|
|
767 {
|
|
768 return;
|
|
769 }
|
|
770 /* Free all the error messages for each thread.
|
|
771 * This locks and unlocks as it needs.
|
|
772 */
|
|
773 Internal_FreeErrorMessageList(err_pool);
|
|
774
|
|
775 /* Free opaque data structure. */
|
|
776 free(err_pool->opaqueData);
|
|
777
|
|
778 /* Delete mutex after all locking functions. */
|
|
779 Internal_DestroyMutex(err_pool->mutexLock);
|
|
780
|
|
781 /* Free main data structure. */
|
|
782 free(err_pool);
|
|
783 }
|
|
784
|
|
785 void TError_SetError(TErrorPool* err_pool, int err_num, const char* err_str, ...)
|
|
786 {
|
|
787 va_list argp;
|
|
788 va_start(argp, err_str);
|
|
789 TError_SetErrorv(err_pool, err_num, err_str, argp);
|
|
790 va_end(argp);
|
|
791 }
|
|
792
|
|
793 void TError_SetErrorv(TErrorPool* err_pool, int err_num, const char* err_str, va_list argp)
|
|
794 {
|
|
795 TErrorMessage* error_message;
|
|
796 int ret_num_chars;
|
|
797
|
|
798 if(NULL == err_pool)
|
|
799 {
|
|
800 return;
|
|
801 }
|
|
802
|
|
803 error_message = Internal_GetErrorOnCurrentThread(err_pool);
|
|
804 /* If no error message was found, that means we must allocate
|
|
805 * a new entry for this entry.
|
|
806 */
|
|
807 if(NULL == error_message)
|
|
808 {
|
|
809 error_message = Internal_CreateErrorOnCurrentThread(err_pool);
|
|
810 /* If this fails, this is bad...not sure what to do though. */
|
|
811 if(NULL == error_message)
|
|
812 {
|
|
813 return;
|
|
814 }
|
|
815 }
|
|
816
|
|
817 /*
|
|
818 * I don't think I have to lock here. The [Get|Create]ErrorOnCurrentThread
|
|
819 * functions lock err_pool as they need access. Here, I don't access
|
|
820 * err_pool (which is shared) and error_message should be unique for
|
|
821 * each thread so I don't think there is any contention. (Remember that
|
|
822 * simultaneous calls to SetError would only happen if they are in
|
|
823 * different threads.)
|
|
824 * There *might* be a problem with library calls (strncpy, calloc).
|
|
825 * I'm not sure if the various platforms are reentrant.
|
|
826 * I guess for now, I will assume they won't bite me.
|
|
827 */
|
|
828
|
|
829 /* If the err_str is NULL, we need to free our current string
|
|
830 * for consistency. More aggressive optimizations to hold the
|
|
831 * memory might be considered in the future.
|
|
832 */
|
|
833 if(NULL == err_str)
|
|
834 {
|
|
835 if(NULL != error_message->errorString)
|
|
836 {
|
|
837 free(error_message->errorString);
|
|
838 error_message->errorString = NULL;
|
|
839 error_message->errorMaxStringLength = 0;
|
|
840 }
|
|
841 }
|
|
842 /* Else, copy the string */
|
|
843 else
|
|
844 {
|
|
845 /* I am using vasprintf which is a GNU extension so it is not
|
|
846 * portable. However, vasprintf makes certain things possible
|
|
847 * which would not be otherwise, which is the reason for my
|
|
848 * use. The main benefit of asprintf/vasprintf is that you can
|
|
849 * create a string using printf style formatters without
|
|
850 * worrying about the buffer size. sprintf should never be
|
|
851 * used because of potential buffer overflows. snprintf
|
|
852 * is safer, but you are limited to a fixed size string
|
|
853 * which from time-to-time, I have exceeded unless you make
|
|
854 * the number really big.
|
|
855 * Furthermore, snprintf itself is not currently terribly portable
|
|
856 * because it is specified only for C99 which some compilers
|
|
857 * still have not have embraced.
|
|
858 * If you can't use the vasprintf implementation,
|
|
859 * you must add -DDONT_USE_VASPRINTF to your compile flags.
|
|
860 */
|
|
861 #ifdef DONT_USE_VASPRINTF
|
|
862 /* This implementation uses vsnprintf instead of
|
|
863 * vasprintf. It is strongly recommended you use
|
|
864 * the vasprintf implmententation instead.
|
|
865 * Never use vsprintf unless you like
|
|
866 * buffer overflows and security exploits.
|
|
867 */
|
|
868
|
|
869 /* If the string was set to NULL, we must reallocate memory first. */
|
|
870 if(NULL == error_message->errorString)
|
|
871 {
|
|
872 error_message->errorString = (char*)calloc(TERROR_DEFAULT_STRING_LENGTH, sizeof(char));
|
|
873 if(NULL == error_message->errorString)
|
|
874 {
|
|
875 /* Very bad...what should I do?
|
|
876 */
|
|
877 error_message->errorMaxStringLength = 0;
|
|
878 }
|
|
879 else
|
|
880 {
|
|
881 error_message->errorMaxStringLength = TERROR_DEFAULT_STRING_LENGTH;
|
|
882 }
|
|
883 }
|
|
884 /* Because of the "Very Bad" situation directly above,
|
|
885 * I need to check again to make sure the string isn't NULL.
|
|
886 * This will let the very bad situation continue on so va_end
|
|
887 * can be called and the error_number still has a chance to be set.
|
|
888 */
|
|
889 if(NULL != error_message->errorString)
|
|
890 {
|
|
891 ret_num_chars = vsnprintf(error_message->errorString,
|
|
892 error_message->errorMaxStringLength,
|
|
893 err_str,
|
|
894 argp
|
|
895 );
|
|
896 }
|
|
897
|
|
898 #else /* DONT_USE_VASPRINTF */
|
|
899 /* You might be wondering why the #ifdef logic assumes
|
|
900 * asprintf is available instead of requiring an explicit
|
|
901 * #define for that. The reason is because asprintf is the
|
|
902 * better option and I want you to realize that you are not
|
|
903 * using it. Typically, nobody knows or understands the build
|
|
904 * system and/or files get copied into new projects with a
|
|
905 * entirely new build system, so it is easy to forget to
|
|
906 * add a -D flag. So if you compile without asprintf,
|
|
907 * you are encouraged to explicitly know this.
|
|
908 */
|
|
909 /* There may be a slight performance advantage to using snprintf
|
|
910 * over asprintf depending how asprintf is written. But this
|
|
911 * implementation will completely destroy and reallocate a
|
|
912 * string regardless if a string is set to NULL, so there will
|
|
913 * actually be no performance gains for these cases.
|
|
914 * (This could be optimized but some additional bookkeeping
|
|
915 * might be needed which might not be worth the effort and
|
|
916 * code clutter.)
|
|
917 * As for memory allocation safety, because new messages for
|
|
918 * different threads must be allocated dynamically, there is no
|
|
919 * way for this library to use purely static memory.
|
|
920 * So I don't believe there is anything to be gained using
|
|
921 * snprintf over asprintf and you lose out on arbitrary lengthed
|
|
922 * error messages.
|
|
923 * If memory allocation must be minimized, I recommend just
|
|
924 * using the error number interface by itself which
|
|
925 * will always keep the strings at NULL, and don't mess
|
|
926 * with the asprintf/sprintf code.
|
|
927 */
|
|
928
|
|
929 ret_num_chars = vasprintf(&error_message->errorString, err_str, argp);
|
|
930 /* vasprintf returns -1 as an error */
|
|
931 if(-1 == ret_num_chars)
|
|
932 {
|
|
933 /* Very bad, but not sure what to do here. */
|
|
934 if(NULL != error_message->errorString)
|
|
935 {
|
|
936 free(error_message->errorString);
|
|
937 error_message->errorString = NULL;
|
|
938 error_message->errorMaxStringLength = 0;
|
|
939 /* Don't return here. Still need to va_end, and
|
|
940 * there is a chance that the err_num might work.
|
|
941 * Plus the availability needs to be set.
|
|
942 */
|
|
943 }
|
|
944 }
|
|
945 /* else vasprint returns the number of characters in the string
|
|
946 * not including the \0 character.
|
|
947 */
|
|
948 else
|
|
949 {
|
|
950 /* I actually don't know how much memory vasprintf allocated
|
|
951 * for the string. But it is at least ret_num_chars+1, so
|
|
952 * I will use that as my max string length (which is
|
|
953 * mainly used by CopyDynamicString() for efficiency
|
|
954 * which is becoming less used in this code).
|
|
955 */
|
|
956 error_message->errorMaxStringLength = ret_num_chars+1;
|
|
957 }
|
|
958 #endif /* DONT_USE_VASPRINTF */
|
|
959 }
|
|
960
|
|
961 /* I'm allowing for a user to explicitly clear an error message by
|
|
962 * clearing both attributes.
|
|
963 */
|
|
964 if((TERROR_NOERROR_VALUE == err_num) && (NULL == err_str))
|
|
965 {
|
|
966 error_message->errorNumber = TERROR_NOERROR_VALUE;
|
|
967 error_message->errorAvailable = 0;
|
|
968 }
|
|
969 /* This is the normal case, copy the error number
|
|
970 * and mark the error as unread.
|
|
971 */
|
|
972 else
|
|
973 {
|
|
974 error_message->errorNumber = err_num;
|
|
975 error_message->errorAvailable = 1;
|
|
976 }
|
|
977
|
|
978 /* Now that the data is set, we also want to denote that this
|
|
979 * thread is the last error message. We need to lock for this
|
|
980 * since the lastError pointer is shared across threads.
|
|
981 */
|
|
982 Internal_SetLastErrorMessagePointerToErrorMessage(err_pool, error_message);
|
|
983 }
|
|
984
|
|
985 void TError_SetErrorNoFormat(TErrorPool* err_pool, int err_num, const char* err_str)
|
|
986 {
|
|
987 TErrorMessage* error_message;
|
|
988 if(NULL == err_pool)
|
|
989 {
|
|
990 return;
|
|
991 }
|
|
992
|
|
993 error_message = Internal_GetErrorOnCurrentThread(err_pool);
|
|
994 /* If no error message was found, that means we must allocate
|
|
995 * a new entry for this entry.
|
|
996 */
|
|
997 if(NULL == error_message)
|
|
998 {
|
|
999 error_message = Internal_CreateErrorOnCurrentThread(err_pool);
|
|
1000 /* If this fails, this is bad...not sure what to do though. */
|
|
1001 if(NULL == error_message)
|
|
1002 {
|
|
1003 return;
|
|
1004 }
|
|
1005 }
|
|
1006
|
|
1007 /*
|
|
1008 * I don't think I have to lock here. The [Get|Create]ErrorOnCurrentThread
|
|
1009 * functions lock err_pool as they need access. Here, I don't access
|
|
1010 * err_pool (which is shared) and error_message should be unique for
|
|
1011 * each thread so I don't think there is any contention. (Remember that
|
|
1012 * simultaneous calls to SetError would only happen if they are in
|
|
1013 * different threads.)
|
|
1014 * There *might* be a problem with library calls (strncpy, calloc).
|
|
1015 * I'm not sure if the various platforms are reentrant.
|
|
1016 * I guess for now, I will assume they won't bite me.
|
|
1017 */
|
|
1018 error_message->errorNumber = err_num;
|
|
1019 /* This will copy the string and set the new errorMaxStringLength as needed. */
|
|
1020 error_message->errorString = Internal_CopyDynamicString(error_message->errorString, &error_message->errorMaxStringLength, err_str);
|
|
1021 /* I'm allowing for a user to explicitly clear an error message by
|
|
1022 * clearing both attributes.
|
|
1023 */
|
|
1024 if((TERROR_NOERROR_VALUE == err_num) && (NULL == err_str))
|
|
1025 {
|
|
1026 error_message->errorAvailable = 0;
|
|
1027 }
|
|
1028 else
|
|
1029 {
|
|
1030 error_message->errorAvailable = 1;
|
|
1031 }
|
|
1032
|
|
1033 /* Now that the data is set, we also want to denote that this
|
|
1034 * thread is the last error message. We need to lock for this
|
|
1035 * since the lastError pointer is shared across threads.
|
|
1036 */
|
|
1037 Internal_SetLastErrorMessagePointerToErrorMessage(err_pool, error_message);
|
|
1038 }
|
|
1039
|
|
1040 void TError_SetErrorNum(TErrorPool* err_pool, int err_num)
|
|
1041 {
|
|
1042 TError_SetErrorNoFormat(err_pool, err_num, NULL);
|
|
1043 }
|
|
1044
|
|
1045 void TError_SetErrorStr(TErrorPool* err_pool, const char* err_str, ...)
|
|
1046 {
|
|
1047 va_list argp;
|
|
1048 va_start(argp, err_str);
|
|
1049 if(NULL == err_str)
|
|
1050 {
|
|
1051 TError_SetErrorv(err_pool, TERROR_NOERROR_VALUE, err_str, argp);
|
|
1052 }
|
|
1053 else
|
|
1054 {
|
|
1055 TError_SetErrorv(err_pool, TERROR_ERROR_VALUE, err_str, argp);
|
|
1056 }
|
|
1057 va_end(argp);
|
|
1058 }
|
|
1059
|
|
1060 void TError_SetErrorStrv(TErrorPool* err_pool, const char* err_str, va_list argp)
|
|
1061 {
|
|
1062 if(NULL == err_str)
|
|
1063 {
|
|
1064 TError_SetErrorv(err_pool, TERROR_NOERROR_VALUE, err_str, argp);
|
|
1065 }
|
|
1066 else
|
|
1067 {
|
|
1068 TError_SetErrorv(err_pool, TERROR_ERROR_VALUE, err_str, argp);
|
|
1069 }
|
|
1070 }
|
|
1071
|
|
1072 /* If a NULL string is set, then it is presumed no error actually occurred
|
|
1073 * and this is a reset. So the err_num will be implicitly set to 0. Otherwise
|
|
1074 * the err_num will be set to 1 (for internal consistency and conventions).
|
|
1075 */
|
|
1076 void TError_SetErrorStrNoFormat(TErrorPool* err_pool, const char* err_str)
|
|
1077 {
|
|
1078 if(NULL == err_str)
|
|
1079 {
|
|
1080 TError_SetErrorNoFormat(err_pool, TERROR_NOERROR_VALUE, err_str);
|
|
1081 }
|
|
1082 else
|
|
1083 {
|
|
1084 TError_SetErrorNoFormat(err_pool, TERROR_ERROR_VALUE, err_str);
|
|
1085 }
|
|
1086 }
|
|
1087
|
|
1088 /* This currently returns 0 as a "no error found" value.
|
|
1089 * This could potentially conflict with a user. For now, users
|
|
1090 * shouldn't use 0 to represent an error. If this becomes a
|
|
1091 * problem, we could introduce a magic number like -999999 and
|
|
1092 * define TERROR_NO_ERROR_FOUND.
|
|
1093 */
|
|
1094 int TError_GetErrorNumOnCurrentThread(TErrorPool* err_pool)
|
|
1095 {
|
|
1096 TErrorMessage* error_message;
|
|
1097
|
|
1098 error_message = Internal_GetErrorOnCurrentThread(err_pool);
|
|
1099
|
|
1100 /* If no error message was found for the thread. */
|
|
1101 if(NULL == error_message)
|
|
1102 {
|
|
1103 return 0;
|
|
1104 }
|
|
1105 /* If an error message was found for the thread, but
|
|
1106 * it has already been read/cleared.
|
|
1107 */
|
|
1108 if(0 == error_message->errorAvailable)
|
|
1109 {
|
|
1110 return 0;
|
|
1111 }
|
|
1112 /* We found a legitimate error message, clear it and return it. */
|
|
1113 error_message->errorAvailable = 0;
|
|
1114 return error_message->errorNumber;
|
|
1115 }
|
|
1116
|
|
1117 const char* TError_GetErrorStrOnCurrentThread(TErrorPool* err_pool)
|
|
1118 {
|
|
1119 TErrorMessage* error_message;
|
|
1120
|
|
1121 error_message = Internal_GetErrorOnCurrentThread(err_pool);
|
|
1122
|
|
1123 /* If no error message was found for the thread. */
|
|
1124 if(NULL == error_message)
|
|
1125 {
|
|
1126 return 0;
|
|
1127 }
|
|
1128 /* If an error message was found for the thread, but
|
|
1129 * it has already been read/cleared.
|
|
1130 */
|
|
1131 if(0 == error_message->errorAvailable)
|
|
1132 {
|
|
1133 return 0;
|
|
1134 }
|
|
1135 /* We found a legitimate error message, clear it and return it. */
|
|
1136 error_message->errorAvailable = 0;
|
|
1137 return error_message->errorString;
|
|
1138 }
|
|
1139
|
|
1140 TErrorStatus TError_GetErrorOnCurrentThread(TErrorPool* err_pool)
|
|
1141 {
|
|
1142 TErrorMessage* error_message;
|
|
1143 TErrorStatus error_container;
|
|
1144 error_container.errorNumber = TERROR_NOERROR_VALUE;
|
|
1145 error_container.errorString = NULL;
|
|
1146
|
|
1147 error_message = Internal_GetErrorOnCurrentThread(err_pool);
|
|
1148
|
|
1149 /* If no error message was found for the thread. */
|
|
1150 if(NULL == error_message)
|
|
1151 {
|
|
1152 return error_container;
|
|
1153 }
|
|
1154 /* If an error message was found for the thread, but
|
|
1155 * it has already been read/cleared.
|
|
1156 */
|
|
1157 if(0 == error_message->errorAvailable)
|
|
1158 {
|
|
1159 return error_container;
|
|
1160 }
|
|
1161 /* We found a legitimate error message, clear it and return it. */
|
|
1162 error_message->errorAvailable = 0;
|
|
1163
|
|
1164 error_container.errorNumber = error_message->errorNumber;
|
|
1165 error_container.errorString = error_message->errorString;
|
|
1166 return error_container;
|
|
1167 }
|
|
1168
|
|
1169 /* This function is for alternative usage where you just want one error
|
|
1170 * for all threads. The backend will still work the same, but when you
|
|
1171 * call this function, it will look up the last set error, copy (with locking)
|
|
1172 * the last error to the current thread's memory, and return the object.
|
|
1173 * As always, since the returned object is only accessed on this thread, you
|
|
1174 * don't have to worry about locking.
|
|
1175 */
|
|
1176 TErrorStatus TError_GetLastError(TErrorPool* err_pool)
|
|
1177 {
|
|
1178
|
|
1179 // Lock the error pool to get the lastMessage pointer
|
|
1180 // if the lastMessage pointer is pointing to data on the current thread,
|
|
1181 // we can just return it.
|
|
1182 // Otherwise, we need to copy the message
|
|
1183
|
|
1184 TErrorMessage* error_message;
|
|
1185 TErrorStatus error_container;
|
|
1186 error_container.errorNumber = TERROR_NOERROR_VALUE;
|
|
1187 error_container.errorString = NULL;
|
|
1188
|
|
1189 error_message = Internal_GetLastError(err_pool);
|
|
1190
|
|
1191 /* If no error message was found for the thread. */
|
|
1192 if(NULL == error_message)
|
|
1193 {
|
|
1194 return error_container;
|
|
1195 }
|
|
1196 /* If an error message was found for the thread, but
|
|
1197 * it has already been read/cleared.
|
|
1198 */
|
|
1199 if(0 == error_message->errorAvailable)
|
|
1200 {
|
|
1201 return error_container;
|
|
1202 }
|
|
1203 /* We found a legitimate error message, clear it and return it. */
|
|
1204 error_message->errorAvailable = 0;
|
|
1205
|
|
1206 error_container.errorNumber = error_message->errorNumber;
|
|
1207 error_container.errorString = error_message->errorString;
|
|
1208 return error_container;
|
|
1209 }
|
|
1210
|
|
1211 /* This currently returns 0 as a "no error found" value.
|
|
1212 * This could potentially conflict with a user. For now, users
|
|
1213 * shouldn't use 0 to represent an error. If this becomes a
|
|
1214 * problem, we could introduce a magic number like -999999 and
|
|
1215 * define TERROR_NO_ERROR_FOUND.
|
|
1216 */
|
|
1217 int TError_GetLastErrorNum(TErrorPool* err_pool)
|
|
1218 {
|
|
1219 TErrorMessage* error_message;
|
|
1220
|
|
1221 error_message = Internal_GetLastError(err_pool);
|
|
1222
|
|
1223 /* If no error message was found for the thread. */
|
|
1224 if(NULL == error_message)
|
|
1225 {
|
|
1226 return 0;
|
|
1227 }
|
|
1228 /* If an error message was found for the thread, but
|
|
1229 * it has already been read/cleared.
|
|
1230 */
|
|
1231 if(0 == error_message->errorAvailable)
|
|
1232 {
|
|
1233 return 0;
|
|
1234 }
|
|
1235 /* We found a legitimate error message, clear it and return it. */
|
|
1236 error_message->errorAvailable = 0;
|
|
1237 return error_message->errorNumber;
|
|
1238 }
|
|
1239
|
|
1240 const char* TError_GetLastErrorStr(TErrorPool* err_pool)
|
|
1241 {
|
|
1242 TErrorMessage* error_message;
|
|
1243
|
|
1244 error_message = Internal_GetLastError(err_pool);
|
|
1245
|
|
1246 /* If no error message was found for the thread. */
|
|
1247 if(NULL == error_message)
|
|
1248 {
|
|
1249 return 0;
|
|
1250 }
|
|
1251 /* If an error message was found for the thread, but
|
|
1252 * it has already been read/cleared.
|
|
1253 */
|
|
1254 if(0 == error_message->errorAvailable)
|
|
1255 {
|
|
1256 return 0;
|
|
1257 }
|
|
1258 /* We found a legitimate error message, clear it and return it. */
|
|
1259 error_message->errorAvailable = 0;
|
|
1260 return error_message->errorString;
|
|
1261 }
|
|
1262
|
|
1263
|
|
1264
|