Mercurial > almixer_isolated
annotate Isolated/tErrorLib.c @ 59:7d508c8cd75a
New implementation backend for SimpleThread using native Windows threading APIs.
Documentation found here:
http://msdn.microsoft.com/en-us/library/kdzttdcb(v=vs.110).aspx
Web searches indicated I should be using _beginthreadex instead of CreateThread for C runtime compatibility.
author | Eric Wing <ewing . public |-at-| gmail . com> |
---|---|
date | Fri, 08 Jun 2012 01:04:51 -0700 |
parents | c07dbd386ded |
children | 72570fcd30e8 |
rev | line source |
---|---|
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 */ | |
39
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
928 if(NULL != error_message->errorString) |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
929 { |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
930 /* Need to free errorString from previous pass otherwise we leak. |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
931 * Maybe there is a smarter way to avoid the free/malloc, |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
932 * but this would probably require determining the length of the |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
933 * final string beforehand which probably implies two |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
934 * *printf calls which may or may not be better. |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
935 */ |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
936 free(error_message->errorString); |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
937 error_message->errorString = NULL; |
c07dbd386ded
fixed memory leak in tErrorLib
Eric Wing <ewing@anscamobile.com>
parents:
38
diff
changeset
|
938 } |
38 | 939 |
940 ret_num_chars = vasprintf(&error_message->errorString, err_str, argp); | |
941 /* vasprintf returns -1 as an error */ | |
942 if(-1 == ret_num_chars) | |
943 { | |
944 /* Very bad, but not sure what to do here. */ | |
945 if(NULL != error_message->errorString) | |
946 { | |
947 free(error_message->errorString); | |
948 error_message->errorString = NULL; | |
949 error_message->errorMaxStringLength = 0; | |
950 /* Don't return here. Still need to va_end, and | |
951 * there is a chance that the err_num might work. | |
952 * Plus the availability needs to be set. | |
953 */ | |
954 } | |
955 } | |
956 /* else vasprint returns the number of characters in the string | |
957 * not including the \0 character. | |
958 */ | |
959 else | |
960 { | |
961 /* I actually don't know how much memory vasprintf allocated | |
962 * for the string. But it is at least ret_num_chars+1, so | |
963 * I will use that as my max string length (which is | |
964 * mainly used by CopyDynamicString() for efficiency | |
965 * which is becoming less used in this code). | |
966 */ | |
967 error_message->errorMaxStringLength = ret_num_chars+1; | |
968 } | |
969 #endif /* DONT_USE_VASPRINTF */ | |
970 } | |
971 | |
972 /* I'm allowing for a user to explicitly clear an error message by | |
973 * clearing both attributes. | |
974 */ | |
975 if((TERROR_NOERROR_VALUE == err_num) && (NULL == err_str)) | |
976 { | |
977 error_message->errorNumber = TERROR_NOERROR_VALUE; | |
978 error_message->errorAvailable = 0; | |
979 } | |
980 /* This is the normal case, copy the error number | |
981 * and mark the error as unread. | |
982 */ | |
983 else | |
984 { | |
985 error_message->errorNumber = err_num; | |
986 error_message->errorAvailable = 1; | |
987 } | |
988 | |
989 /* Now that the data is set, we also want to denote that this | |
990 * thread is the last error message. We need to lock for this | |
991 * since the lastError pointer is shared across threads. | |
992 */ | |
993 Internal_SetLastErrorMessagePointerToErrorMessage(err_pool, error_message); | |
994 } | |
995 | |
996 void TError_SetErrorNoFormat(TErrorPool* err_pool, int err_num, const char* err_str) | |
997 { | |
998 TErrorMessage* error_message; | |
999 if(NULL == err_pool) | |
1000 { | |
1001 return; | |
1002 } | |
1003 | |
1004 error_message = Internal_GetErrorOnCurrentThread(err_pool); | |
1005 /* If no error message was found, that means we must allocate | |
1006 * a new entry for this entry. | |
1007 */ | |
1008 if(NULL == error_message) | |
1009 { | |
1010 error_message = Internal_CreateErrorOnCurrentThread(err_pool); | |
1011 /* If this fails, this is bad...not sure what to do though. */ | |
1012 if(NULL == error_message) | |
1013 { | |
1014 return; | |
1015 } | |
1016 } | |
1017 | |
1018 /* | |
1019 * I don't think I have to lock here. The [Get|Create]ErrorOnCurrentThread | |
1020 * functions lock err_pool as they need access. Here, I don't access | |
1021 * err_pool (which is shared) and error_message should be unique for | |
1022 * each thread so I don't think there is any contention. (Remember that | |
1023 * simultaneous calls to SetError would only happen if they are in | |
1024 * different threads.) | |
1025 * There *might* be a problem with library calls (strncpy, calloc). | |
1026 * I'm not sure if the various platforms are reentrant. | |
1027 * I guess for now, I will assume they won't bite me. | |
1028 */ | |
1029 error_message->errorNumber = err_num; | |
1030 /* This will copy the string and set the new errorMaxStringLength as needed. */ | |
1031 error_message->errorString = Internal_CopyDynamicString(error_message->errorString, &error_message->errorMaxStringLength, err_str); | |
1032 /* I'm allowing for a user to explicitly clear an error message by | |
1033 * clearing both attributes. | |
1034 */ | |
1035 if((TERROR_NOERROR_VALUE == err_num) && (NULL == err_str)) | |
1036 { | |
1037 error_message->errorAvailable = 0; | |
1038 } | |
1039 else | |
1040 { | |
1041 error_message->errorAvailable = 1; | |
1042 } | |
1043 | |
1044 /* Now that the data is set, we also want to denote that this | |
1045 * thread is the last error message. We need to lock for this | |
1046 * since the lastError pointer is shared across threads. | |
1047 */ | |
1048 Internal_SetLastErrorMessagePointerToErrorMessage(err_pool, error_message); | |
1049 } | |
1050 | |
1051 void TError_SetErrorNum(TErrorPool* err_pool, int err_num) | |
1052 { | |
1053 TError_SetErrorNoFormat(err_pool, err_num, NULL); | |
1054 } | |
1055 | |
1056 void TError_SetErrorStr(TErrorPool* err_pool, const char* err_str, ...) | |
1057 { | |
1058 va_list argp; | |
1059 va_start(argp, err_str); | |
1060 if(NULL == err_str) | |
1061 { | |
1062 TError_SetErrorv(err_pool, TERROR_NOERROR_VALUE, err_str, argp); | |
1063 } | |
1064 else | |
1065 { | |
1066 TError_SetErrorv(err_pool, TERROR_ERROR_VALUE, err_str, argp); | |
1067 } | |
1068 va_end(argp); | |
1069 } | |
1070 | |
1071 void TError_SetErrorStrv(TErrorPool* err_pool, const char* err_str, va_list argp) | |
1072 { | |
1073 if(NULL == err_str) | |
1074 { | |
1075 TError_SetErrorv(err_pool, TERROR_NOERROR_VALUE, err_str, argp); | |
1076 } | |
1077 else | |
1078 { | |
1079 TError_SetErrorv(err_pool, TERROR_ERROR_VALUE, err_str, argp); | |
1080 } | |
1081 } | |
1082 | |
1083 /* If a NULL string is set, then it is presumed no error actually occurred | |
1084 * and this is a reset. So the err_num will be implicitly set to 0. Otherwise | |
1085 * the err_num will be set to 1 (for internal consistency and conventions). | |
1086 */ | |
1087 void TError_SetErrorStrNoFormat(TErrorPool* err_pool, const char* err_str) | |
1088 { | |
1089 if(NULL == err_str) | |
1090 { | |
1091 TError_SetErrorNoFormat(err_pool, TERROR_NOERROR_VALUE, err_str); | |
1092 } | |
1093 else | |
1094 { | |
1095 TError_SetErrorNoFormat(err_pool, TERROR_ERROR_VALUE, err_str); | |
1096 } | |
1097 } | |
1098 | |
1099 /* This currently returns 0 as a "no error found" value. | |
1100 * This could potentially conflict with a user. For now, users | |
1101 * shouldn't use 0 to represent an error. If this becomes a | |
1102 * problem, we could introduce a magic number like -999999 and | |
1103 * define TERROR_NO_ERROR_FOUND. | |
1104 */ | |
1105 int TError_GetErrorNumOnCurrentThread(TErrorPool* err_pool) | |
1106 { | |
1107 TErrorMessage* error_message; | |
1108 | |
1109 error_message = Internal_GetErrorOnCurrentThread(err_pool); | |
1110 | |
1111 /* If no error message was found for the thread. */ | |
1112 if(NULL == error_message) | |
1113 { | |
1114 return 0; | |
1115 } | |
1116 /* If an error message was found for the thread, but | |
1117 * it has already been read/cleared. | |
1118 */ | |
1119 if(0 == error_message->errorAvailable) | |
1120 { | |
1121 return 0; | |
1122 } | |
1123 /* We found a legitimate error message, clear it and return it. */ | |
1124 error_message->errorAvailable = 0; | |
1125 return error_message->errorNumber; | |
1126 } | |
1127 | |
1128 const char* TError_GetErrorStrOnCurrentThread(TErrorPool* err_pool) | |
1129 { | |
1130 TErrorMessage* error_message; | |
1131 | |
1132 error_message = Internal_GetErrorOnCurrentThread(err_pool); | |
1133 | |
1134 /* If no error message was found for the thread. */ | |
1135 if(NULL == error_message) | |
1136 { | |
1137 return 0; | |
1138 } | |
1139 /* If an error message was found for the thread, but | |
1140 * it has already been read/cleared. | |
1141 */ | |
1142 if(0 == error_message->errorAvailable) | |
1143 { | |
1144 return 0; | |
1145 } | |
1146 /* We found a legitimate error message, clear it and return it. */ | |
1147 error_message->errorAvailable = 0; | |
1148 return error_message->errorString; | |
1149 } | |
1150 | |
1151 TErrorStatus TError_GetErrorOnCurrentThread(TErrorPool* err_pool) | |
1152 { | |
1153 TErrorMessage* error_message; | |
1154 TErrorStatus error_container; | |
1155 error_container.errorNumber = TERROR_NOERROR_VALUE; | |
1156 error_container.errorString = NULL; | |
1157 | |
1158 error_message = Internal_GetErrorOnCurrentThread(err_pool); | |
1159 | |
1160 /* If no error message was found for the thread. */ | |
1161 if(NULL == error_message) | |
1162 { | |
1163 return error_container; | |
1164 } | |
1165 /* If an error message was found for the thread, but | |
1166 * it has already been read/cleared. | |
1167 */ | |
1168 if(0 == error_message->errorAvailable) | |
1169 { | |
1170 return error_container; | |
1171 } | |
1172 /* We found a legitimate error message, clear it and return it. */ | |
1173 error_message->errorAvailable = 0; | |
1174 | |
1175 error_container.errorNumber = error_message->errorNumber; | |
1176 error_container.errorString = error_message->errorString; | |
1177 return error_container; | |
1178 } | |
1179 | |
1180 /* This function is for alternative usage where you just want one error | |
1181 * for all threads. The backend will still work the same, but when you | |
1182 * call this function, it will look up the last set error, copy (with locking) | |
1183 * the last error to the current thread's memory, and return the object. | |
1184 * As always, since the returned object is only accessed on this thread, you | |
1185 * don't have to worry about locking. | |
1186 */ | |
1187 TErrorStatus TError_GetLastError(TErrorPool* err_pool) | |
1188 { | |
1189 | |
1190 // Lock the error pool to get the lastMessage pointer | |
1191 // if the lastMessage pointer is pointing to data on the current thread, | |
1192 // we can just return it. | |
1193 // Otherwise, we need to copy the message | |
1194 | |
1195 TErrorMessage* error_message; | |
1196 TErrorStatus error_container; | |
1197 error_container.errorNumber = TERROR_NOERROR_VALUE; | |
1198 error_container.errorString = NULL; | |
1199 | |
1200 error_message = Internal_GetLastError(err_pool); | |
1201 | |
1202 /* If no error message was found for the thread. */ | |
1203 if(NULL == error_message) | |
1204 { | |
1205 return error_container; | |
1206 } | |
1207 /* If an error message was found for the thread, but | |
1208 * it has already been read/cleared. | |
1209 */ | |
1210 if(0 == error_message->errorAvailable) | |
1211 { | |
1212 return error_container; | |
1213 } | |
1214 /* We found a legitimate error message, clear it and return it. */ | |
1215 error_message->errorAvailable = 0; | |
1216 | |
1217 error_container.errorNumber = error_message->errorNumber; | |
1218 error_container.errorString = error_message->errorString; | |
1219 return error_container; | |
1220 } | |
1221 | |
1222 /* This currently returns 0 as a "no error found" value. | |
1223 * This could potentially conflict with a user. For now, users | |
1224 * shouldn't use 0 to represent an error. If this becomes a | |
1225 * problem, we could introduce a magic number like -999999 and | |
1226 * define TERROR_NO_ERROR_FOUND. | |
1227 */ | |
1228 int TError_GetLastErrorNum(TErrorPool* err_pool) | |
1229 { | |
1230 TErrorMessage* error_message; | |
1231 | |
1232 error_message = Internal_GetLastError(err_pool); | |
1233 | |
1234 /* If no error message was found for the thread. */ | |
1235 if(NULL == error_message) | |
1236 { | |
1237 return 0; | |
1238 } | |
1239 /* If an error message was found for the thread, but | |
1240 * it has already been read/cleared. | |
1241 */ | |
1242 if(0 == error_message->errorAvailable) | |
1243 { | |
1244 return 0; | |
1245 } | |
1246 /* We found a legitimate error message, clear it and return it. */ | |
1247 error_message->errorAvailable = 0; | |
1248 return error_message->errorNumber; | |
1249 } | |
1250 | |
1251 const char* TError_GetLastErrorStr(TErrorPool* err_pool) | |
1252 { | |
1253 TErrorMessage* error_message; | |
1254 | |
1255 error_message = Internal_GetLastError(err_pool); | |
1256 | |
1257 /* If no error message was found for the thread. */ | |
1258 if(NULL == error_message) | |
1259 { | |
1260 return 0; | |
1261 } | |
1262 /* If an error message was found for the thread, but | |
1263 * it has already been read/cleared. | |
1264 */ | |
1265 if(0 == error_message->errorAvailable) | |
1266 { | |
1267 return 0; | |
1268 } | |
1269 /* We found a legitimate error message, clear it and return it. */ | |
1270 error_message->errorAvailable = 0; | |
1271 return error_message->errorString; | |
1272 } | |
1273 | |
1274 | |
1275 |