Prompt function is not allowed in ThreadSafe function

viralvector
Posts: 61
Joined: 2014-01-01
Location: Canada

I tried to create two inputs of variables for calculating mean square error in Threadsafe function but it failed.

Apparently, I cannot use the function of prompt in ThreadSafe...
e.g.

ThreadSafe Function ThreadWorker(wIn, wln_control, waveIndex,xr,xl)
variable xr,xl
....
Prompt xl, "Enter left bracket:"
Prompt xr, "Enter right bracket:"
DoPrompt "Enter left and right", xl, xr
wave w1 = wln_control
duplicate/R=(xl, xr)/FREE w1, rw, bdw
duplicate/R=(xl, xr)/FREE wIn, tdw
rw = (bdw - tdw)^2 / numpnts(rw)
Variable MeanSquare = sum(rw)
....

Any workaround ideas?

Thanks in advance.


[ last edited June 5, 2014 - 14:43 ]
Posts: 1852
Joined: 2007-06-29
Location: United States

No user interface is allowed in a thread.

It doesn't really make sense to ask for user input from a thread- that's a technique to gain high performance. Waiting for a user to enter data is not high performance.

I presume that what you want is to thread a computation for speed, but that computation needs user input. Your prompts need to be in the driver function so that the inputs can be collected first, then passed into the thread worker functions, possibly via input parameters.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com


viralvector
Posts: 61
Joined: 2014-01-01
Location: Canada

johnweeks wrote:
No user interface is allowed in a thread.

It doesn't really make sense to ask for user input from a thread- that's a technique to gain high performance. Waiting for a user to enter data is not high performance.

I presume that what you want is to thread a computation for speed, but that computation needs user input. Your prompts need to be in the driver function so that the inputs can be collected first, then passed into the thread worker functions, possibly via input parameters.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com

Your presumption is right! but I don't know how to integrate the function into the driver function.

This is what I have tried, and I received:
"Expected operand"
Igor found an operator where it expected an operand.
This can also happen if you forget to use a comma after a numeric operand

Here is the code:

#pragma rtGlobals=3		// Use modern global access method and strict wave access.
#pragma rtGlobals=3		// Use modern global access method and strict wave access.
#pragma rtGlobals=3		// Use modern global access method and strict wave access.
 
Menu "SME_PEAK"
	"Multi-CPUs MSE & Peak", ThreadMSE_PEAK()
End
 
// These control various troubleshooting items and other aspects of the demonstration
Constant kNumDatasetsToProcess =10	// Number of sample input waves to create and process
Constant kSampleWaveNumPoints = 1000		// Number of points in each sample input wave
Constant kPrintStartThreadMessage = 1
Constant kPrintGotThreadDFMessage = 1
Constant kSimulateVaryingComputationTime = 1
 
ThreadSafe Function ThreadWorker(wIn, wln_control, waveIndex,xr,xl)
	variable xr,xl
	wave wIn
	WAVE wln_control
	Variable waveIndex
 
	String NameWave = NameofWave(wIn)
   Wavestats/q/z wIn
	variable xPeak = V_maxloc
	Variable i,n=numpnts(wIn), massTimesX=0
	for(i=0; i<n; i+=1)
		massTimesX += wIn[i]*pnt2x(wIn,i)
	endfor
	Variable totalMass=sum(wIn)
	Variable centerOfMass= massTimesX/totalMass
//SP nile red living 
//Variable xl=30
//Variable xr =50
 
//SP nile red living photodamage
//Variable xl=20
//Variable xr =55
 
//SP nile red
//Variable xl=30
//Variable xr =50
 
//SP nile red solvents
//Variable xl=5
//Variable xr =90
 
//C102 solvents
//Variable xl=400
//Variable xr =580
 
//	Nile Blue 404
//	Variable xl=550
//	Variable xr =700
 
//NB 561
 
//	Variable xl=600
//	Variable xr =700
//	Nile red
//	Variable xl=530
//	Variable xr =680
//	Nile red solvents
 
//	Variable xl=500
//Variable xr =700
      // wave  w2=  wln
 
//     Prompt xl, "Enter left bracket:"
//Prompt xr, "Enter right bracket:"
//DoPrompt "Enter left and right", xl, xr
 
    	wave w1 = wln_control
	duplicate/R=(xl, xr)/FREE w1, rw, bdw
	duplicate/R=(xl, xr)/FREE wIn, tdw
 
	rw = (bdw - tdw)^2 / numpnts(rw)
	Variable MeanSquare = sum(rw)
//	print wIn
 
 
	// This is used to simulate varying computation time
	if (kSimulateVaryingComputationTime > 0)
		Variable randomSecs = abs(enoise(kSimulateVaryingComputationTime))
		Sleep/S randomSecs
	endif
 
	//Variable wMin = WaveMin(wIn)
	//Variable wMax = WaveMax(wIn)
 
	// Create data folder to return to calling thread
	NewDataFolder/S output
 
	// Create output variables
	// Variable/G gMin = wMin
	Variable/G gMeanSquare = MeanSquare//centerOfMass
	Variable/G gxPeak = xPeak
      String/G gNameWave=NameWave
 
	// This tells the calling thread which wave was processed
	Variable/G gWaveIndex = waveIndex
 
	// Send data back to main thread
	ThreadGroupPutDF 0, :
 
	return 0		// Success
End
 
 
Function ThreadMSE_PEAK(xr,xl)
 
variable xl
variable xr
prompt xl,"enter xl"
Prompt xr,"enter xr"
Doprompt "Enter values", xl,xr
return xl&xr
 
 
	if (IgorVersion() < 6.23)
		Abort "This demo requires Igor Pro 6.23 or later."
	endif
 
 
	Variable t0 = StopMSTimer(-2)		// Used to time the function
 
 
	//if (V_flag)
//	 return -1
  //   endif 
 
	DFREF originalDFR = GetDataFolderDFR()
 
	DFREF outputDFR = GetDataFolderDFR()
 
	Variable numDatasetsToProcess = kNumDatasetsToProcess
	Variable numDatasetsProcessed = 0
     Variable numPointsInSampleData = kSampleWaveNumPoints
	// Make output wave
	Make/O/D/N=(numDatasetsToProcess,2)  outputDFR:results = NaN
 
	wave results= outputDFR:results
	SetDimLabel 0,0,MeanSquareError,results
	SetDimLabel 0,1,SP_Peak,results
	make/O /T/N=(numDatasetsToProcess)  outputDFR:resultstext
	Wave/t resultstext = outputDFR:resultstext
	DoWindow /F OutputTable
	if (V_flag == 0)
		Edit /N=OutputTable /W=(466,49,906,652) results.ld
		Edit/N=OutputTableText resultstext
	endif
 
	// Look for sample data
 
//	NewDataFolder /O :SampleData
//
   DFREF inputDFR = :
//    String name 
//     Variable i
//    for(i=0; i<numDatasetsToProcess; i+=1)
 
  //  sprintf name, "(%d)", i		// wave0, wave1
//Make/O/D/N=(numPointsInSampleData) inputDFR:$name = gnoise(1)
 
 // SetDimLabel 0,i,$name,results
 
//   endfor
 
 
	// Create threads
	Variable numThreads = ThreadProcessorCount
	Variable threadGroupID = ThreadGroupCreate(numThreads)
 
	SetDataFolder inputDFR		// Needed for WaveRefIndexed
 
	// Dispatch threads and collect output
 
	Variable threadIndex
   Variable waveIndex  = 0
 
 
	 //control
    	wave wln_control =  WaveRefIndexed("", 0, 1)
 
	do
		// Start any free threads
		do
			if (waveIndex >= numDatasetsToProcess)
				break								// No more input data to dispatch to a thread
			endif
 
			// Find a free thread
			threadIndex = ThreadGroupWait(threadGroupID, -2)		// Requires Igor Pro 6.23
			threadIndex -= 1	// Because ThreadGroupWait returns threadIndex+1
			if (threadIndex < 0)
				break			// No free threads
			endif
 
			// Start thread
			Wave wIn = WaveRefIndexed("", waveIndex, 1)
 
		//	ThreadStart threadGroupID, threadIndex, ThreadWorker(wIn, wln_control, waveIndex,xl,xr)
		ThreadStart threadGroupID, threadIndex, ThreadWorker(wIn, wln_control, waveIndex,xr,xl)
			if (kPrintStartThreadMessage)
				Printf "Started thread %d\r", threadIndex		// For debugging only
			endif
			waveIndex += 1
		while(1)
 
	DFREF dfr = ThreadGroupGetDFR(threadGroupID, 0)	
		// create results as a 2 column matrix
//Make/O/D/N=(numDatasetsToProcess,2)  outputDFR:results = NaN
// create gListInfo to store content in the same place that you create gCenterOfMass
// make sure it is initialized to be blank
// String/G gListInfo = ""
// 
//if (DataFolderRefStatus(dfr) != 0)
//// Process results
// NVAR gcenterOfMass = dfr:gcenterOfMass
// NVAR gxPeak = dfr:gxPeak
// NVAR gWaveIndex = dfr:gWaveIndex
//SVAR gNameWave= dfr:gNameWave
//SVAR gNameWave= dfr:gListInfo
////	NVAR/DFREF=dfr gcenterOfMass, gxPeak, gWaveIndex
////	SVAR/DFREF=dfr gNameWave, gListInfo   // create gListInfo when you create gNameWave
//	results[gWaveIndex][0] = gcenterOfMass
//	results[gWaveIndex][1] = gxPeak
////  gListInfo += num2str(gWaveIndex) + "=" + gNameWave + ";" // one option with num2str (use this or below, not both!)
//	sprintf( "%s%d=%s;",gListInfo,gWaveIndex,gNameWave) // alternative with sprint (use this or above, not both!)
//	
//		
		// See if thread output is available
 
		if (DataFolderRefStatus(dfr) != 0)
			// Process results
			NVAR gMeanSquare = dfr:gMeanSquare
		NVAR gxPeak = dfr:gxPeak
			SVAR gNameWave= dfr:gNameWave
			NVAR gWaveIndex = dfr:gWaveIndex
 
 
			results[gWaveIndex][0] = gMeanSquare
		results[gWaveIndex][1] = gxPeak
	  resultstext[gWaveIndex][0] =gNameWave
			// Update the output table
			DoUpdate
 
			// For debugging only
			if (kPrintGotThreadDFMessage)
				Printf "%d %s : MeanSquare=%g, Peak=%g\r", gWaveIndex, gNameWave,gMeanSquare, gxPeak
			endif
 
			numDatasetsProcessed += 1
 
			// This is a free data folder that would be killed automatically by Igor.
			// Nonetheless we explicitly kill it here and now.
			KillDataFolder dfr
		endif
	while(numDatasetsProcessed < numDatasetsToProcess)
 
	Variable dummy = ThreadGroupRelease(threadGroupID)
 
	Variable t1 = StopMSTimer(-2)
	Variable elapsedTime = (t1 - t0) / 1E6
	Printf "Finished ThreadMSE_PEAK in %.2f seconds.\r", elapsedTime
 
	SetDataFolder originalDFR
End


Posts: 1852
Joined: 2007-06-29
Location: United States

Your function has two inputs, but your menu item tries to invoke it with none. The ability to invoke a macro with no inputs is only for macros; a function that will use DoPrompt should have no inputs.

But keep your function, and move the Prompt/DoPrompt out if it into another function. Put all the UI code in that second function (that is, Prompt and DoPrompt) and have it call your engine function. Something like this:

Menu "SME_PEAK"
	"Multi-CPUs MSE & Peak", mThreadMSE_PEAK()
End
 
Function mThreadMSE_PEAK()
 
	variable xl
	variable xr
	prompt xl,"enter xl"
	Prompt xr,"enter xr"
	Doprompt "Enter values", xl,xr
	if (V_flag == 0)
		ThreadMSE_PEAK(xr, xl)
	endif
end

I like to start my menu-servicing functions with "m" to remind me what it's for. And it allows me to use the same function name as the engine that it calls.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com


viralvector
Posts: 61
Joined: 2014-01-01
Location: Canada

johnweeks wrote:
Your function has two inputs, but your menu item tries to invoke it with none. The ability to invoke a macro with no inputs is only for macros; a function that will use DoPrompt should have no inputs.

But keep your function, and move the Prompt/DoPrompt out if it into another function. Put all the UI code in that second function (that is, Prompt and DoPrompt) and have it call your engine function. Something like this:

Menu "SME_PEAK"
	"Multi-CPUs MSE & Peak", mThreadMSE_PEAK()
End
 
Function mThreadMSE_PEAK()
 
	variable xl
	variable xr
	prompt xl,"enter xl"
	Prompt xr,"enter xr"
	Doprompt "Enter values", xl,xr
	if (V_flag == 0)
		ThreadMSE_PEAK(xr, xl)
	endif
end

I like to start my menu-servicing functions with "m" to remind me what it's for. And it allows me to use the same function name as the engine that it calls.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com

I like your way of managing macro functions with "m", and the code works flawlessly.
Thank you very much and have a nice weekend! johnweeks


[ last edited June 6, 2014 - 17:17 ]
Posts: 1852
Joined: 2007-06-29
Location: United States

I think of the "m" as standing for "menu" :)

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com


Back to top