Prompt function is not allowed in ThreadSafe function

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.
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
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




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
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