src/lib/errCatch.c 1.4

1.4 2009/11/03 00:35:53 angie
Make errCatch POSIX thread (pthread) safe: instead of a single global stack, use per-thread stacks hashed by thread ID.
Index: src/lib/errCatch.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/lib/errCatch.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -b -B -U 1000000 -r1.3 -r1.4
--- src/lib/errCatch.c	12 Aug 2009 18:13:19 -0000	1.3
+++ src/lib/errCatch.c	3 Nov 2009 00:35:53 -0000	1.4
@@ -1,99 +1,121 @@
 /* errCatch - help catch errors so that errAborts aren't
  * fatal, and warn's don't necessarily get printed immediately. 
  * Note that error conditions caught this way will tend to
  * leak resources unless there are additional wrappers. 
  *
  * Typical usage is
  * errCatch = errCatchNew();
  * if (errCatchStart(errCatch))
  *     doFlakyStuff();
  * errCatchEnd(errCatch);
  * if (errCatch->gotError)
  *     warn(errCatch->message->string);
  * errCatchFree(&errCatch); 
  * cleanupFlakyStuff();
  */
 
 #include "common.h"
 #include "errabort.h"
 #include "dystring.h"
+#include "hash.h"
+#include <pthread.h>
 #include "errCatch.h"
 
 static char const rcsid[] = "$Id$";
 
 
 struct errCatch *errCatchNew()
 /* Return new error catching structure. */
 {
 struct errCatch *errCatch;
 AllocVar(errCatch);
 errCatch->message = dyStringNew(0);
 return errCatch;
 }
 
 void errCatchFree(struct errCatch **pErrCatch)
 /* Free up resources associated with errCatch */
 {
 struct errCatch *errCatch = *pErrCatch;
 if (errCatch != NULL)
     {
     dyStringFree(&errCatch->message);
     freez(pErrCatch);
     }
 }
 
-static struct errCatch *errCatchStack = NULL;
+static struct errCatch **getStack()
+/* Return a pointer to the errCatch object stack for the current pthread. */
+{
+static struct hash *perThreadStacks = NULL;
+int pid = pthread_self();
+// A true integer has function would be nicer, but this will do.  
+// Don't safef, theoretically that could abort.
+char key[16];
+snprintf(key, sizeof(key), "%d", pid);
+key[ArraySize(key)-1] = '\0';
+if (perThreadStacks == NULL)
+    perThreadStacks = hashNew(0);
+struct hashEl *hel = hashLookup(perThreadStacks, key);
+if (hel == NULL)
+    hel = hashAdd(perThreadStacks, key, NULL);
+return (struct errCatch **)(&hel->val);
+}
 
 static void errCatchAbortHandler()
 /* semiAbort */
 {
+struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack;
 errCatchStack->gotError = TRUE;
 longjmp(errCatchStack->jmpBuf, -1);
 }
 
 static void errCatchWarnHandler(char *format, va_list args)
 /* Write an error to top of errCatchStack. */
 {
+struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack;
 dyStringVaPrintf(errCatchStack->message, format, args);
 dyStringAppendC(errCatchStack->message, '\n');
 }
 
 boolean errCatchPushHandlers(struct errCatch *errCatch)
 /* Push error handlers.  Not usually called directly. */
 {
 pushAbortHandler(errCatchAbortHandler);
 pushWarnHandler(errCatchWarnHandler);
-slAddHead(&errCatchStack, errCatch);
+struct errCatch **pErrCatchStack = getStack();
+slAddHead(pErrCatchStack, errCatch);
 return TRUE;
 }
 
 void errCatchEnd(struct errCatch *errCatch)
 /* Restore error handlers and pop self off of catching stack. */
 {
 popWarnHandler();
 popAbortHandler();
+struct errCatch **pErrCatchStack = getStack(), *errCatchStack = *pErrCatchStack;
 if (errCatch != errCatchStack)
    errAbort("Mismatch betweene errCatch and errCatchStack");
-errCatchStack = errCatch->next;
+*pErrCatchStack = errCatch->next;
 }
 
 boolean errCatchFinish(struct errCatch **pErrCatch)
 /* Finish up error catching.  Report error if there is a
  * problem and return FALSE.  If no problem return TRUE.
  * This handles errCatchEnd and errCatchFree. */
 {
 struct errCatch *errCatch = *pErrCatch;
 boolean ok = TRUE;
 if (errCatch != NULL)
     {
     errCatchEnd(errCatch);
     if (errCatch->gotError)
 	{
 	ok = FALSE;
 	warn("%s", errCatch->message->string);
 	}
     errCatchFree(pErrCatch);
     }
 return ok;
 }