Discuss Scratch

eaglgenes101
New to Scratch
100+ posts

Race condition question

Suppose player 1 is behind a firewall and has long ping times . The instance of the project tells it to change a cloud variable by 2; at the beginning it is zero. .5 seconds later, another player without a firewall runs an instance of the project, and it changes the cloud variable to 4.

What are the results?

Last edited by eaglgenes101 (Aug. 22, 2013 19:43:03)

DadOfMrLog
Scratcher
1000+ posts

Race condition question

Totally depends who gets there last.

Cloud vars are really not robust enough for such situations, so you've gotta be pretty careful. Actually, one of the worst things is the delay in getting updated values after you change a cloud var (you often don't get any updates for over a second after you make a change, so if you're changing a cloudvar within about every second or so of the last change, you will never see *any* other cloudvar values change… )

Take a look at my ping test project to get an idea of how long it takes for the ‘round-trip’, and how hard it can be to make something that's even reasonably robust.

I use a couple of methods to try to help:
1) Allocate each ‘connected user’ a different cloudvar - yes, this means you can only have a few players atm (max. of ten cloudvars)
2) Use a cloudvar with a unique user id to ‘lock’ access - and have to check a few times, each time waiting over a second, to ensure you've really ‘got’ the lock…

Hope that's of some use…

Last edited by DadOfMrLog (Aug. 22, 2013 22:05:36)



Alternate account: TheLogFather –– HowTos and useful custom blocks (see studio). Examples below…


- String manipulation - - - X to power of Y - - - Clone point to clone - Detect New Scratcher - Speed tests studio -

jgatcomb
Scratcher
90 posts

Race condition question

DadOfMrLog wrote:

2) Use a cloudvar with a unique user id to ‘lock’ access - and have to check a few times, each time waiting over a second, to ensure you've really ‘got’ the lock…

Hope that's of some use…

I haven't yet played with cloud variables but I do have years of experience dealing with race conditions. I think the only way to completely eliminate race conditions would be the following (which I assume is what you meant by option 2):

Create a cloud variable used to designate who is allowed to write to the other cloud variable (I would suggest encoding the username into a number which guarantees each user is unique)

Next, you have to create a value that signifies that you are done using it and it is available for someone else to use (say -1)

Finally, when you want to write, you have to go through the following steps

repeat_until(lock = -1) {
wait(0.05)
}
set lock = <my_encoded_user_id>
repeat_until(NOT lock = -1) {
wait(0.05)
}
if (lock = <my_encoded_user_id>) {
# Only now is it ok to write
}
else {
# Someone else got there first - go back to the beginning
}

Cheers,
Joshua
DadOfMrLog
Scratcher
1000+ posts

Race condition question

Yup, what I do in the ping test project is very similar to that.

However, I wrestled quite a while with this, and what I found suggests a couple of gotchas with cloudvars that mean even what you've outlined above still could fail…

First of all (a simple one), if someone stops their project (or navigates to a different page, or loses connection, or…) while they have the ‘lock’, the cloudvar will then be ‘stuck’ with their lock ID. This means, in practice, if you find the lock is not set to the unlocked value (-1 in your case), you eventually have to just give up and carry on anyway if it doesn't change its value at all for a number of seconds.

Secondly, when you change a cloudvar, you always see the new value immediately yourself - even if it actually failed to really get through to the server! That means there's no point waiting for it to change from the unlocked value. It also means you don't ever know for sure that you really have the lock - it's only if it changes from the lock ID you've set it to (and not back to its previous value, likely unlocked, but back to some other lock ID - see below) that you know for sure that you *don't* have the lock. In other words, unless such a ‘bad’ change happens, you just have to carry on and hope for the best…

Thirdly, cloudvars can be temperamental in their behaviour for a couple of seconds after you've set them. It's quite possible (sometimes even common) for a cloudvar you've just set to switch back to its previous value. I'm not sure why, exactly, but my guess would be that what happens is that the latest poll of cloud values sent from the server arrives just after you've sent it the new value. But those values were sent just before the server received your new value and made the update, so still contain the old value, thereby nullifying the change you've just made to your own cloudvar (and also, note, meaning you're out of sync with the server).
I think the only thing you can really do if this happens (i.e. if you notice the lock reverts back to the unlocked/previous value) is to just carry on and ignore it, and assume that the timing meant the messages crossed in transit - either that or wait another couple of secs and see if it corrects itself, or even go back to the start and try the whole thing again. It's only if it changes to something other than the previous value that you can feel confident someone else is accessing the lock at the same time, and so go back and wait for it to unlock (or timeout) again.

Fourthly, because there's this (at least) one second delay after making a change to a cloudvar, before you see the next update from the server (and probably another half-second or so before that for the server to have actually received and processed your change), you can't really assume you've ‘got’ the lock for at least a second and a half after you set it. It's only after that time is up that you will see any update from the server, and so see if someone else has got it instead. In practice, it's probably a good idea to check you've got the lock a handful of times over a period of 3 or 4 seconds after you set it. Kind of a downer for multiplayer game performance…

Finally, just a minor note that it's possible to run a project as the same user from two places. I do encode the username to create a lock ID, but I also add a random string of digits to the end to (hopefully) ensure it's also unique!

Anyway, I hope that hasn't put you off cloudvars too much!

Last edited by DadOfMrLog (Aug. 23, 2013 00:58:50)



Alternate account: TheLogFather –– HowTos and useful custom blocks (see studio). Examples below…


- String manipulation - - - X to power of Y - - - Clone point to clone - Detect New Scratcher - Speed tests studio -

jgatcomb
Scratcher
90 posts

Race condition question

DadOfMrLog wrote:

Yup, what I do in the ping test project is very similar to that.

However, I wrestled quite a while with this, and what I found suggests a couple of gotchas with cloudvars that mean even what you've outlined above still could fail…
Anyway, I hope that hasn't put you off cloudvars too much!

Wow, thank you.

Problem 1 is a common one in other areas (using a lock file). People attempt to get around it by using the process id of the program that obtained the lock - that way you can check the process table to see if it is still running. While unlikely, it is not impossible for a different program all together to get the process ID between the time the actual program died and the second program checked. It is also possible for the file to get deleted (presumably by a sysadmin that has more power than sense) making it appear that the running program doesn't have a lock. For this reason, I prefer to use semaphores handed out by the kernel or by opening a random network socket - both of which, the OS will clean up if the program goes away. The only way I could think to continue to employ this strategy is by using yet another cloud variable to communicate with the program locking it. If it fails to respond to “are you still running” after a specified period of time - set the lock status back to -1.

Problems 2 - 4 just make cloud variables unusable to me for anything that I can think of write now. I am not put off by them but knowing that I can't guarantee isolated operations means I will have to think of different ways of using them.

Cheers,
Joshua

Last edited by jgatcomb (Aug. 23, 2013 12:16:00)

Powered by DjangoBB