diff --git a/Packages/MIES/MIES_Async.ipf b/Packages/MIES/MIES_Async.ipf index ee98ef4acd..3365984a51 100644 --- a/Packages/MIES/MIES_Async.ipf +++ b/Packages/MIES/MIES_Async.ipf @@ -27,8 +27,9 @@ static StrConstant ASYNC_WORKLOADCLASS_STR = "workloadClass" static StrConstant ASYNC_PARAMCOUNT_STR = "paramCount" static StrConstant ASYNC_INORDER_STR = "inOrder" static StrConstant ASYNC_ABORTFLAG_STR = "abortFlag" -static StrConstant ASYNC_ERROR_STR = "err" -static StrConstant ASYNC_ERRORMSG_STR = "errmsg" +static StrConstant ASYNC_RTERROR_STR = "rtErr" +static StrConstant ASYNC_RTERRORMSG_STR = "rtErrMessage" +static StrConstant ASYNC_ABORTCODE_STR = "abortCode" static StrConstant ASYNC_WLCOUNTER_STR = "workloadClassCounter" ///@} @@ -86,13 +87,8 @@ End /// @brief Prototype function for an async readout function /// -/// @param dfr reference to returned data folder from thread -/// @param err error code, only set in the error case -/// @param errmsg error message, only set in the error case -Function ASYNC_ReadOut(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +/// @param ar ASYNC readout structure +Function ASYNC_ReadOut(STRUCT ASYNC_ReadOutStruct &ar) End /// @brief thread function that receives data folders from the thread input queue @@ -134,19 +130,26 @@ threadsafe static Function/DF ASYNC_Run_Worker(DFREF dfr) DFREF dfrInp = dfr:input DFREF dfrAsync = dfr:async - variable/G dfrAsync:$ASYNC_ERROR_STR = 0 - NVAR err = dfrAsync:$ASYNC_ERROR_STR - string/G dfrAsync:$ASYNC_ERRORMSG_STR = "" - SVAR errmsg = dfrAsync:$ASYNC_ERRORMSG_STR + variable/G dfrAsync:$ASYNC_RTERROR_STR = 0 + variable/G dfrAsync:$ASYNC_ABORTCODE_STR = 0 + string/G dfrAsync:$ASYNC_RTERRORMSG_STR = "" + NVAR rtErr = dfrAsync:$ASYNC_RTERROR_STR + NVAR abortCode = dfrAsync:$ASYNC_ABORTCODE_STR + SVAR rtErrMsg = dfrAsync:$ASYNC_RTERRORMSG_STR - err = 0 dfrOut = $"" AssertOnAndClearRTError() try dfrOut = f(dfrInp); AbortOnRTE catch - errmsg = GetRTErrMessage() - err = ClearRTError() + rtErrMsg = GetRTErrMessage() + rtErr = ClearRTError() + if(!rtErr) + rtErrMsg = "" + endif + if(V_AbortCode != ABORTCODE_ABORTONRTE) + abortCode = V_AbortCode + endif endtry if(IsFreeDatafolder(dfrOut)) @@ -168,7 +171,8 @@ Function ASYNC_ThreadReadOut() variable justBuffered variable wlcIndex, statCnt, index - string rterrmsg + string msg + STRUCT ASYNC_ReadOutStruct ar NVAR tgID = $GetThreadGroupID() ASSERT(!isNaN(tgID), "Async frame work is not running") WAVE/DF DFREFbuffer = GetDFREFBuffer(getAsyncHomeDF()) @@ -238,19 +242,25 @@ Function ASYNC_ThreadReadOut() track[%$workloadClass][%OUTPUTCOUNT] += 1 - SVAR RFunc = dfr:$ASYNC_READOUTFUNC_STR - FUNCREF ASYNC_ReadOut f = $RFunc - NVAR err = dfr:$ASYNC_ERROR_STR - SVAR errmsg = dfr:$ASYNC_ERRORMSG_STR + SVAR RFunc = dfr:$ASYNC_READOUTFUNC_STR + FUNCREF ASYNC_ReadOut f = $RFunc + NVAR rtErr = dfr:$ASYNC_RTERROR_STR + SVAR rtErrMsg = dfr:$ASYNC_RTERRORMSG_STR + NVAR abortCode = dfr:$ASYNC_ABORTCODE_STR + + ar.dfr = dfrOut + ar.rtErr = rtErr + ar.rtErrMsg = rtErrMsg + ar.abortCode = abortCode statCnt += 1 AssertOnAndClearRTError() try - f(dfrOut, err, errmsg); AbortOnRTE + f(ar); AbortOnRTE catch - rterrmsg = GetRTErrMessage() - ClearRTError() - ASSERT(0, "ReadOut function " + RFunc + " aborted with: " + rterrmsg) + msg = GetRTErrMessage() + ASSERT(!ClearRTError(), "ReadOut function " + RFunc + " encountered an RTE: " + msg) + ASSERT(!V_AbortCode, "ReadOut function " + RFunc + " aborted with code: " + GetErrMessage(V_AbortCode)) endtry endfor diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 0f35d397d6..455dcca979 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2307,3 +2307,13 @@ Constant SUTTER_MAX_MAX_TP_PULSES = 10000 Constant INVALID_SWEEP_NUMBER = -1 StrConstant PERCENT_F_MAX_PREC = "%.15f" + +/// @name Igor Internal Abort Codes +/// +/// @anchor IgorAbortCodes +///@{ +Constant ABORTCODE_ABORTONRTE = -4 +Constant ABORTCODE_ABORT = -3 +Constant ABORTCODE_STACKOVERFLOW = -2 +Constant ABORTCODE_USERABORT = -1 +///@} diff --git a/Packages/MIES/MIES_NeuroDataWithoutBorders.ipf b/Packages/MIES/MIES_NeuroDataWithoutBorders.ipf index 43637f68b5..dd2acfa9db 100644 --- a/Packages/MIES/MIES_NeuroDataWithoutBorders.ipf +++ b/Packages/MIES/MIES_NeuroDataWithoutBorders.ipf @@ -366,13 +366,14 @@ threadsafe Function/DF NWB_ASYNC_Worker(DFREF dfr) return $"" End -Function NWB_ASYNC_Readout(DFREF dfr, variable err, string errmsg) +Function NWB_ASYNC_Readout(STRUCT ASYNC_ReadOutStruct &ar) - if(!err) - return NaN + if(ar.rtErr) + BUG("Async jobs finished with RTE: " + ar.rtErrMsg) + endif + if(ar.abortCode) + BUG("Async jobs finished with Abort: " + GetErrMessage(ar.abortCode)) endif - - BUG("Async jobs finished with: " + errmsg) End threadsafe static Function NWB_WriteLabnoteBooksAndComments(STRUCT NWBAsyncParameters &s) diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index 5ef93391a7..54f43af94e 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -637,3 +637,11 @@ Structure SF_PlotMetaData string xAxisLabel // from SF_META_XAXISLABEL constant string yAxisLabel // from SF_META_YAXISLABEL constant EndStructure + +/// @brief ReadOut Structure for ASYNC +Structure ASYNC_ReadOutStruct + DFREF dfr // dfr with output data + variable rtErr // runtime error code + string rtErrMsg // runtime error message + variable abortCode // abort code +EndStructure diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index 575d11eea9..3bc62f89f0 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -179,26 +179,21 @@ End /// results are base line level, steady state resistance, instantaneous resistance and their positions /// collected results for all channels of a measurement are send to TP_RecordTP(), DQ_ApplyAutoBias() when complete /// -/// @param dfr output data folder from ASYNC frame work with results from workloads associated with this registered function -/// The output parameter in the data folder follow the definition as created in TP_TSAnalysis() -/// -/// @param err error code of TP_TSAnalysis() function -/// -/// @param errmsg error message of TP_TSAnalysis() function -Function TP_ROAnalysis(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +/// @param ar ASYNC_ReadOutStruct structure with dfr with input data +Function TP_ROAnalysis(STRUCT ASYNC_ReadOutStruct &ar) variable i, j, bufSize variable posMarker, posAsync, tpBufferSize variable posBaseline, posSSRes, posInstRes variable posElevSS, posElevInst - if(err) - ASSERT(0, "RTError " + num2str(err) + " in TP_Analysis thread: " + errmsg) + if(ar.rtErr || ar.abortCode) + ASSERT(!ar.rtErr, "TP analysis thread encountered RTE " + ar.rtErrMsg) + ASSERT(!ar.abortCode, "TP analysis thread aborted with code: " + GetErrMessage(ar.abortCode)) endif + DFREF dfr = ar.dfr + WAVE/SDFR=dfr inData = outData NVAR/SDFR=dfr now = now NVAR/SDFR=dfr hsIndex = hsIndex diff --git a/Packages/tests/Basic/UTF_AsynFrameworkTest.ipf b/Packages/tests/Basic/UTF_AsynFrameworkTest.ipf index 1a1fb0d902..085ea9b4e7 100644 --- a/Packages/tests/Basic/UTF_AsynFrameworkTest.ipf +++ b/Packages/tests/Basic/UTF_AsynFrameworkTest.ipf @@ -696,6 +696,44 @@ static Function TASYNC_WorkerNoDF() ASYNC_Stop(timeout = 1) End +/// @brief Test if errors in a thread is handled properly +// IUTF_TD_GENERATOR DataGenerators#GetASYNCThreadErrorFunctions +static Function TASYNC_WorkerThreadErrors([string str]) + + string myDF, worker, roFunc + DFREF threadDF + variable endtime, timeout + + worker = StringFromList(0, str, ",") + roFunc = StringFromList(1, str, ",") + ASYNC_Start(1) + threadDF = ASYNC_PrepareDF(worker, roFunc, "TASYNCTest") + ASYNC_AddParam(threadDF, var = 1) + Make/N=10 data + ASYNC_AddParam(threadDF, w = data, move = 1) + myDF = GetDataFolder(1) + ASYNC_AddParam(threadDF, str = myDF) + + ASYNC_Execute(threadDF) + + Make/N=0 returnOrder + endtime = datetime + THREADING_TEST_TIMEOUT + timeout = 0 + for(;;) + ASYNC_ThreadReadOut() + if(numpnts(returnOrder) == 1) + break + endif + if(datetime > endtime) + timeout = 1 + break + endif + endfor + CHECK(!timeout) + + ASYNC_Stop(timeout = 1) +End + /// @brief Test if workloads are correctly processed out of order /// note: due to the random order there is still a tiny chance that the processing is ordered /// In such case a warning is given. @@ -891,14 +929,15 @@ static Function TASYNC_RunErrorWorker() End /// @brief Test if a ReadOut function that generates a runtime error is properly caught. -static Function TASYNC_RunErrorReadOut() +// IUTF_TD_GENERATOR DataGenerators#GetASYNCReadOutErrorFunctions +static Function TASYNC_RunErrorReadOut([string str]) string myDF DFREF threadDF variable endtime, timeout ASYNC_Start(ThreadProcessorCount) - threadDF = ASYNC_PrepareDF("RunGenericWorker", "FailReadOut", "TASYNCTest") + threadDF = ASYNC_PrepareDF("RunGenericWorker", str, "TASYNCTest") ASYNC_AddParam(threadDF, var = 1) Make/N=10 data ASYNC_AddParam(threadDF, w = data, move = 1) @@ -1327,15 +1366,49 @@ threadsafe Function/DF RunGenericWorker5(dfr) return dfrOut End +/// @brief Worker that aborts after one second with an AbortOnValue +threadsafe Function/DF RunGenericWorkerAbortOnValue(DFREF dfr) + + variable now + + // sleep for 1s, Sleep does not work + // reliable here on Win7/wine + now = datetime + for(;;) + if(datetime > now + 1) + break + endif + endfor + + AbortOnValue 1, 2345 +End + +/// @brief Worker that creates an RTE 330 +threadsafe Function/DF RunGenericWorkerRTE(DFREF dfr) + + variable now + + // sleep for 1s, Sleep does not work + // reliable here on Win7/wine + now = datetime + for(;;) + if(datetime > now + 1) + break + endif + endfor + + WAVE/Z wv = $"" + wv[0] = 0 +End + /// @brief ReadOut function for combination with RunGenericWorker, order is saved in wave returnOrder -Function RunGenericReadOut(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function RunGenericReadOut(STRUCT ASYNC_ReadOutStruct &ar) variable size - CHECK(!err) + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 0) + DFREF dfr = ar.dfr SVAR/SDFR=dfr testDF = myDF NVAR/SDFR=dfr oID = outV @@ -1346,14 +1419,13 @@ Function RunGenericReadOut(dfr, err, errmsg) End /// @brief ReadOut function for combination with RunGenericWorker2, order is saved in wave returnOrder2 -Function RunGenericReadOut2(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function RunGenericReadOut2(STRUCT ASYNC_ReadOutStruct &ar) variable size - CHECK(!err) + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 0) + DFREF dfr = ar.dfr SVAR/SDFR=dfr testDF = myDF NVAR/SDFR=dfr oID = outV @@ -1365,14 +1437,13 @@ End /// @brief ReadOut function for combination with RunGenericWorker, order is saved in wave returnOrder /// Aborts at end to trigger an exception -Function RunGenericReadOutAbort(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function RunGenericReadOutAbort(STRUCT ASYNC_ReadOutStruct &ar) variable size - CHECK(!err) + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 0) + DFREF dfr = ar.dfr SVAR/SDFR=dfr testDF = myDF NVAR/SDFR=dfr oID = outV @@ -1401,12 +1472,11 @@ threadsafe Function/DF RunWorkerOfDOOM(dfr) End /// @brief ReadOut function for combination with RunWorkerOfDOOM, checks if Worker generated an error -Function RunReadOutOfDOOM(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function RunReadOutOfDOOM(STRUCT ASYNC_ReadOutStruct &ar) - CHECK_EQUAL_VAR(err, 330) + CHECK_EQUAL_VAR(ar.rtErr, 330) + CHECK_EQUAL_VAR(ar.abortCode, 0) + DFREF dfr = ar.dfr SVAR/SDFR=dfr testDF = myDF NVAR/SDFR=dfr oID = outV @@ -1435,33 +1505,28 @@ threadsafe Function InfiniteWorkerHelper_IGNORE() End /// @brief Empty ReadOut function -Function EmptyReadOut(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function EmptyReadOut(STRUCT ASYNC_ReadOutStruct &ar) End /// @brief Empty ReadOut function -Function ReadOutCheckDF(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function ReadOutCheckDF(STRUCT ASYNC_ReadOutStruct &ar) - CHECK(DataFolderExistsDFR(dfr)) + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 0) + CHECK(DataFolderExistsDFR(ar.dfr)) WAVE returnOrder Redimension/N=1 returnOrder End /// @brief ReadOut function for combination with RunGenericWorker that generates a runtime error 330 -Function FailReadOut(dfr, err, errmsg) - DFREF dfr - variable err - string errmsg +Function FailReadOut(STRUCT ASYNC_ReadOutStruct &ar) variable size - CHECK(!err) + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 0) + DFREF dfr = ar.dfr SVAR/SDFR=dfr testDF = myDF NVAR/SDFR=dfr oID = outV @@ -1473,3 +1538,42 @@ Function FailReadOut(dfr, err, errmsg) WAVE/Z w = $"" w[0] = 0xCAFEBABE End + +/// @brief ReadOut function for combination with RunGenericWorker that Aborts with code 1234 +Function FailReadOutAbort(STRUCT ASYNC_ReadOutStruct &ar) + + variable size + + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 0) + DFREF dfr = ar.dfr + + SVAR/SDFR=dfr testDF = myDF + NVAR/SDFR=dfr oID = outV + WAVE retOrder = $(testDF + "returnOrder") + size = numpnts(retOrder) + Redimension/N=(size + 1) retOrder + retOrder[size] = oID + // generate AbortCode + AbortOnValue 1, 1234 +End + +/// @brief ReadOut function for combination with RunGenericWorkerAbortOnValue that receives abortCode 2345 from thread +Function FailThreadReadOutAbortOnValue(STRUCT ASYNC_ReadOutStruct &ar) + + CHECK_EQUAL_VAR(ar.rtErr, 0) + CHECK_EQUAL_VAR(ar.abortCode, 2345) + + WAVE returnOrder + Redimension/N=1 returnOrder +End + +/// @brief ReadOut function for combination with RunGenericWorkerAbortOnValue that receives abortCode 2345 from thread +Function FailThreadReadOutRTE(STRUCT ASYNC_ReadOutStruct &ar) + + CHECK_EQUAL_VAR(ar.rtErr, 330) + CHECK_EQUAL_VAR(ar.abortCode, 0) + + WAVE returnOrder + Redimension/N=1 returnOrder +End diff --git a/Packages/tests/UTF_DataGenerators.ipf b/Packages/tests/UTF_DataGenerators.ipf index 6dd8dedcd8..9b8898a2d5 100644 --- a/Packages/tests/UTF_DataGenerators.ipf +++ b/Packages/tests/UTF_DataGenerators.ipf @@ -876,3 +876,17 @@ static Function/WAVE GetBasicMathOperations() return op End + +static Function/WAVE GetASYNCReadOutErrorFunctions() + Make/FREE/T wt = {"FailReadOutAbort", "FailReadOut"} + SetDimensionLabels(wt, TextWaveToList(wt, ";"), ROWS) + + return wt +End + +static Function/WAVE GetASYNCThreadErrorFunctions() + Make/FREE/T wt = {"RunGenericWorkerAbortOnValue,FailThreadReadOutAbortOnValue,", "RunGenericWorkerRTE,FailThreadReadOutRTE,"} + SetDimensionLabels(wt, TextWaveToList(wt, ";"), ROWS) + + return wt +End