6afa9dd4343c3b66ff09f39735170ec8be5f282e
braney
  Wed Sep 18 17:33:15 2024 -0700
add ability to set a timeout on remote loads

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index b6a3ce0..dad0f84 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -71,30 +71,31 @@
 #include "wigCommon.h"
 #include "knetUdc.h"
 #include "hex.h"
 #include <openssl/sha.h>
 #include "customComposite.h"
 #include "chromAlias.h"
 #include "jsonWrite.h"
 #include "cds.h"
 #include "cacheTwoBit.h"
 #include "cartJson.h"
 #include "wikiLink.h"
 #include "decorator.h"
 #include "decoratorUi.h"
 #include "mouseOver.h"
 #include "exportedDataHubs.h"
+#include "signal.h"
 
 //#include "bed3Sources.h"
 
 /* Other than submit and Submit all these vars should start with hgt.
  * to avoid weeding things out of other program's namespaces.
  * Because the browser is a central program, most of its cart
  * variables are not hgt. qualified.  It's a good idea if other
  * program's unique variables be qualified with a prefix though. */
 char *excludeVars[] = { "submit", "Submit", "dirty", "hgt.reset",
             "hgt.in1", "hgt.in2", "hgt.in3", "hgt.inBase",
             "hgt.out1", "hgt.out2", "hgt.out3", "hgt.out4",
             "hgt.left1", "hgt.left2", "hgt.left3",
             "hgt.right1", "hgt.right2", "hgt.right3",
             "hgt.dinkLL", "hgt.dinkLR", "hgt.dinkRL", "hgt.dinkRR",
             "hgt.tui", "hgt.hideAll", "hgt.visAllFromCt",
@@ -8568,30 +8569,57 @@
 
 #ifdef NOTNOW
 static void printAliases(char *name)
 /* Print out the aliases for this sequence. */
 {
 struct slName *names = chromAliasFindAliases(name);
 
 printf("<div id='aliases'><a title='");
 for(;names; names = names->next)
     printf("%s;",names->name);
 printf("'>Aliases</a></div>");
 }
 #endif
 
 
+static void paraLoadTimeoutFunc(int sig)
+// signal handler for alarm timeout.   Tell parallel loads to stop by errAborts
+// in udcRead
+{
+udcReadStopMessage("Parallel read timeout message"); 
+alarm(0); // disable the alarm
+}
+
+unsigned getParaLoadTimeout()
+// get the parallel load timeout in milliseconds if any
+{
+char *cfg = cfgOption("paraLoadTimeout");
+
+if (cfg == NULL)
+    return 0;
+
+char *paraLoadTimeoutStr = cartOptionalString(cart, "paraLoadTimeout");
+if (paraLoadTimeoutStr == NULL)
+    paraLoadTimeoutStr = cfg;
+
+unsigned paraLoadTimeout = 0;
+if (paraLoadTimeoutStr != NULL)
+    paraLoadTimeout = sqlUnsigned(paraLoadTimeoutStr);
+
+return paraLoadTimeout;
+}
+
 void doTrackForm(char *psOutput, struct tempName *ideoTn)
 /* Make the tracks display form with the zoom/scroll buttons and the active
  * image.  If the ideoTn parameter is not NULL, it is filled in if the
  * ideogram is created.  */
 {
 int disconCount = 0;
 struct group *group;
 struct track *track;
 char *freezeName = NULL;
 boolean hideAll = cgiVarExists("hgt.hideAll");
 boolean hideTracks = cgiOptionalString( "hideTracks") != NULL;
 boolean defaultTracks = cgiVarExists("hgt.reset");
 boolean showedRuler = FALSE;
 boolean showTrackControls = cartUsualBoolean(cart, "trackControlsOnMain", TRUE);
 boolean multiRegionButtonTop = cfgOptionBooleanDefault(MULTI_REGION_CFG_BUTTON_TOP, TRUE);
@@ -8774,30 +8802,33 @@
 		    subtrack->nextWindow = subcopy;
 		    }
 		}
 	    slReverse(&copy->subtracks);
 	    }
 	}
     slReverse(&newTrackList);
     trackList = newTrackList;
     window->next->trackList = trackList;  // save new track list in window
     }
 trackList = windows->trackList;  // restore original track list
 
 // Loop over each window loading all tracks
 trackLoadingInProgress = TRUE;
 
+// see if we have a parallel load time out 
+unsigned paraLoadTimeout = getParaLoadTimeout();
+
 // LOAD OPTIMIZATION HACK GALT
 // This is an attempt to try to optimize loading by having multiple regions
 // treated as a single span.  The hack just grabs the dimensions of the first and last windows
 // and uses the loader in the first window to load them, then copies the results to all tracks.
 // This basically has only been tried on BED-like tracks, and only for exon/gene-mostly vmodes.
 // I am not re-partitioning the results after the load, so this means all windows see all items.
 // The reason that tends to work is that by luck most BED handlers have code to check if the item
 // overlaps the current window and to skip it if it does not.
 // I do not expect something so simple would work with wigs and other track types.
 // Even if we do want to optimize the BED-like tracks (which are already the fastest loading type),
 // to handle all of the virtmodes properly, this would have be be done differently.
 // Instead of just lumping them all into a single range, you would have to cluster together
 // ranges that are close together and on the same chromosome.
 // Clearly this was just to test an idea for optimizing.
 // NOT FINISHED.
@@ -8807,30 +8838,41 @@
 for (window=windows; window; window=window->next)
     lastWinEnd = window->winEnd;
 
 for (window=windows; window; window=window->next)
     {
     trackList = window->trackList;  // set track list
     setGlobalsFromWindow(window);
 
     // TEMP HACK GALT REMOVE
     if (loadHack)
 	{
 	if (currentWindow == windows) // first window
 	    winEnd = lastWinEnd; // so now we load the entire span inside the first window.
 	}
 
+    if (paraLoadTimeout)  // we stop loading after a number of milliseconds
+        {
+        struct sigaction siga;
+        siga.sa_handler = paraLoadTimeoutFunc;
+        siga.sa_flags = SA_RESTART;
+        sigaction(SIGALRM, &siga, NULL);
+        
+        udcReadStopMessage(NULL); // ask udcRead to NOT errAbort
+        ualarm(paraLoadTimeout * 1000, 0);
+        }
+
     /* pre-load remote tracks in parallel */
     int ptMax = atoi(cfgOptionDefault("parallelFetch.threads", "20"));  // default number of threads for parallel fetch.
     int pfdListCount = 0;
     pthread_t *threads = NULL;
     if (ptMax > 0)     // parallelFetch.threads=0 to disable parallel fetch
 	{
 	findLeavesForParallelLoad(trackList, &pfdList);
 	pfdListCount = slCount(pfdList);
 	/* launch parallel threads */
 	ptMax = min(ptMax, pfdListCount);
 	if (ptMax > 0)
 	    {
 	    AllocArray(threads, ptMax);
 	    /* Create threads */
 	    int pt;
@@ -8899,30 +8941,36 @@
 		    }
 		}
 	    }
 	}
 
     if (ptMax > 0)
 	{
 	/* wait for remote parallel load to finish */
 	remoteParallelLoadWait(atoi(cfgOptionDefault("parallelFetch.timeout", "90")));  // wait up to default 90 seconds.
 	if (measureTiming)
 	    measureTime("Waiting for parallel (%d threads for %d tracks) remote data fetch", ptMax, pfdListCount);
 	}
     }
 trackLoadingInProgress = FALSE;
 
+if (paraLoadTimeout)
+    {
+    udcReadStopMessage(NULL); // ask udcRead to NOT errAbort
+    alarm(0);
+    }
+
 setGlobalsFromWindow(windows); // first window // restore globals
 trackList = windows->trackList;  // restore track list
 
 // Some loadItems() calls will have already set limitedVis.
 // Look for lowest limitedVis across all windows
 // if found, set all windows to same lowest limitedVis
 for (track = trackList; track != NULL; track = track->next)
     {
     setSharedLimitedVisAcrossWindows(track);
     struct track *sub;
     for (sub=track->subtracks; sub; sub=sub->next)
 	{
 	setSharedLimitedVisAcrossWindows(sub);
 	}
     }