Jump to content

A beginning state of a mono script in a newly created object might be not what you think.


Ela Talaj
 Share

You are about to reply to a thread that has been inactive for 4335 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Recently I've completed a new product which does a few interesting things but basically consists of 2 objects, a rezzer and a rezzed object. A rezzer has a bunch of scripts but only one communicates with the rezzed object via llRegionSayTo()/listen. The rezzed object has only one script, which is a classic state machine. In default state it just moves the rezzed object into a certain position and waits to hear from the rezzer. Then it goes thru a sequence of states depending upon messaging from the rezzer. It cannot get out of default unless receiving one of those specific messages; when it does it never returns to default, but dies in some other state.

There may be several of these rezzed objects existing simultaneously but rezzed by different rezzers. All scripts are mono.

While testing I noticed a weird thing. Sometimes a rezzed object would not move into its default position but would just sit at the rezzing point. No matter what I tried it would still happen, not every time but seemingly on random.Now the code includes on_rez script reset only in default state, because what other state could a script possibly be in a newly rezzed object?

Turns out it could be anywhere in any state. As soon as I put on_rez script reset in every state, the problem disappeared like it never was. The best I can explain it, there is no such thing as a mono script. There is a mono thread. There are at least as many of them as there are objects running the same mono bytecode. When one of the objects dies, its thread is deleted but others remain. When a new object is created one of the existing threads is forked and a child thread continues exactly from the same point at which its parent was when forked. That is why a script in my newly rezzed object was not in default state: when it was born it picked a state from its parent - another rezzed object existing at that time. When none was existing it would indeed start anew, in default - that is why the problem was occuring from time to time, not every time.

There are still holes in the above analysis. How does a server selects a parent thread to fork? Is it the oldest one still existing or the last one already existing or something else? I don't have specific .NET server knowledge to answer this.Maybe someone can.

The reason I post it here is for someone else to avoid spending time on solving this or similar problem resulting from mono multi-threading. To put it simply, always put on_rez event in all your states even if you think a script could not possibly be there, little beasties always find a way to surprise you...

 

 

Link to comment
Share on other sites

Nice catch Ela.  A script/object which has been running, then taken into inventory, will continue in whatever state it reached next time it is rezzed but I understand you aren't commenting on that.  You're seeing a brand new script/object 'resuming'.  It makes some sort of sense as a possibility with Mono's code-sharing but it's very unsafe if initialisation isn't followed predictably.  With luck Kelly will see this and comment, have you checked Jira?

Link to comment
Share on other sites

Thank you for sharing.

A newly created object should make scripts start in the default state, whether the scripts are compiled in the object or dragged from inventory?
That's what I always believed...
I will try to remember that it may not always be like that:smileyhappy:

Come to think of it...
The object that is rezzed by another object must have been rezzed before, when it got it's script.
What happened in the script then? Is it possible that it changed state?

Link to comment
Share on other sites

That is a great observation Dora and would be applicable to many situations, yet not to this one.

When a blank object is rezzed manually to put the script in, the rezzer doesn't know its id so can't send msgs to it via llRegionSayTo() so the script remains in default state.

@Peter.  What I described is a standard Unix/Linux way to create threads. It is perfectly safe provided a programmer knows what she's doing :)

Link to comment
Share on other sites


Ela Talaj wrote:

...It is perfectly safe provided a programmer knows what she's doing
:)

Ergo ... very unsafe for scripters unless we know what the server's doing.  That's why I said "good catch".  If there is this chance that scripts will not initialise implicitly 'as expected' then we have to make it explicit (as you said).  That's a pretty big gotcha.

Link to comment
Share on other sites

Is there a jira about this, with a minimal repro?

I tried unsuccessfully to replicate this with very dumbed-down scripts, as follows:

// rezzerfloat RADIUS = 5.0;default{    touch_start(integer total_number)    {        llRezObject(llGetInventoryName(INVENTORY_OBJECT, 0)            , llGetPos()+<llFrand(RADIUS), llFrand(RADIUS), 0.0>            , ZERO_VECTOR            , ZERO_ROTATION            , 0);    }}

 and

// rezzed objectdefault{    state_entry()    {        llOwnerSay("in state default");    }    touch_start(integer total_number)    {        state first;    }}state first{    state_entry()    {        llOwnerSay("in state first");    }    touch_start(integer total_number)    {        state second;    }}state second{    state_entry()    {        llOwnerSay("in state second");    }    touch_start(integer total_number)    {        llResetScript();    }}

Then I tested, rezzing, touching, and killing objects from one, then two rezzers, in lots of sequences, and every time the rezzed object would transition to "state first" on the first touch.  So whatever is going on, it's not as simple as every freshly-rezzed mono script inheriting state from another instance.

I could try replacing the touch_start with a listen in each state of the rezzed object script, or whatever else we think might be triggering the problem, but it's not obvious to me what to try, so it would be handy if the minimal repro had already been discovered.

Link to comment
Share on other sites

True.  Coincidentally, this report somehow reminded me of the first time I encountered that bug, which at the time demonstrated different behaviors whether the script was was reset manually or by a call to llResetScript().  It's not the same thing, of course--there was no Mono back then--and anyway that weirdness disappeared even while the touch_start misbehavior persists to this day, but it retriggered my "something funky with states" receptor.

Link to comment
Share on other sites

I have not tried to reproduce with simpler scripts. However behavior I've noted in the OP is pretty consistent. In fact since posting I've encountered the same in another development. In both the rezzer monitors rezzed object creation via object_rez event and then after 1 sec delay (llsleep) sends moving coordinates to the rezzed id via llRegionSayTo().The rezzed object listens for this message in default state only. Upon receipt it moves to the specified point and then leaves the default state.

Putting on_rez reset in each state of the rezzed object script cured the OP-described problem in both cases. My explanation in the OP is consistent with the POSIX Threads standard (pthreads) so I don't look at it as a bug but rather as expected behavior. It is entirely possible that SL servers create multiple instances of mono scripts by some other mechanism but untill someone from LL tells us we wouldn't know.

Link to comment
Share on other sites

Except it's not consistent, or at least I haven't found a way to replicate it -- at all, let alone consistently.

I revised the simple scripts (below) to emulate more of the problem scenario, with the object_rez, llRegionSayTo(), llSleep(), etc. And again I tried rezzing, deleting, touching, ad nauseum, in random order, from one and two rezzers, and every object was in the default state when freshly rezzed and moved to position on message from the rezzer.

There's a bug here somewhere, otherwise the behavior should obtain every time a duplicate Mono script is instantiated. (Never mind that it would be a pretty radical departure from LSO for Mono scripts to just fork() from another script instance.)

Anyway, my dopey test scripts, as revised:

// rezzerinteger CHAN = -456789012;float RADIUS = 5.0;default{    touch_start(integer total_number)    {        llRezObject(llGetInventoryName(INVENTORY_OBJECT, 0)            , llGetPos()            , ZERO_VECTOR            , ZERO_ROTATION            , CHAN);    }    object_rez(key id)    {        llSleep(1.0);        llRegionSayTo(id, CHAN,             (string)(llGetPos()+<llFrand(RADIUS), llFrand(RADIUS), 0.0>));    }}

 and

// rezzed objectdefault{    state_entry()    {        llOwnerSay("in state default");    }    on_rez(integer start_param)    {        llListen(start_param, "", "", "");    }    listen(integer channel, string name, key id, string message)    {        llSetPos((vector)message);        state first;    }}state first{    state_entry()    {        llOwnerSay("in state first");    }    touch_end(integer num_detected)    {        state second;    }}state second{    state_entry()    {        llOwnerSay("in state second");    }    touch_end(integer num_detected)    {        llResetScript();    }}

 

Link to comment
Share on other sites

Well it is consistent in the 2 projects I've encountered it. The rezzed object script is pretty complex in both cases. In each state (except for default) it receives inventory, removes some of the prior inventory depending on what is received, switches animations, monitors avatar sitting on the object...things like that. If the effect in question depends on script complexity then there is a good probability it is indeed a bug and not a feature :) A thing to do would be reducing the script complexity and observing on which stage the effect would disappear.

But I'm not a research facility, I'm a commercial enterprise :) So have neither time nor resources to inestigate... after all if there is indeed a rather specific server bug, LL is not going to pay me for finding it. Besides my primary work is not related to .NET server so I don't really have motivation to read up on it. The problem was there and disappeared after I put on_rez reset in every state even though it should've not possibly been in those states. So if someone encounters a similar proble that's a resolution.

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 4335 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...