
... then remembered why we had llMessageLinked() in the first place. And I couldn't find anything to code that absolutely required this sort of thing anyway. So for the technicality itself, here's an example, copyleft and untested:
CODE
// round-robin cooperative multithreading in LSL
// Ugly, innefficient and slow. Use multiple scripts with link messages for IPC instead !
// Author: Jesrad Seraph
// Don't use, copy or modify this code unless you like messing with insane and near-useless scripts
// In the absence of support for cross-event jumps or even switch/case in LSL, this has to use bitmasks,
// and one run counter per thread.
// TO DO: add a priority list and use another type of scheduler (yeah right)
// TO DO: add a more complex thread context management (especially for stack address and size)
// This example script IMs "hello" after 4 seconds to whoever touches the prim. The more people touch at
// about the same time, the slower the whole thing becomes (well over 8 seconds). Instead of batching through
// all the touch events one after the other efficiently, this mess makes everyone wait lot bit longer so it
// can greet almost everyone after roughly the same delay
integer run; // number of the currently running thread
integer running // number of simultaneous threads running
list stack; // where data is piled up
ptouch_start(integer i)
{
// normally this happens in the scheduler, but then this is LSL so it can't manage it:
integer touch_run = llList2Integer(stack, 2 * i + 1);
if (touch_run == 0) {
// first slice of the event
llSleep(0.5); // wow!
} else if (touch_run == 1) {
// second slice of the event
llSleep(0.5); // wow again!
} else if (touch_run == 2) {
llSleep(0.5); // wow again!
} else if (touch_run == 3) {
llSleep(0.5); // wow again!
} else if (touch_run == 4) {
llSleep(0.5); // wow again!
} else if (touch_run == 5) {
llSleep(0.5); // wow again!
} else if (touch_run == 6) {
llSleep(0.5); // wow again!
} else if (touch_run == 7) {
llSleep(0.5); // wow again!
} else if (touch_run == 8) {
llSleep(0.5); // wow again!
} else if (touch_run == 9) {
// last slice of the event
llInstantMessage((key)llList2String(stack, 2 * i), "Hello !");
// normally this happens in the scheduler code, it's here for convenience instead:
stack = llDeleteSubList(stack, 2 * i, 2 * i + 1);
--running;
--run; // so next thread to run actually gets its slice of run time
return;
}
stack = llListReplaceList(stack, [++touch_run], 2 * i + 1, 2 * i + 1);
}
sched()
{
if (running == 0) return;
run = ++run % running;
// normally the script should retrive the thread context from "run" and pass useful
// things back to the event code, but I'm lazy
ptouch_start(run);
}
default
{
state_entry() { run = -1; running = 0; stack = []; }
touch_start(integer c)
{
running++;
stack += [llDetectedKey(0), 0];
llSetTimerEvent(0.0625);
}
timer()
{
if (running == 0) { llSetTimerEvent(0.0) } else sched();
}
}