If the stop_after variable is not empty, it shows the stop_signal display after an interval specified by the stop_after variable.

During the entire (2000 ms) interval, a keyboard response is collected.

The temporal flow is controlled by the coroutines plugin. Therefore, the timeout and duration values specified in the items are not used. For example, in Figure 1, the keyboard_response will run for 2000 ms, regardless of the timeout that is specified in the item.

Supported items

Currently, the following items are supported:

keyboard_response

mouse_response

sampler

sketchpad

feedback

inline_script (see Writing a custom coroutine)

Writing a custom coroutine

Technically, coroutines are generators. Generators are functions that can suspend their execution (i.e., they yield) and resume later on; therefore, multiple generators can run in a rapidly alternating suspend-resume cycle. This trick is sometimes called weightless threading, because it has most of benefits of real threading, without any of the overhead or (potential) instability. Coroutines do not use threading or multiprocessing.

In the coroutines plugin, you can indicate the name of a generator function that you have defined in an inline_script. This generator needs to work in a particular way (as illustrated in the examples below):

It must initialize and then yield. This first yield returns nothing.

It may loop while yielding on every iteration. The loop breaks when:

The coroutine should end; or

When the yield returns False; this is a signal from the coroutines plugin to the generator to signal that the coroutines ends.

When the generator yields False itself; this is a signal from the generator to the coroutines to signal that the coroutines ends.

No time-consuming things should happen between yield statements, except during initialization.

The first and simplest option is to write a one-shot coroutine. This is a function that is called once to prepare itself, once to execute, and then terminates. For example:

defmy_oneshot_coroutine():""" This is an example of a one-shot generator coroutine. """# All initialization stuff goes hereprint('Initialize!')# Now yield to signal the end of the preparationyield# Do something here and terminateprint('Stopped!')

A more complicated example is a coroutine that prepares itself, is then called repeatedly (and keeps track of how often), and then terminates:

defmy_coroutine():""" This is an example of a generator coroutine that keeps track of how many cycles it goes through. """# All initialization stuff goes herei=0# Now yield to signal the end of the preparationyield# Enter an infinite loopwhileTrue:# Do some important stuff here. This should not be# too time consuming; otherwise you will block the# other items of the coroutines.i+=1# Yield to give other items an opportunity to execute,# and receive a boolean that indicates whether we# should keep goingkeep_going=yieldifnotkeep_going:break# We are done!print('Stopped after %d cycles!'%i)

Another example, which stops the coroutines when a response has been collected.

defmy_coroutine():""" This is an example of a generator coroutine that aborts the coroutines when a response has been collected. """# To start, set response to Nonevar.response=Noneyield# Loop while coroutines is runningwhileTrue:# If response is None (i.e. no response has been collected), signal# True to the coroutines to indicate that, from this end, the coroutines# should keep going.signal_to_coroutines=var.responseisNone# Send the signal to the coroutines, and get a return signal. If the# return signal is False, we should break the loop.signal_from_coroutines=yieldsignal_to_coroutinesifnotsignal_from_coroutines:break