c50d1616fc9f52b6cdb9f109865d4f7044e96a0f
angie
  Thu Dec 8 23:45:40 2011 -0800
Feature #2823 (VCF+tabix track handler): found & fixed more non-reentrant code that can lead to race conditions when multiple VCF
tracks are loaded in parallel.  More than 1000 tries have been
successful compared to 5 or 6 failures per 1000 tries before.

diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c
index 130a07a..7ee68e0 100644
--- src/hg/hgTracks/vcfTrack.c
+++ src/hg/hgTracks/vcfTrack.c
@@ -297,54 +297,50 @@
 const struct hapCluster *kid1 = (const struct hapCluster *)item1;
 const struct hapCluster *kid2 = (const struct hapCluster *)item2;
 struct cwaExtraData *helper = extraData;
 struct hapCluster *consensus = lmHapCluster(helper);
 consensus->leafCount = kid1->leafCount + kid2->leafCount;
 consensus->gtHapIx = kid1->gtHapIx;
 int i;
 for (i=0;  i < helper->len;  i++)
     {
     consensus->refCounts[i] = kid1->refCounts[i] + kid2->refCounts[i];
     consensus->unkCounts[i] = kid1->unkCounts[i] + kid2->unkCounts[i];
     }
 return (struct slList *)consensus;
 }
 
-INLINE void hapClusterToString(const struct hapCluster *c, struct dyString *dy, int len)
-/* Write a text representation of hapCluster's alleles into dy.  */
+INLINE void hapClusterToString(const struct hapCluster *c, char *s, int len)
+/* Write a text representation of hapCluster's alleles into s which is at least len+1 long.  */
 {
-dyStringClear(dy);
 int i;
 for (i=0;  i < len;  i++)
-    dyStringAppendC(dy, (isRef(c, i) ? '0': '1'));
+    s[i] = isRef(c, i) ? '0': '1';
+s[i] = 0;
 }
 
 static int cwaCmp(const struct slList *item1, const struct slList *item2, void *extraData)
 /* Convert hapCluster to allele strings for easy comparison by strcmp. */
 {
 const struct hapCluster *c1 = (const struct hapCluster *)item1;
 const struct hapCluster *c2 = (const struct hapCluster *)item2;
 struct cwaExtraData *helper = extraData;
-static struct dyString *dy1 = NULL, *dy2 = NULL;
-if (dy1 == NULL)
-    {
-    dy1 = dyStringNew(0);
-    dy2 = dyStringNew(0);
-    }
-hapClusterToString(c1, dy1, helper->len);
-hapClusterToString(c2, dy2, helper->len);
-return strcmp(dy1->string, dy2->string);
+char s1[helper->len+1];
+char s2[helper->len+1];
+hapClusterToString(c1, s1, helper->len);
+hapClusterToString(c2, s2, helper->len);
+return strcmp(s1, s2);
 }
 
 void rSetGtHapOrder(struct hacTree *ht, unsigned short *gtHapOrder, unsigned short *retGtHapEnd)
 /* Traverse hacTree and build an ordered array of genotype + haplotype indices. */
 {
 if (ht->left == NULL && ht->right == NULL)
     {
     struct hapCluster *c = (struct hapCluster *)ht->itemOrCluster;
     gtHapOrder[(*retGtHapEnd)++] = c->gtHapIx;
     }
 else if (ht->left == NULL)
     rSetGtHapOrder(ht->right, gtHapOrder, retGtHapEnd);
 else if (ht->right == NULL)
     rSetGtHapOrder(ht->left, gtHapOrder, retGtHapEnd);
 else
@@ -453,45 +449,39 @@
 INLINE Color pgSnpColor(char *allele)
 /* Color allele by first base according to pgSnp palette. */
 {
 if (allele[0] == 'A')
     return revCmplDisp ? MG_MAGENTA : MG_RED;
 else if (allele[0] == 'C')
     return revCmplDisp ? darkGreenColor : MG_BLUE;
 else if (allele[0] == 'G')
     return revCmplDisp ? MG_BLUE : darkGreenColor;
 else if (allele[0] == 'T')
     return revCmplDisp ? MG_RED : MG_MAGENTA;
 else
     return shadesOfGray[5];
 }
 
-INLINE char *gtSummaryString(struct vcfRecord *rec)
+INLINE void gtSummaryString(struct vcfRecord *rec, struct dyString *dy)
 // Make pgSnp-like mouseover text, but with genotype counts instead of allele counts.
-// NOTE: Returned string is statically allocated, don't free it!
 {
-static struct dyString *dy = NULL;
-if (dy == NULL)
-    dy = dyStringNew(0);
-else
-    dyStringClear(dy);
 if (isNotEmpty(rec->name) && !sameString(rec->name, "."))
     dyStringPrintf(dy, "%s: ", rec->name);
 if (rec->alleleCount < 2)
     {
     dyStringAppendC(dy, '?');
-    return dy->string;
+    return;
     }
 const struct vcfFile *vcff = rec->file;
 int gtRefRefCount = 0, gtRefAltCount = 0, gtAltAltCount = 0, gtOtherCount = 0;
 int i;
 for (i=0;  i < vcff->genotypeCount;  i++)
     {
     struct vcfGenotype *gt = &(rec->genotypes[i]);
     if (gt->hapIxA == 0 && gt->hapIxB == 0)
 	gtRefRefCount++;
     else if (gt->hapIxA == 1 && gt->hapIxB == 1)
 	gtAltAltCount++;
     else if ((gt->hapIxA == 0 && gt->hapIxB == 1) || (gt->hapIxA == 1 && gt->hapIxB == 0))
 	gtRefAltCount++;
     else
 	gtOtherCount++;
@@ -501,42 +491,39 @@
     {
     for (i=0;  i < rec->alleleCount;  i++)
 	reverseComplement(rec->alleles[i], strlen(rec->alleles[i]));
     }
 dyStringPrintf(dy, "%s/%s:%d %s/%s:%d %s/%s:%d", rec->alleles[0], rec->alleles[0], gtRefRefCount,
 	       rec->alleles[0], rec->alleles[1], gtRefAltCount,
 	       rec->alleles[1], rec->alleles[1], gtAltAltCount);
 if (gtOtherCount > 0)
     dyStringPrintf(dy, " other:%d", gtOtherCount);
 // Restore original values of pooled strings.
 if (revCmplDisp)
     {
     for (i=0;  i < rec->alleleCount;  i++)
 	reverseComplement(rec->alleles[i], strlen(rec->alleles[i]));
     }
-return dy->string;
 }
 
 void mapBoxForCenterVariant(struct vcfRecord *rec, struct hvGfx *hvg, struct track *tg,
 			    int xOff, int yOff, int width)
 /* Special mouseover for center variant */
 {
-static struct dyString *dy = NULL;
-if (dy == NULL)
-    dy = dyStringNew(0);
-dyStringClear(dy);
-dyStringPrintf(dy, "%s   Haplotypes sorted on ", gtSummaryString(rec));
+struct dyString *dy = dyStringNew(0);
+gtSummaryString(rec, dy);
+dyStringAppend(dy, "   Haplotypes sorted on ");
 char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "centerVariantChrom");
 if (centerChrom == NULL || !sameString(chromName, centerChrom))
     dyStringAppend(dy, "middle variant by default. ");
 else
     dyStringAppend(dy, "this variant. ");
 dyStringAppend(dy, "To anchor sorting to a different variant, click on that variant and "
 	       "then click on the 'Use this variant' button below the variant name.");
 const double scale = scaleForPixels(width);
 int x1 = round((double)(rec->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)(rec->chromEnd-winStart)*scale) + xOff;
 int w = x2-x1;
 if (w <= 1)
     {
     x1--;
     w = 3;
@@ -645,55 +632,58 @@
 	    else
 		refs++;
 	    }
 	}
     int y = yOff + extraPixel + pixIx;
     Color col;
     if (colorMode == baseMode)
 	col = colorByBase(refs, alts, unks, rec->alleles[0], rec->alleles[1]);
     else if (colorMode == refAltMode)
 	col = colorByRefAlt(refs, alts, unks);
     else
 	col = colorByAltOnly(refs, alts, unks);
     if (col != MG_WHITE)
 	hvGfxLine(hvg, x1, y, x2, y, col);
     }
-char *mouseoverText = gtSummaryString(rec);
 int yBot = yOff + tg->height - CLIP_PAD - 1;
 if (isCenter)
     {
     if (colorMode == altOnlyMode)
 	{
 	// Colorful outline to distinguish this variant:
 	hvGfxLine(hvg, x1-1, yOff, x1-1, yBot, purple);
 	hvGfxLine(hvg, x2+1, yOff, x2+1, yBot, purple);
 	hvGfxLine(hvg, x1-1, yOff, x2+1, yOff, purple);
 	hvGfxLine(hvg, x1-1, yBot, x2+1, yBot, purple);
 	}
     else
 	{
 	// Thick black lines to distinguish this variant:
 	hvGfxBox(hvg, x1-3, yOff, 3, tg->height, MG_BLACK);
 	hvGfxBox(hvg, x2, yOff, 3, tg->height, MG_BLACK);
 	hvGfxLine(hvg, x1-2, yOff, x2+2, yOff, MG_BLACK);
 	hvGfxLine(hvg, x1-2, yBot, x2+2, yBot, MG_BLACK);
 	}
     // Mouseover is handled separately by mapBoxForCenterVariant
     }
 else
+    {
+    struct dyString *dy = dyStringNew(0);
+    gtSummaryString(rec, dy);
     mapBoxHgcOrHgGene(hvg, rec->chromStart, rec->chromEnd, x1, yOff, w, tg->height, tg->track,
-		      rec->name, mouseoverText, NULL, TRUE, NULL);
+		      rec->name, dy->string, NULL, TRUE, NULL);
+    }
 if (colorMode == altOnlyMode)
     hvGfxLine(hvg, x1, yBot, x2, yBot, (isClustered ? purple : shadesOfGray[5]));
 }
 
 static int getCenterVariantIx(struct track *tg, int seqStart, int seqEnd,
 			      struct vcfRecord *records)
 // If the user hasn't specified a local variant/position to use as center,
 // just use the median variant in window.
 {
 int defaultIx = (slCount(records)-1) / 2;
 char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "centerVariantChrom");
 if (centerChrom != NULL && sameString(chromName, centerChrom))
     {
     int centerPos = cartUsualIntClosestToHome(cart, tg->tdb, FALSE, "centerVariantPos", -1);
     int winSize = seqEnd - seqStart;
@@ -714,36 +704,33 @@
 struct titleHelper
     {
     char *track;           // For imageV2's map item code
     int startIx;           // Index of first record (variant) used in clustering
     int centerIx;          // Index of center record (variant) used in clustering
     int endIx;             // Half-open index of last record (variant) used in clustering
     int nRecords;          // Total number of records in position range
     char **refs;           // Array of reference alleles for records used in clustering
     char **alts;           // Array of alternate alleles for records used in clustering
                            // (refs[0] and alts[0] are from record at startIx)
     };
 
 void addClusterMapItem(struct hacTree *ht, int x1, int y1, int x2, int y2, struct titleHelper *helper)
 /* If using imageV2, add mouseover text (no link) with info about this cluster. */
 {
-static struct dyString *dy = NULL;
 if (theImgBox && curImgTrack)
     {
-    if (dy == NULL)
-	dy = dyStringNew(0);
-    dyStringClear(dy);
+    struct dyString *dy = dyStringNew(0);
     struct hapCluster *c = (struct hapCluster *)ht->itemOrCluster;
     dyStringPrintf(dy, "N=%d ", c->leafCount);
     while (dyStringLen(dy) < 7)
 	dyStringAppendC(dy, ' ');
     if (helper->startIx > 0)
 	dyStringAppend(dy, "...");
     int i, nHapsForClustering = helper->endIx - helper->startIx;
     for (i=0;  i < nHapsForClustering;  i++)
 	{
 	boolean isCenter = (helper->startIx+i == helper->centerIx);
 	char *allele = isRef(c, i) ? helper->refs[i] : helper->alts[i];
 	if (isCenter)
 	    dyStringAppendC(dy, '[');
 	if (hasUnk(c, i))
 	    dyStringAppendC(dy, '?');