Papyrus threading: alternative techniques

Post » Thu Jun 21, 2012 4:05 am

The http://www.creationkit.com/Threading_Notes_(Papyrus) says: "The above example represents the script as we'd want to re-write it - blocking further activations after the first. There are several ways to get this result. This example will be solved using States."

What are the other ways?
User avatar
Schel[Anne]FTL
 
Posts: 3384
Joined: Thu Nov 16, 2006 6:53 pm

Post » Thu Jun 21, 2012 4:10 am

There ain't any, at least not reliable ones.

I sure the way that seems most natural is to use a simple boolean as guard, but that just means you introduced a race-condition.
User avatar
Robert Jackson
 
Posts: 3385
Joined: Tue Nov 20, 2007 12:39 am

Post » Thu Jun 21, 2012 9:01 am

I still do not understand states and how they effect threading? The boolean guard doesn't seem to work?>
User avatar
Tanika O'Connell
 
Posts: 3412
Joined: Fri Jan 26, 2007 1:34 am

Post » Thu Jun 21, 2012 3:15 am

How could it? A lot of threads might get to read the boolean with its first value before one of them changed it, and thereafter all the others would be acting on false data. It could only be safe if, somehow, any thread that read the boolean could prevent other threads changing it until it had finished doing everything it had to do that depended on the boolean's value. That's what states are for.

Here is how I explain states to myself, in case it helps: states allow us to give objects alternative sets of events.

Every type of object has a certain list of events - OnActivate, OnUpdate and so on. But these are actually the events that fire in the empty state. They could more accurately be called 'OnActivateWhileInTheEmptyState', 'OnUpdateWhileInTheEmptyState', only it would be a dreadful chore to have to type all that.

When you create a state you are creating another set of events the object will respond to. So this:

State Busy  Event OnUpdate()  endEventendState

is creating an event that could be called 'OnUpdateWhileInTheBusyState'. And when you say
GotoState("Busy")
you're saying that, from now on, events in the busy state, when they exist, will be fired instead of events in the empty state.

Here's an example:
Event OnUpdate()	GotoState("Busy")	int AnInt = 1	DoSomethingThatMightAffectAnInt()	if AnInt == 1		 DoSomethingElse()	endIf	GotoState("")endEventState Busy  Event OnUpdate()  endEventendState

Without the gotostate, while this code was away DoingSomethingThatMightAffectAnInt, another OnUpdate event might start, and set AnInt back to one again. But thanks to the
GotoState("Busy")
when an OnUpdate event is started it will be an 'OnUpdateWhileInTheBusyState', which does not set AnInt to 1, rather than 'OnUpdateWhileInTheEmptyState', which does. Once we are safely passed the code which cares about AnInt's value, we can go back to using 'OnUpdateWhileInTheEmptyState', by calling GotoState("").
User avatar
gemma king
 
Posts: 3523
Joined: Fri Feb 09, 2007 12:11 pm

Post » Thu Jun 21, 2012 6:13 pm

I'm not sure I understand how using a state differs from using a boolean to flag that the object is busy. For example:

bool bBusyevent OnUpdate()  if (!bBusy)    bBusy = true    DoStuff()    bBusy = false  endifendevent

Is that less robust for some reason?
User avatar
RUby DIaz
 
Posts: 3383
Joined: Wed Nov 29, 2006 8:18 am

Post » Thu Jun 21, 2012 10:10 am

In other programming languages, yes - two threads might read bBusy at the same time, and proceed to do stuff together. Papyrus has more synchronization built in, and I don't think that can happen. But if bBusy could be changed anywhere else there'd be trouble. States are surely safer.
User avatar
Czar Kahchi
 
Posts: 3306
Joined: Mon Jul 30, 2007 11:56 am

Post » Thu Jun 21, 2012 2:25 pm

Booleans don't work reliably.

I think the other method the wiki talks about is:

Event OnInit()  RegisterForSingleUpdate(0.1)EndEventEvent OnUpdate()  ;Do Stuff  RegisterForSingleUpdate(0.1)EndEvent
User avatar
Bedford White
 
Posts: 3307
Joined: Tue Jun 12, 2007 2:09 am

Post » Thu Jun 21, 2012 6:23 am

To probe a little deeper, if I may - the wiki page says "If the script calls another function in an external script, or calls a "latent" function (a function that does something on its own before returning), it lets in the next thing from the queue" (implying that otherwise the next thing is on hold).

In the Boolean example I quoted, there is no external or latent function call between Boolean test and Boolean set. If that doesn't work reliably in TES5 Papyrus, are we saying that the wiki is wrong?

I'm more than happy to use states as the preferred technique, but knowing exactly when one thread can pre-empt another is of wider significance when trying to write robust scripts :ermm:
User avatar
Siobhan Thompson
 
Posts: 3443
Joined: Sun Nov 12, 2006 10:40 am

Post » Thu Jun 21, 2012 2:09 am

The wiki is correct, your example would set the Boolean before another thread could come in and check the value because there are no calls to external scripts or latent functions between the if check and the setting of the Boolean.

However, in this case, states are still better. Your OnUpdate contains script code and therefore the VM cannot optimize any calls to it away. Additional OnUpdate calls will stack up, consuming memory and resources until they can run. Sure, they can trivially check the Boolean and exit, but the VM still has to run your code and all the resources and time that entails. If you use a state and in that state is an empty OnUpdate event then the VM can toss out the event request entirely, saving resources and speeding up your script.
User avatar
Jodie Bardgett
 
Posts: 3491
Joined: Sat Jul 29, 2006 9:38 pm

Post » Thu Jun 21, 2012 7:21 am

I'm more than happy to use states as the preferred technique, but knowing exactly when one thread can pre-empt another is of wider significance when trying to write robust scripts
Not robust scripts, correct scripts. Code with a race-condition is not less robust, it's just wrong.

To probe a little deeper, if I may - the wiki page says "If the script calls another function in an external script, or calls a "latent" function (a function that does something on its own before returning), it lets in the next thing from the queue" (implying that otherwise the next thing is on hold).
Yes, we know that, too public-shared data (properties, which are functions) access is synchronized (queued) from the wiki as well as SmkViper. Your example uses private, non-shared data (a field), so this doesn't apply -- as far as we know.
With that in mind, I can't say why it would not work, as long as the boolean is only a field.

A special case may be the OnUpdate event, if it was registered with a low interval. Then it would depend on how other script-instances are instantiated.
Either created and initialized as usual, from scratch. That could cause confusion, but has no intrinsic logical issues. Unless of course, what is being guarded modifies shared data.
On the other hand, if the original instance where to be cloned, than that results in a classical race-condition from the getgo.
Ah well, this case is to be avoided anyway...

P.S.: Using Hungarian notation in a statically typed language to annotate types is bit silly.

Edit: Guess I did take to long to post...
User avatar
Kirsty Wood
 
Posts: 3461
Joined: Tue Aug 15, 2006 10:41 am

Post » Thu Jun 21, 2012 8:38 am

Here is how I explain states to myself, in case it helps: states allow us to give objects alternative sets of events.

http://www.joeydevilla.com/wordpress/wp-content/uploads/2012/04/mind-blown.gif

Seriously, States have never made any sense to me in terms of their purpose; "Why would i want to use that? I can just use boolean flags to segregate logic." Now it all makes sense. Thank you!
User avatar
darnell waddington
 
Posts: 3448
Joined: Wed Oct 17, 2007 10:43 pm


Return to V - Skyrim