Threaded and non-threaded script 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.
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.