doupdate eating event codes

I am having a problem.  I am controlling a data acquisition interface from Igor, delivering stimuli and collecting responses.  I use background tasks, so the user can vary stimulus properties while collecting data.  For example, I use cursor position on a graph to control the frequency and intensity of a sound stimulus.  As data are collected, they are analyzed and displayed in graphs, so the experimenter has immediate feedback. These background tasks run as frequently as Igor allows.  To make sure the analysis and display windows are refreshed, my background task uses "doupdate".  Otherwise, they are only refreshed every few goes.

The problem is that somewhere between Igor 6 and 7, I started losing events with this strategy.  Keypresses and mouse clicks get killed.  This makes it super hard to even stop the background tasks.  I just discovered that if I leave out the "doupdate", then no problems.  The key presses or mouse clicks are caught, and cause changes in the stimulus properties, but then the data are not updated on each trial, which is crippling. 

The following code recapitulates the problem:

function dumbbackground (s)
    STRUCT WMBackgroundStruct &s
   
    sleep /s .1
    doupdate
    return 0
end

function doit ()
    ctrlnamedbackground dumbback, start, proc=dumbbackground
end

function stopit ()
    ctrlnamedbackground dumbback, stop
end

When I execute doit() on the command line, I spend the next 10 minutes or so trying to type in "stopit ()" (please don't hate me if you have the same problem).  If I comment out "doupdate", then the keyboard responds normally. Changing the doupdate to be more focal (e.g. "doupdate /w=graph0") didn't solve the problem.

Anybody have any ideas?  Does the answer lie in "live" graphs?  I am very grateful for anyone's help.

-Matthew

Calling DoUpdate from a background task is a road to hell. If the DoUpdate call results in other parts of your code running, this probably messes up your program.

Have you tried doing

Execute /P/Q "DoUpdate"

instead?

I fixed my application by not requiring DoUpdate calls from background tasks any more.

Are you using IP7 or IP8? For the latter you can try the fix from https://github.com/AllenInstitute/MIES/commit/511bd39637c6a1248349d6a80….

There were major changes to event processing for Igor 7. Our old methods were simply not compatible with modern operating systems. We tried to maintain functionality as it existed in Igor 6, but not always successfully.

The default period of your background task is 1 tick. Our main loop runs with a period of (by default) 20 mS, just over 1 tick, which means that your background task wants to run every time through the main event loop. That especially true because your sleep command is asking for a sleep of 6 ticks.

I'm curious why you need DoUpdate to see the graphs updating. If you change the waves shown in the graphs, they should update. Or are you running a continuous loop in a foreground function? Or perhaps your background task is taking so much time that no time is left for updates via the normal mechanism?

The problem is that we field events and block them during user functions in order to enforce the things you're not supposed to be able to do during user function execution. That has the effect of catching keyboard events and throwing them away, which is why your background task makes it so hard to type.

I just tried keeping DoUpdate but removing the call to sleep, but leaving in DoUpdate. That also makes it possible to type. I presume that's because the background task doesn't take long to execute, leaving time for us to process keyboard events. The call to DoUpdate no doubt is very quick because I have no graphs that need to be updated. I presume the call to Sleep is there as a stand-in for the processing your background task does.

Trying to make a more realistic version of your test, I did this:

function dumbbackground (s)
    STRUCT WMBackgroundStruct &s
   
    //sleep /s .1
    //   doupdate
    Wave junk
    junk = gnoise(1)
    DoUpdate
   
    return 0
end

function doit ()
    ctrlnamedbackground dumbback, period=1, start, proc=dumbbackground
    ctrlnamedbackground dumbback, status
    print S_info
end

function stopit ()
    ctrlnamedbackground dumbback, stop
end

I made a 1000-point wave named "junk", made a graph with junk, and started the background task. That worked acceptably, with little trouble typing on the command line. The graph updates with or without the call to DoUpdate.

Then I redimensioned junk to have 1e6 points. Now, with or without the DoUpdate it is very difficult to type. The graph does update frequently, though. I don't know if it is updating with every single run of the background task, but it probably is.

@Thomas I had forgotten about "noevents"!

I also see that it isn't documented, and recall that I implemented it especially for you. I guess I better add documentation. If I intended it to be a secret, the cat is out of the bag now :)

In fact, the noevents keyword prevents delivery of events during background tasks, which also means they can't be blocked during background tasks. If your background task takes a long time to run, it could be a bad thing to do, though.

@xufriedman I'm still wondering why you need DoUpdate.

I should mention that DoUpdate will, in fact, subvert the noevents keyword. It will cause processing of events because that is necessary to make graphics update.

Thanks for your suggestions.  I guess I was wrong that DoUpdate was causing the problem.  My background routine takes a while to run, so it may be more like your example of the junk wave being 1e6 points.  It calls DAQmx_Scan, which locks things up for ~0.1 s, and then there is quite a bit of analysis after that.  The whole shebang goes at about 5 or 6 per second.  I agree it makes sense that Igor doesn't interrupt my task to deliver the events that occur during it, so I expect to be deaf until the task finishes, but if the events are also discarded, it is rather difficult for me as a user to time my key presses for the 1 tick or so that the background task is not operating.  Is this what "noevents" addresses?  Maybe I should wait for documentation to understand how it might help?

The important events are all arrow keys steering the stimulus characteristics around.  If I understand right, lowering the rate of calling my background task wouldn’t help (I still have to time my key presses just right).  Nor would recoding as a regular (non-background) procedure, because GetKeyState has similar limitations.  

P.S. I found I needed DoUpdate calls, because without it, multiple stimuli go by without the responses being displayed.  Maybe live graphs would help this.

Rather than wait for documentation, I just tried "noevents", and it totally solved the problem.  Thank you thank you thomas_braun for needing it before me and thank you thank you johnweeks for implementing it!  Also somehow the doupdate call I left in there didn't matter.

-Matthew

I'm glad that worked for you. I'm also glad that Thomas remembered it where I didn't. I'd better document it, eh?

What noevents does is to prevent the usual checking for abort keys (Cmd-period if you're on a Mac, Ctrl-Break on Windows, shift-esc on both). That check allows delivery of events while a user function is running, with the result that many are tossed out. So with noevents, during a background task the check isn't done, and the events stay in the event queue until the background task isn't running. Then normal event processing occurs with the stored-up events.

Note that it also means you can't abort a stuck background task! A bug in your code could result in having to force quit Igor.

It would be better if you could come to some decision about a "reasonable" rate for the background. Right now, you are basically telling Igor to run it helter-skelter as fast as possible. My guess is that the actual rate varies quite a bit. The fact that you can get any events or updates is due to the fact that the next background task run doesn't happen until the outer event loop runs once more. Its a good thing you aren't using CtrlNamedBackground burst. That would tie up Igor with running the background task over and over again until it caught up with all the times it didn't get to run because it was already running. In your case, that would be forever!

You know, Igor isn't really fundamentally designed as a real-time application. We have worked hard to try to make it useful that way to a certain extent.

Glad that the workaround John added for us is useful for others as well.


> I'd better document it, eh?

Yeah, would be nice!