Error: Wave is in use by preemptive thread. Can't be resized or killed.
Hi
I am getting this error executing some code. It runs fine normally, but after running for a while on many gigabytes of data (around 500 GB) I get this error: Wave is in use by preemptive thread. Can't be resized or killed.
The code is essentially for detecting corrupted data in frames of video data
The error occurs on the following line:
make /O /N=(NumOut) FSSeq
The 4 arrays FSSeq, FESeq, LSSeq, and TSSeq are used in the multithreaded and threadsafe FrameData_AnalyzeFrame function
The starting point for execution is the FrameData_Analyze_Example function
Due to the amount of data required to set get this error, its a bit hard to give you any firm examples. Each of the data files we process (SrcWave) are generally just under 4GB of 16 bit unsigned integers loaded from hdf5 files. The SrcWave for each file process is deleted but the summary arrays (FrameData_AnalyzeSummary) created for each file are renamed and kept
Any suggestions would be welcome
Thanks
Kent
#pragma rtGlobals=1 // Use modern global access method.
// Example Usage
function FrameData_Analyze_Example(SrcWave, [SkipAlign, KeepSrc, KeepRes])
wave SrcWave // Aligned or unaligned data can be 1d or 2D, Must be 2D if SkipAlign is set
variable SkipAlign // If set skips the frame alignment process
variable KeepSrc,KeepRes // Setting KeepSrc keeps the SrcWave, Setting KeepRes keeps the FrameData_AnalyzeResults wave
If (ParamIsDefault(SkipAlign))
SkipAlign = 0
EndIf
if (paramIsDefault(KeepSrc))
KeepSrc=0
endif
if (paramIsDefault(KeepRes))
KeepRes=0
endif
string refResultsName = "FrameData_ResultsRef"
FrameData_MakeResults(refResultsName)
wave refResults = $refResultsName
// set values to -1 to not use that value to find bad frames
// values greater than -1 consider a frame bad if the results dont match
refResults[%FSCnt] = 1
refResults[%TSCnt] = 1
refResults[%LSCnt] = 1024
refResults[%FECnt] = 1
refResults[%FSInitIdx] = 520
refResults[%TSInitIdx] = 0
refResults[%FEInitIdx] = 4203024
refResults[%LSInitIdx] = 528
refResults[%LSFinIdx] = 4198920
refResults[%LSIdxDifMin] = 4104
refResults[%LSIdxDifMax] = 4104
refResults[%LSIdxDifAvg] = 4104
refResults[%BadLine] = 0
refResults[%NumWord] = -1
refResults[%NumSamp] = 4203032
refResults[%NumDisp] = 0
refResults[%NumK] = 16432
// Fill these in so the code can identify the framing characters
variable FSCode = 0x5C5C
variable FECode = 0x9C9C
variable LSCode = 0x7C7C
variable TSCode = 0xDCDC
variable FBCode = TSCode // Frame Begin char (First char of the Frame)
variable NumOut = 8 // 8 outputs from this
FrameData_Analyze(SrcWave,NumOut,FSCode,FECode,LSCode,TSCode,refResults,KeepSrc=KeepSrc,KeepRes=KeepRes)
end
// End of example Usage
// This analyzes the framing data of a 2D data set and characterizes it for each frame
// non -1 values in the RefResults array are then used to flag bad frames for inspection.
// Bad frames are summarized in the FrameData_AnalyzeSummary array
// This functions output 2 arrays. FrameData_AnalyzeSummary and FrameData_AnalyzeResults
// FrameData_AnalyzeResults lists the analyzed results for every frame. This wave is normally deleted but can be kept by setting KeepRes=1
// FrameData_AnalyzeSummary lists the RefResults and the analyzed results for any from that does not match the set parameters in RefResults
Function FrameData_Analyze(SrcWave,NumOut,FSCode,FECode,LSCode,TSCode,RefResults,[KeepSrc,KeepRes,OutStatSize,OutStat32])
wave SrcWave // Aligned 2D Source Wave
variable NumOut // Number of FPA Outputs
variable FSCode,FECode,LSCode,TSCode // FrameStart Code, Frame End Code, Line Start code, Telem Start code
wave RefResults // 1D FrameData_Results array used to ignore good frames in the summary results array
variable KeepSrc,KeepRes // Setting KeepSrc keeps the SrcWave, Setting KeepRes keeps the FrameData_Results wave
variable OutStat32 // Overrides the default OutStats Word size (1 = 32 bit, 0 = 16 bit) (Autocalculated)
variable OutStatSize // Overrides the default OutStats size (4 for 32 bit, 8 for 18 bit)
//Variable RunTime = datetime
variable FrSize = DimSize(SrcWave,0)
variable LineLen = RefResults[%LSIdxDifAvg]
if (paramIsDefault(KeepSrc))
KeepSrc=0
endif
if (paramIsDefault(KeepRes))
KeepRes=0
endif
variable debugval = WaveType(SrcWave)
if (paramIsDefault(OutStat32))
if ( WaveType(SrcWave) == 0x60 )
OutStat32 = 1
elseif ( WaveType(SrcWave) == 0x50 )
OutStat32 = 0
else
Print ("\rFrameData_Analyze Error: OutStat32 can't be deduced. Please specify it with the optional OutStat32 parameter.\r")
return 0
endif
endif
if (paramIsDefault(OutStatSize))
if ( OutStat32 == 1 )
OutStatSize = 4
else
OutStatSize = 8
endif
endif
if ( WaveDims(SrcWave) != 2 )
Print ("\rFrameData_Analyze Error: SrcWave must be a 2D wave to be analyzed.\r")
return 0
endif
make /O /N=(NumOut) FSSeq
make /O /N=(NumOut) FESeq
make /O /N=(NumOut) LSSeq
make /O /N=(NumOut) TSSeq
FSSeq = FSCode
FESeq = FECode
LSSeq = LSCode
TSSeq = TSCode
string resultsName = "FrameData_AnalyzeResults"
FrameData_MakeResults(resultsName, Size=DimSize(SrcWave,1))
wave results = $resultsName
// Multithread Analyzing each frame of data
Variable nthreads= ThreadProcessorCount
Variable threadGroupID= ThreadGroupCreate(nthreads)
variable ii, frameIdx
variable numFrames = DimSize(SrcWave,1)
for(frameIdx=0; frameIdx < numFrames;)
for(ii=0; ii<nthreads; ii+=1)
if (frameIdx < numFrames) // Data Maps
ThreadStart threadGroupID,ii,FrameData_AnalyzeFrame(SrcWave,NumOut,FSSeq,FESeq,LSSeq,TSSeq,frameIdx,LineLen,OutStatSize,OutStat32,results)
else
break
endif
frameIdx++
endfor
variable threadGroupStatus = 0
do
threadGroupStatus = ThreadGroupWait(threadGroupID,100)
while( threadGroupStatus != 0 )
endfor
Variable dummy= ThreadGroupRelease(threadGroupID)
resultsName = "FrameData_AnalyzeSummary"
FrameData_MakeResults(resultsName, Size=1)
wave summary = $resultsName
// Stuff the RefResults into the first index of the Summary array for reference
summary[][0] = RefResults[P]
summary[%FRNum][0] = NaN
SetDimLabel 1, 0, RefResults, summary
string dimLabel
variable numBad = 0
for(frameIdx=0; frameIdx < numFrames;frameidx++)
for(ii=0; ii<DimSize(results,0); ii+=1)
if((RefResults[ii] >= 0) && (RefResults[ii] != results[ii][frameIdx]))
numBad++
InsertPoints /M=1 numBad, 1, summary
summary[][numBad] = results[P][frameidx]
dimLabel = "BadFr_" + num2str(numBad)
SetDimLabel 1, numBad, $dimLabel, summary
break
endif
endfor
endfor
KillWaves FSSeq, FESeq, LSSeq, TSSeq
if (KeepSrc == 0)
KillWaves SrcWave
endif
if (KeepRes == 0)
KillWaves results
endif
//Printf "RunTime: %d\r" , datetime-RunTime
Printf "%d Bad Frames Found\r" , numBad
return numBad
end
// This function gets called. Not for direct use
threadsafe Function FrameData_AnalyzeFrame(SrcWave,NumOut,FSSeq,FESeq,LSSeq,TSSeq,FrameIdx,LineLen,OutStatSize,OutStat32,AnalyzeResults)
wave SrcWave
variable NumOut
wave FSSEQ,FESEQ,LSSeq,TSSeq
variable FrameIdx
variable LineLen
variable OutStatSize, OutStat32
wave AnalyzeResults
variable FrLen = DimSize(SrcWave,0)
variable ii
make /O /N=(NumOut) CmpWave
variable FSCnt = 0
variable TSCnt = 0
variable LSCnt = 0
variable FECnt = 0
variable FSInitIdx = -1
variable TSInitIdx = -1
variable LSInitIdx = -1
variable LSFinIdx = -1
variable LSIdxDifSum = 0
variable LSIdxDifMax = 0
variable LSIdxDifMin = 0xFFFFFFFF
variable FEInitIdx = -1
variable BadLine = 0
variable LSDiff = 0
for (ii=0;ii<FrLen;ii+=NumOut)
CmpWave[] = SrcWave[ii+P][FrameIdx]
If ((EqualWaves(CmpWave,FSSeq,1)) == 1)
FSCnt++
FSInitIdx = ((FSInitIdx < 0) ? ii : FSInitIdx)
endif
If ((EqualWaves(CmpWave,FESeq,1)) == 1)
FECnt++
FEInitIdx = ((FEInitIdx < 0) ? ii : FEInitIdx)
endif
If ((EqualWaves(CmpWave,LSSeq,1)) == 1)
LSCnt++
if (LSCnt == 1)
LSInitIdx = ii
else
LSDiff = ii - LSFinIdx
LSIdxDifSum += LSDiff
LSIdxDifMin = (LSDiff < LSIdxDifMin) ? LSDiff : LSIdxDifMin
LSIdxDifMax = (LSDiff > LSIdxDifMax) ? LSDiff : LSIdxDifMax
BadLine = (((LineLen != LSDiff)) ? BadLine+1 : BadLine )
endif
LSFinIdx = ii
endif
If ((EqualWaves(CmpWave,TSSeq,1)) == 1)
TSCnt++
TSInitIdx = ((TSInitIdx < 0) ? ii : TSInitIdx)
endif
endfor
AnalyzeResults[%FRNum][FrameIdx] = FrameIdx
AnalyzeResults[%FSCnt][FrameIdx] = FSCnt
AnalyzeResults[%TSCnt][FrameIdx] = TSCnt
AnalyzeResults[%LSCnt][FrameIdx] = LSCnt
AnalyzeResults[%FECnt][FrameIdx] = FECnt
AnalyzeResults[%FSInitIdx][FrameIdx] = FSInitIdx
AnalyzeResults[%TSInitIdx][FrameIdx] = TSInitIdx
AnalyzeResults[%FEInitIdx][FrameIdx] = FEInitIdx
AnalyzeResults[%LSInitIdx][FrameIdx] = LSInitIdx
AnalyzeResults[%LSFinIdx][FrameIdx] = LSFinIdx
AnalyzeResults[%LSIdxDifMin][FrameIdx] = LSIdxDifMin
AnalyzeResults[%LSIdxDifMax][FrameIdx] = LSIdxDifMax
AnalyzeResults[%LSIdxDifAvg][FrameIdx] = (LSCnt > 1) ? LSIdxDifSum/(LSCnt-1) : 0
AnalyzeResults[%BadLine][FrameIdx] = (LineLen > 0) ? BadLine : 0
if (((OutStatSize > 4) && (OutStatSize != 0)) || ((OutStatSize > 8) && (OutStatSize == 0)))
AnalyzeResults[%NumWord][FrameIdx] = 0
AnalyzeResults[%NumSamp][FrameIdx] = 0
AnalyzeResults[%NumDisp][FrameIdx] = 0
AnalyzeResults[%NumK][FrameIdx] = 0
for (ii=0;ii<NumOut;ii+=1)
if (OutStat32 == 0)
AnalyzeResults[%NumWord][FrameIdx] += (SrcWave[FrLen-7*NumOut + ii][FrameIdx] << 16) + SrcWave[FrLen-8*NumOut + ii][FrameIdx]
AnalyzeResults[%NumSamp][FrameIdx] += (SrcWave[FrLen-5*NumOut + ii][FrameIdx] << 16) + SrcWave[FrLen-6*NumOut + ii][FrameIdx]
AnalyzeResults[%NumDisp][FrameIdx] += (SrcWave[FrLen-3*NumOut + ii][FrameIdx] << 16) + SrcWave[FrLen-4*NumOut + ii][FrameIdx]
AnalyzeResults[%NumK][FrameIdx] += SrcWave[FrLen-2*NumOut + ii][FrameIdx]
else
AnalyzeResults[%NumWord][FrameIdx] += SrcWave[FrLen-4*NumOut + ii][FrameIdx]
AnalyzeResults[%NumSamp][FrameIdx] += SrcWave[FrLen-3*NumOut + ii][FrameIdx]
AnalyzeResults[%NumDisp][FrameIdx] += SrcWave[FrLen-2*NumOut + ii][FrameIdx]
AnalyzeResults[%NumK][FrameIdx] += (SrcWave[FrLen-1*NumOut + ii][FrameIdx] && 0xFFFF0000) >> 16
endif
endfor
endif
killwaves CmpWave
return 0
end
// Using this to create the Results/Ref waves allows them to be modified in the future and keep the code
// Backward compatible
function FrameData_MakeResults(Name, [Size])
string Name // The name of the array created
variable Size // If set the returned array will be 2D with Dim 1 set by size (Results array type) Default for 1D array (Ref array type)
variable NumParams = 18
if (paramIsDefault(Size))
make /O /N=((NumParams)) $Name
else
make /O /N=((NumParams),Size) $Name
endif
wave FrameData_Results = $Name
FrameData_Results = (paramIsDefault(Size)) ? -1 : NaN
if (!paramIsDefault(Size))
SetDimLabel 1, -1, FrIdx, FrameData_Results // Dim 1 - Frame Index
endif
SetDimLabel 0, 0, FrNum, FrameData_Results // Dim 0 Idx 0 - Frame Number Index Index
SetDimLabel 0, 1, FSCnt, FrameData_Results // Dim 0 Idx 1 - Number of Frame Starts detected
SetDimLabel 0, 2, TSCnt, FrameData_Results // Dim 0 Idx 2 - Number of Telem Starts detected
SetDimLabel 0, 3, FECnt, FrameData_Results // Dim 0 Idx 3 - Number of Frame Ends detected
SetDimLabel 0, 4, LSCnt, FrameData_Results // Dim 0 Idx 4 - Number of Line Starts detected
SetDimLabel 0, 5, FSInitIdx, FrameData_Results // Dim 0 Idx 5 - Offset from start of frame to the first Frame Start character
SetDimLabel 0, 6, TSInitIdx, FrameData_Results // Dim 0 Idx 6 - Offset from start of frame to the first Telem Start character
SetDimLabel 0, 7, FEInitIdx, FrameData_Results // Dim 0 Idx 7 - Offset from start of frame to the first Frame End character
SetDimLabel 0, 8, LSInitIdx, FrameData_Results // Dim 0 Idx 8 - Offset from start of frame to the first Line Start character
SetDimLabel 0, 9, LSFinIdx, FrameData_Results // Dim 0 Idx 9 - Offset from start of frame to the last Line Start character
SetDimLabel 0, 10, LSIdxDifMin, FrameData_Results // Dim 0 Idx 10 - Minimum distance from one Line Start to the next Line Start
SetDimLabel 0, 11, LSIdxDifMax, FrameData_Results // Dim 0 Idx 11 - Maximum distance from one Line Start to the next Line Start
SetDimLabel 0, 12, LSIdxDifAvg, FrameData_Results // Dim 0 Idx 12 - Average distance from one Line Start to the next Line Start
SetDimLabel 0, 13, BadLine, FrameData_Results // Dim 0 Idx 13 - Number of Lines where the distance to the last Line Start to the current Line Start is not the passed in value
SetDimLabel 0, 14, NumWord, FrameData_Results // Dim 0 Idx 14 - Total number of words in the frame (All words - Idles + Data)
SetDimLabel 0, 15, NumSamp, FrameData_Results // Dim 0 Idx 15 - Total number of data words in the frame (Data words - Non Idle words)
SetDimLabel 0, 16, NumDisp, FrameData_Results // Dim 0 Idx 16 - Total number of words with disparity errors in the frame
SetDimLabel 0, 17, NumK, FrameData_Results // Dim 0 Idx 17 - Total number of K code words in the frame
end
Seems like the only way that error would happen is if one of your threads is still running. But that would seem to indicate that the loop calling `ThreadGroupWait()` exited without getting a zero return, which doesn't seem possible. Can you print out the value of the variable `dummy`? Is it ever non-zero?
The fact that this problem appears only after a lot of processing of huge waves suggests some sort of memory leak (perhaps). That might simply make Igor erratic. It's hard to test for such problems since it requires running with lots of huge waves.
Are you able to create a test of some sort? Can you manufacture dummy input that exercises the code in the same way as your real data? If you can write code to create the needed input for such a test, then it might be feasible to test it here at WaveMetrics.
May 16, 2025 at 09:41 am - Permalink
This code generates the "Wave is in use by pre-emptive thread" error the second time I run it. It uses an Abort to abort the function before ThreadGroupRelease is called. I think any error could take the place of the abort but I would expect that the user would get the original error message before getting the "Wave is in use by pre-emptive thread" error.
Executing this kills all threads and allows the function to run again without throwing the "Wave is in use by pre-emptive thread" error:
Doing "New Experiment" also kills all threads.
May 16, 2025 at 11:04 am - Permalink
We will try the ThreadGroupRelease(-2) and see if we can execute after. The error doesn't really happen until we have been processing continuously for 4-5 hours on beefy ~200 ThreadProcessorCount machines. I will see if I can come up with a test case. Generally to recover from the error we have to save the experiment, exit igor, restart Igor, then kill the XXseq waves. Next time it happens we will do a little bit of diagnosing to get more details
Thanks for the insights
May 16, 2025 at 06:25 pm - Permalink
> The error doesn't really happen until we have been processing continuously for 4-5 hours on beefy ~200 ThreadProcessorCount machines.
Is that on Windows? I'm asking because here I can only use up to 64 cores in IP.
Regarding your threadsafe functions I would think you can get it faster quite a bit more:
- Use FindDimLabel to translate dimension labels to numeric indices and prefer that in inner loops
- Precompute things like
- I would playaround with translating
to something use implied loops (p, q, r, s). So that you don't need the inner for loop. Something like
could be a start. Or maybe also matrixop would help.
June 3, 2025 at 12:21 pm - Permalink
Hi Thomas
Thanks for the input
It is running on windows. I will have to look into how many threades our higher core count machines were being given. My understanding is the number of threads caps out at 200 but I have not looked specifically at that in a bit. I do know when they are processing task manager shows the cores are 100% utilized. But again something I will check into
As for the optimizations, by far and away the lions share of the processing time is happening in the for loop early in the threadsafe function just after the variable declarations and in that I try to keep the janky dereferencing to a minimum
For instance in a single threadsafe execution the initial for loop may have a million or more iterations while the for loop working on AnalyzeResults might run 4 or 8 times
The threadsafe function itself is generally called less than 1000 times per data set
I have always wonderered what if any optimizations the scripting language is able to accomplish, but I generally assume it's not able to do any and I arrange my code correspondingly.
June 3, 2025 at 02:30 pm - Permalink
Prior to Windows 11, an application was limited to 64 logical processors unless it had code to specifically allow it to use more, which Igor does not have. Starting with Windows 11 (and Windows Server 2022) it is no longer necessary for an application to use special code to access more than 64 logical processors. See https://learn.microsoft.com/en-us/windows/win32/procthread/processor-gr… for the details.
We don't have access to a machine with more than 64 logical processors, so we have no idea how this works when running Igor on Windows 11.
Note that Igor does limit the number of threads to 100 in most situations, including the MultiThread keyword for wave assignment and ThreadGroupCreate. That limit was primarily to prevent the user from accidentally trying to use an inappropriate number of threads and probably should be raised in Igor Pro 10, currently in beta.
If you're willing to beta test IP10, please sign up at https://www.wavemetrics.com/form/igor-pro-10-beta-tester-signup. In the comments, please mention that you're interested in testing a version of Igor that has an increased value for MAXIMUM_NUMBER_OF_THREADS. I'll get in touch with you about testing.
Note that Igor's ThreadProcessorCount function will return the actual number of logical processors, not the number that Igor can use.
Regarding Igor doing optimizations, your assumption is correct. Igor's compiler doesn't do much to optimize your code.
June 4, 2025 at 09:53 am - Permalink
We had enquired about this a few years ago and I had thought the number was 200 but I am clearly mistaken. Next time I am with the machines that do that stuff I will do some investigation of both cpu usage and the answers I am getting in Igor code. We had discussed with you guys about upping the thread count while Igor 9 was being developed and I guess I was assuming the limit was changed. I'll sign up for the beta and see what I can do to help test
The newest system we have is running a single threadripper pro 7995 WX with 192 threads. In the past it has been multi xeon cpu systems with similar or higher thread counts. For reference it takes it about 3 seconds to run that routine on a 4 GB data file
Thanks
Kent
June 4, 2025 at 10:35 am - Permalink