LSL functions you should NEVER call
Posted: Wed Jan 15, 2014 11:01 pm
As you know, many of the functions in the LSL library (llSetPos, llSetRot, llSetPrimitiveParameters, etc) have a built-in delay.
It turns out that this delay is implemented in a VERY BAD WAY in Open Simulator, and it can seriously mess up your SIM. I'm including an article below that I wrote for the OSGrid forum explaining why. The thing that you can do about this is never call one of these routines unless you have no choice. Unfortunately many free scripts that people have used for years in SL can cause big problems in Open Sim based grids. For example, I saw a SIM that had 300 blinking lights at Xmas time. Each light called llSetPrimitiveParams to turn glow on and off. The lights kept blinking, but no other scripts in the SIM could run at all! We replaced all of these scripts with one that called llSeLinktPrimitiveParamsFast, and the SIM worked fine.
Here's a (probably incomplete) list of routines you should never call, and how to do the same thing without locking up a SIM:
llSleep(); Avoid this whenever possible. Use timers, re-write your code to be event driven.
llSetPos(pos); replace with llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,pos]);
llSetRot(rot); replace with llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_ROTAZTION,rot]);
llSetPrimitiveParams([]); replace with llSetLinkPrimitiveParamsFast(LINK_THIS,[]);
llSetLinkPrimitiveParams(link,[]); replace with llSetLinkPrimitiveParamsFast(link,[]);
Technical Explanation:
XEngine, the part of Open Sim that runs scripts, can only run a handful of scripts at once. Each running script is processed in a separate thread. A thread is like a separate processor. (And if you are lucky your server has multiple processor cores and each script does run in another core.) When a script is waiting for an event, like a timer or a touch, it does not need the thread and that thread can be re-used for a different script. When an event happens, a thread is assigned to a script and it is allowed to run again. So if all your scripts are basically event driven, you can have 1000's of them all waiting for events without using a lot of SIM resources.
Because there are a limited number of threads in a SIM, if an event needs to happen and all the threads are already working on other scripts, the new event will have to wait for some other script to release a thread first. This could cause lag if the wait becomes very long. On the other hand, if all your scripts are well-behaved and return from events as soon as possible, this will not be a problem.
llSleep is a special problem. Instead of returning the thread for other scripts to use, it locks up the thread for the requested time. I assume this was done because events can only happen once a “heartbeat” which is 11 times a second or about once every 0.09 seconds. Locking the thread allows pausing the script for short accurate times, much shorter than the heartbeat. But if too many scripts all call llSleep at about the same time, all the threads can get locked up and NO OTHER SCRIPTS can run.
No problem you say, I'll never use llSleep again! It turns out that lots of LSL functions have a delay, and this delay is implemented by stopping the thread exactly the same way that llSleep works! If you call llSetPos, llSetRot, llSetPrimitiveParams, llSetLinkPrimitiveParams, llCreateLink, llDialog, llEmail (20 SECONDS!), llGiveInventory, llGiveInventoryList, llInstantMessage, llRezObject, llRezAtRoot, llSetLocalRot, ALL OF THESE FUNCTIONS INCUR AN LLSLEEP-LIKE DELAY WHEN YOU CALL THEM! (And a few more that I missed). Many of these functions can be done by calling llSetLinkPrimitiveParamsFast instead, so you should re-write your scripts to use this whenever possible. Note that although it is intended for moving child prims, if you call it from the root prim with LINK_THIS as the first argument, it moves the entire link set just like llSetPos. (Or rotates it, or whatever).
Timing things is a little more difficult. If you need a delay and don't care how long it is, you can use llSetTimerEvent. This has a limit in OS that it can never set a timer that is less than 0.5 seconds. Plus if you need to time several different things, your timer event can get complicated as it tries to figure out why you called. The advantage of timers is that when you start one (then exit your current event), the thread you are using is freed up for some other script to use in the mean time.
It turns out that this delay is implemented in a VERY BAD WAY in Open Simulator, and it can seriously mess up your SIM. I'm including an article below that I wrote for the OSGrid forum explaining why. The thing that you can do about this is never call one of these routines unless you have no choice. Unfortunately many free scripts that people have used for years in SL can cause big problems in Open Sim based grids. For example, I saw a SIM that had 300 blinking lights at Xmas time. Each light called llSetPrimitiveParams to turn glow on and off. The lights kept blinking, but no other scripts in the SIM could run at all! We replaced all of these scripts with one that called llSeLinktPrimitiveParamsFast, and the SIM worked fine.
Here's a (probably incomplete) list of routines you should never call, and how to do the same thing without locking up a SIM:
llSleep(); Avoid this whenever possible. Use timers, re-write your code to be event driven.
llSetPos(pos); replace with llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,pos]);
llSetRot(rot); replace with llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_ROTAZTION,rot]);
llSetPrimitiveParams([]); replace with llSetLinkPrimitiveParamsFast(LINK_THIS,[]);
llSetLinkPrimitiveParams(link,[]); replace with llSetLinkPrimitiveParamsFast(link,[]);
Technical Explanation:
XEngine, the part of Open Sim that runs scripts, can only run a handful of scripts at once. Each running script is processed in a separate thread. A thread is like a separate processor. (And if you are lucky your server has multiple processor cores and each script does run in another core.) When a script is waiting for an event, like a timer or a touch, it does not need the thread and that thread can be re-used for a different script. When an event happens, a thread is assigned to a script and it is allowed to run again. So if all your scripts are basically event driven, you can have 1000's of them all waiting for events without using a lot of SIM resources.
Because there are a limited number of threads in a SIM, if an event needs to happen and all the threads are already working on other scripts, the new event will have to wait for some other script to release a thread first. This could cause lag if the wait becomes very long. On the other hand, if all your scripts are well-behaved and return from events as soon as possible, this will not be a problem.
llSleep is a special problem. Instead of returning the thread for other scripts to use, it locks up the thread for the requested time. I assume this was done because events can only happen once a “heartbeat” which is 11 times a second or about once every 0.09 seconds. Locking the thread allows pausing the script for short accurate times, much shorter than the heartbeat. But if too many scripts all call llSleep at about the same time, all the threads can get locked up and NO OTHER SCRIPTS can run.
No problem you say, I'll never use llSleep again! It turns out that lots of LSL functions have a delay, and this delay is implemented by stopping the thread exactly the same way that llSleep works! If you call llSetPos, llSetRot, llSetPrimitiveParams, llSetLinkPrimitiveParams, llCreateLink, llDialog, llEmail (20 SECONDS!), llGiveInventory, llGiveInventoryList, llInstantMessage, llRezObject, llRezAtRoot, llSetLocalRot, ALL OF THESE FUNCTIONS INCUR AN LLSLEEP-LIKE DELAY WHEN YOU CALL THEM! (And a few more that I missed). Many of these functions can be done by calling llSetLinkPrimitiveParamsFast instead, so you should re-write your scripts to use this whenever possible. Note that although it is intended for moving child prims, if you call it from the root prim with LINK_THIS as the first argument, it moves the entire link set just like llSetPos. (Or rotates it, or whatever).
Timing things is a little more difficult. If you need a delay and don't care how long it is, you can use llSetTimerEvent. This has a limit in OS that it can never set a timer that is less than 0.5 seconds. Plus if you need to time several different things, your timer event can get complicated as it tries to figure out why you called. The advantage of timers is that when you start one (then exit your current event), the thread you are using is freed up for some other script to use in the mean time.