304e9ab6e3a62d7863b144478fc1ac621623a617
braney
  Wed Jul 29 11:42:01 2026 -0700
Fix data race on the chromAlias caches that was crashing hgTracks in parallel decorator loads.

chromAliasFindNative() and chromAliasFindAliases() lazily created their static
cache hashes and did their first lookup outside the mutex, taking the lock only
for the miss path.  hgTracks loads decorators on real pthreads, so two threads
arriving before a cache existed could race on it and SIGSEGV in hashLookup.
Move the lock above the lazy init and the first lookup so the whole cache
access is inside it.

Both paths of chromAliasFindNative() now return an allocated string rather than
the cache-owned pointer, so document that the caller owns the result, free it in
decorationNativeItem() (called once per decoration), and drop the redundant
outer cloneString() in hgOfficialChromName().

refs #37955

diff --git src/hg/inc/chromAlias.h src/hg/inc/chromAlias.h
index 10b2b613d3d..5f9808ea3ff 100644
--- src/hg/inc/chromAlias.h
+++ src/hg/inc/chromAlias.h
@@ -1,90 +1,91 @@
 /* chromAlias.h was originally generated by the autoSql program, which also 
  * generated chromAlias.c and chromAlias.sql.  This header links the database and
  * the RAM representation of objects. */
 
 #ifndef CHROMALIAS_H
 #define CHROMALIAS_H
 
 #define CHROMALIAS_NUM_COLS 3
 
 extern char *chromAliasCommaSepFieldNames;
 
 struct chromAlias
 /* correspondence of UCSC chromosome names to refseq, genbank, and ensembl names */
     {
     struct chromAlias *next;  /* Next in singly linked list. */
     char *alias;	/* external name */
     char *chrom;	/* UCSC genome browser chromosome name */
     char *source;	/* comma separated list, when available: refseq,genbank,ensembl */
     };
 
 void chromAliasStaticLoad(char **row, struct chromAlias *ret);
 /* Load a row from chromAlias table into ret.  The contents of ret will
  * be replaced at the next call to this function. */
 
 struct chromAlias *chromAliasLoad(char **row);
 /* Load a chromAlias from row fetched with select * from chromAlias
  * from database.  Dispose of this with chromAliasFree(). */
 
 struct chromAlias *chromAliasLoadAll(char *fileName);
 /* Load all chromAlias from whitespace-separated file.
  * Dispose of this with chromAliasFreeList(). */
 
 struct chromAlias *chromAliasLoadAllByChar(char *fileName, char chopper);
 /* Load all chromAlias from chopper separated file.
  * Dispose of this with chromAliasFreeList(). */
 
 #define chromAliasLoadAllByTab(a) chromAliasLoadAllByChar(a, '\t');
 /* Load all chromAlias from tab separated file.
  * Dispose of this with chromAliasFreeList(). */
 
 struct chromAlias *chromAliasCommaIn(char **pS, struct chromAlias *ret);
 /* Create a chromAlias out of a comma separated string. 
  * This will fill in ret if non-null, otherwise will
  * return a new chromAlias */
 
 void chromAliasFree(struct chromAlias **pEl);
 /* Free a single dynamically allocated chromAlias such as created
  * with chromAliasLoad(). */
 
 void chromAliasFreeList(struct chromAlias **pList);
 /* Free a list of dynamically allocated chromAlias's */
 
 void chromAliasOutput(struct chromAlias *el, FILE *f, char sep, char lastSep);
 /* Print out chromAlias.  Separate fields with sep. Follow last field with lastSep. */
 
 #define chromAliasTabOut(el,f) chromAliasOutput(el,f,'\t','\n');
 /* Print out chromAlias as a line in a tab-separated file. */
 
 #define chromAliasCommaOut(el,f) chromAliasOutput(el,f,',',',');
 /* Print out chromAlias as a comma separated list including final comma. */
 
 void chromAliasJsonOutput(struct chromAlias *el, FILE *f);
 /* Print out chromAlias in JSON format. */
 
 /* -------------------------------- End autoSql Generated Code -------------------------------- */
 
 void chromAliasSetupBb(char *database, char *bbFile);
 /* Look for a chromAlias bigBed file and open it. */
 
 void chromAliasSetup(char *database);
 /* Read in the chromAlias file/table for this database. */
 
 char *chromAliasFindNative(char *name);
-/* Find the native seqName for a given alias. */
+/* Find the native seqName for a given alias.  Returns a freshly allocated
+ * string (or NULL), so the caller owns it and should freeMem it. */
 
 struct slName *chromAliasFindAliases(char *seqName);
 /* Get the list of aliases for this sequence name. */
 
 char *chromAliasFindSingleAlias(char *seqName, char *authority);
 /* Find the aliases for a given seqName from a given authority. */
 
 char *chromAliasGetDisplayChrom(char *db, struct cart *cart, char *seqName);
 /* Return the sequence name to display based on the database and cart. */
 
 char *chromAliasNCBI(char *db, char *chr, char *gcX);
 /* given the database and the chrom name, find the NCBI equivalent chr name
  *   gcX helps determine if RefSeq/GCF or GenBank/GCA
  */
 
 #endif /* CHROMALIAS_H */