Threaded and non-threaded script code

Any type of script can run threaded and/or non-threaded script functions.

Non-threaded code

The entrance to a script is always originating from callback functions which are not running threaded, and should not be blocking. This means that every time they are called, they should perform some task and then return control. If control is not returned, then CoppeliaSim will halt.

Non-threaded code runs faster than threaded code, since there is no overhead in handling the thread creation, switching or resume function. It also has inherent synchronization with the simulation loop.


Threaded code

Threading in a script is achieved via coroutines, that CoppeliaSim will preemptively interrupt (i.e. yield, or switch) at regular intervals, and resume at a later point. In this context, we will refer to threads and coroutines , interrupt, yield and switch, in an interchangeable manner.

Threaded code has several weaknesses compared to non-threaded code if not programmed appropriately: it is more resource-intensive, and it can waste some processing time. Following shows a typical threaded function (i.e. coroutineMain), which however is not perfect since it is wasting precious computation time in the loop:

function sysCall_init()
    corout=coroutine.create(coroutineMain) -- create the coroutine
end

function sysCall_actuation()
    if coroutine.status(corout)~='dead' then
        local ok,errorMsg=coroutine.resume(corout) -- resume the coroutine
        if errorMsg then
            error(debug.traceback(corout,errorMsg),2)
        end
    end
end

function coroutineMain()
    for i=1,100,1 do
        print('simulation time: ',sim.getSimulationTime())
    end
end

By default a threaded code section will execute for about 1-2 milliseconds before automatically interrupting or switching and giving control back to the caller. This default behavior can be changed with the sim.setThreadSwitchTiming or sim.setThreadAutomaticSwitch. Once the current thread was switched, it will resume next time the coroutine is resumed (which often is in next simulation step. The thread switching is automatic and is handled by CoppeliaSim (occurs after the specified time), but the sim.switchThread command allows to explicitely switch when needed. Using above three commands, a perfect synchronization with the main simulation loop can be achieved. Following code shows code synchronization with the main simulation loop:

function sysCall_init()
    corout=coroutine.create(coroutineMain)
end

function sysCall_actuation()
    if coroutine.status(corout)~='dead' then
        local ok,errorMsg=coroutine.resume(corout)
        if errorMsg then
            error(debug.traceback(corout,errorMsg),2)
        end
    end
end

function coroutineMain()
    local savedLockLevel=sim.setThreadAutomaticSwitch(false) -- forbid automatic switches
    for i=1,100,1 do
        print('simulation time: ',sim.getSimulationTime())
        sim.switchThread() -- resume in next simulation step
    end
    sim.setThreadAutomaticSwitch(savedLockLevel) -- restore automatic switch behaviour
end

Above while loop will now execute exactly once for each main simulation step and not waste time reading and printing the simulation time over and over for same simulation step.

Some operation should not be interrupted in order to execute correctly (imagine moving several objects in a loop). In that case, you can temporarily forbid thread switches with the sim.setThreadAutomaticSwitch function.

Several blocking functions are specifically meant to be running from within threaded code, e.g. sim.moveToConfig: those functions will handle thread switching for you, at the appropriate time, in order to operate synchronously with the simulation loop.