10-07-2005 07:06
After reading about this I set on a quest to do multithreading in LSL :D

... 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();
}
}
_____________________
Either Man can enjoy universal freedom, or Man cannot. If it is possible then everyone can act freely if they don't stop anyone else from doing same. If it is not possible, then conflict will arise anyway so punch those that try to stop you. In conclusion the only strategy that wins in all cases is that of doing what you want against all adversity, as long as you respect that right in others.