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 is inherently synchronized with the simulation loop.
While non-threaded Lua code executes in the same thread as CoppeliaSim and is thus quite efficient, non-threaded Python code operates in a slightly different manner: CoppeliaSim will launch a new process which will connect to CoppeliaSim via socket communication. Everything happens under the hood, the main difference is however that Python code will start and run slower than Lua code. See also the other differences between Lua and Python scripts.
Threaded code
In a Lua script, threading is achieved via coroutines, that CoppeliaSim will preemptively interrupt (i.e. yield, or switch) at regular intervals, and resume at a later point.
On the other hand, CoppeliaSim will handle threading of a Python script by launching a new process which will connect to CoppeliaSim via socket communication, which happens under the hood, the main difference is however that Python code will start slower than Lua code. See also the other differences between Lua and Python scripts.
In the context of threaded code, we will refer to threads and coroutines, interrupt, yield and switch, in an interchangeable manner.
Lua specific
Lua threaded code has several weaknesses compared to Lua non-threaded code if not programmed appropriately: it can waste some processing time. Following shows a typical threaded function in a Lua script (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()
while true do
print('simulation time: ',sim.getSimulationTime())
end
end
By default a Lua 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 Lua code shows code synchronization with the main simulation loop:
function sysCall_init()
sim.setThreadAutomaticSwitch(false) -- forbid automatic switches
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()
while true do
print('simulation time: ',sim.getSimulationTime())
sim.switchThread() -- resume in next simulation step
end
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 the simulation step.
Python specific
Python threaded code shares similar properties as Lua threaded code, regarding synchronization with CoppeliaSim's simulation loop. Following shows a typical threaded function in a Python script (i.e. threadMain), which is not synchronized:
#python
def sysCall_thread():
sim.setThreadAutomaticSwitch(True) # allow automatic switches
while sim.getThreadExitRequest() == False:
print('simulation time: '+str(sim.getSimulationTime()))
By default, a Python threaded code section will operate in a stepped fashion, but this can be changed with sim.setThreadAutomaticSwitch(True) and sim.switchThread. Once the current thread was switched, it will resume in next simulation step. Following Python code shows code synchronization with the main simulation loop:
#python
def sysCall_thread():
sim.setThreadAutomaticSwitch(False) # forbid automatic switches
while sim.getThreadExitRequest() == False:
print('simulation time: '+str(sim.getSimulationTime()))
sim.switchThread() # resume in next simulation step
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 the same simulation step.
With threads in general, 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.
Finally, 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 hand-in-hand, stepped, with the simulation loop.
|