Discuss Scratch

mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

I hope this is an OK topic to create. I can't seem to get the topic search to actually search for something. Probably on my end.

In the interest of avoiding the “XY problem” (where I ask about what I'm attempting, instead of asking for help with what I'm actually trying to accomplish) I'm looking for a way to create sprite clones and have each clone maintain some clone-local storage. I'm happy to be wrong, but from what I can tell all variables and lists declared at the clone level are common to all copies of the clone: if each sprite is a class, all class members seem to be static only.

I'm getting to my question. Bear with me. :-)

I think I can work around this: it seems function parameters are not static, so I can make clones unique by incrementing a counter, passing that new counter value to a startup function, and having that startup function never exit, to keep its function parameter as a mark of uniqueness. I could for example use lists in place of variables and use that unique value as an array index, so each clone could maintain its own local storage.

Scratch has no synchronization primitives though. If two clones are created at the same moment, their reads and increments of the counter value could overlap, leading to two clones started with the same counter value. I could guard the read / increment / call-function code with an “is locked” variable, but how do I know reading and updating the is-locked variable is any more atomic? Or am I way overthinking things: can I just trust that “wait until my_semaphore > 0; let my_semaphore = my_semaphore - 1” will be atomic?

Thanks in advance.

(Freshly minted Google CS First guru here. I made https://scratch.mit.edu/projects/173856367/ as a way of introducing myself and the third graders really liked it. Scratch is a really fun environment. I didn't think I'd like it much, but the limitations provide some extra challenge.)
TheUltimatum
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

From what I understand you are doing this:

when gf clicked
delete [all v] of [private v] // public list
set [clone_id v] to [0] // private variable
repeat (10)
change [clone_id v] by [1]
add [] to [private v]
create clone of [myself v]
end

when I start as a clone
replace item (clone_id) of [private v] with (pick random (1) to (10)) // or whatever data you need

And you want to know if there will be conflicts between one or the other if you have more than one of these “clone factories”? There shouldn't be as long as they are all separate sprites. Also you mention semaphores? Do you mean something like flags? e.g.
when gf clicked
say [The flag is in effect.]
set [flag v] to [1]
broadcast [wait_for_flag v]
wait (5) secs
set [flag v] to [0]

when I receive [wait_for_flag v]
wait until <(flag) < [1]>
say [The flag is no longer in effect!]
That's what I call them. They are useful for stopping a script that is in another thread for a while.
(Also the way you wrote your question is very confusing. Especially to someone who hasn't passed a college CS class before. It may be a good idea to write things in more simple terms and you'll get higher quality answers.)

Last edited by TheUltimatum (Sept. 15, 2017 02:14:35)

MartinBraendli2
Scratcher
100+ posts

How atomic are reads and writes, for synchronization purposes?

mspencer712 wrote:

I'm happy to be wrong, but from what I can tell all variables and lists declared at the clone level are common to all copies of the clone: if each sprite is a class, all class members seem to be static only.
There are no static Variables like for example in Java. There are only global variables (“For all sprites”) and local variables (“For this sprite only”). Local variables are unique for each clone. At the moment you create a clone, they will have the same value as the sprite (or clone) it is cloned from, but changing the value in one clone won't affect the value of the variables of the original/ other clones.

So with this, its way easier to create unique IDs. This is how I would create 10 instances (1 original + 9 clones):

when green flag clicked
set [id v] to [1]
set [NumberOfInstances v] to [1]
repeat (9)
create clone of [myself v]
end

when I start as a clone
change [NumberOfInstances v] by (1)
set [id v] to (NumberOfInstances)

“NumberOfInstances” is global, “id” is local (“For this sprite only” checked").


mspencer712 wrote:

Scratch has no synchronization primitives though. If two clones are created at the same moment, their reads and increments of the counter value could overlap, leading to two clones started with the same counter value.
There is no real concurrency in Scratch. It all is a single thread, so each “sprite” and each clone are executed one after another. So my code doesn't lead to collisions of IDs. Nevertheless I often avoid having counters such as “NumberOfInstances” (I don't like having to many global variables, because it takes a long time to scroll to the one I need):

when green flag clicked
set [id v] to [1]
create clone of [myself v]
when I start as a clone
change [id v] by (1)
if <(id) < [10]> then
create clone of [myself v]
end
This will also lead to 10 instances with different IDs (if “id” has the “for this sprite only” checked).
“create clone of myself” will create a clone of a clone here, and not a clone of the original.
Also worth noting: When you press the green flag or the stop button, all clones will be deleted, so you always start with 0 clones (1 instance), even if you ran your project before.


You also might want to try out the “run without screen refresh” checkbox when creating a custom block (function). This will make the function execute within 1 frame (1/30 s), so you could for example use it in your “draw_maze” function.
mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

@TheUltimatum: sorry about that. You're not wrong. I was writing for a CS grad audience and maybe shouldn't have been.

@MartinBraendli2: yikes, you're right. I could've sworn I created a clone-local variable and had different clones walking on each other, but I just created a test project and found that not to be the case. Even in turbo mode each clone maintains its own step counter, so it turns right at exactly 90 steps per side.

OK, thread over, I'm kind of an idiot. Sorry all and thanks.
_nix
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

mspencer712 wrote:

OK, thread over, I'm kind of an idiot. Sorry all and thanks.
You're not an idiot, you're awesome and there needs to be more people like you! I love seeing how fairly advanced programmers (i.e., those with experience in other languages) accomplish tasks in Scratch. Plus now you know more about the “Scratch way” of doing “classes” and “instances” (which are really the whole clone system you just learned about).

You might also want to learn about broadcasts; basically, they're "send a broadcast to all sprites and clones (including me)". They're definitely handy and important when using clones.

I'll show you an example, but first you should be familiar with my way of handling clones (which is pretty similar to most other people's). Here's some example code:

when green flag clicked
hide
set [my clone ID v] to [1]
repeat (10)
create clone of [myself v]
change [my clone ID v] by (1)
end
set [my clone ID v] to [original]

when I start as a clone
go to [random position v]
show

This creates ten clones, each of which have their own “my clone ID” value (since that variable is marked “for this sprite only”). It marks the actual sprite (the one that ran that script) as the “original”.

I hide the original sprite, and show the clones. Why is it important that I distinguish between the original sprite and the clones of it? – Because it can be sort of difficult to control both sprites and clones with the same code. Clones can be killed, just like you can delete objects; there's actually a “delete this clone” block. But “delete this clone” doesn't do anything when an original sprite runs it. (In fact, it doesn't even show an error message! Scratch doesn't have any error system like most programming languages.)

Now we can use broadcasts to actually do something.

Inside of the stage, not the sprite that makes clones, I make this script:

when [space v] key pressed
broadcast [jump v]

Then, inside of the sprite which makes the clones (the one we were originally working in), I make this script:

when I receive [jump v]
if <not <(my clone ID) = [original]>> then
change y by (10)
end

It's important to understand that, when the “broadcast (jump)” block is run, every clone, and every sprite (and even the stage), will all run their respective “when I receive (jump)” script.

I've already explained that when I'm programming with clones, I like to separate clones from the original sprite, because otherwise it's tough to write and debug my code. That's why I do “if (not my clone ID = original)” in the “when I receive jump” script. Essentially, we're saying “all clones should run this script; the original sprite should not”.

That about explains how broadcasts work.

(Some other interesting things to explore is the “broadcast and wait” block, which runs all the “when I receive”s in the project of the given broadcast name, and waits for them to complete. Another fairly separate topic is that if you send a broadcast when a “when I receive” of that broadcast is already running, that “when I receive”'s execution will immediately be halted. This lets you do cool things like cancelling the “ask and wait” prompt using your own code.)
mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

Hey, I can be awesome and self-deprecating at the same time. :-) Thanks very much – I think I'm following this better.

Regarding the last bit: I played with the sample project a little and I think you might not have communicated the message-when-message-handler-already-running behavior well. It seems like if I broadcast a message and a handler for that message is already executing, that running execution stops and a new execution is started. You kinda made it sound like a toggle.

That clone-factory pattern makes sense. Thank you. If only we could call one sprite's functions (and pass parameters) from outside that sprite entirely.

(I made another thing. It's not much. If I'm lucky maybe I can get cloud variable access within the next week. I mean, if you know someone . . . :-) )

Next Google CS First class with the third graders is Monday after next. I'm hoping by then I'll have a solid game-appropriate MVC pattern figured out, so I don't have to rely on screen-scraping functions for game logic and can have a play space that pans and zooms.

Last edited by mspencer712 (Sept. 16, 2017 02:15:16)

_nix
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

mspencer712 wrote:

Regarding the last bit: I played with the sample project a little and I think you might not have communicated the message-when-message-handler-already-running behavior well. It seems like if I broadcast a message and a handler for that message is already executing, that running execution stops and a new execution is started. You kinda made it sound like a toggle.
Yep, you're right. It's late here..

mspencer712 wrote:

That clone-factory pattern makes sense. Thank you. If only we could call one sprite's functions (and pass parameters) from outside that sprite entirely.
Yeah, that's certainly a wanted feature.

As a bit of a workaround you can try something like this:

set [target clone ID v] to [3] // These are both global variables.
set [distance v] to [20]
broadcast [jump v] and wait

when I receive [jump v]
if <(my clone ID) = (target clone ID)> then
change y by (distance :: variables)
end

It's a bit more involved to make multiple clones jump in one “broadcast jump”, though (if you want to make each one jump a different distance and/or you only want some clones to jump).

mspencer712 wrote:

(I made another thing. It's not much. If I'm lucky maybe I can get cloud variable access within the next week. I mean, if you know someone . . . :-) )
I saw that project! Funnily enough, when I looked, the project's Instructions and Notes/Credits fields weren't filled out. So all I saw was "This project demonstrates how to emit constant-speed projectiles which wi"! But clearly those are filled in and saved now, so I'll have to take a look again.

If you didn't know, you'll get access to cloud data as soon as you're of the Scratcher rank (not a New Scratcher). Here's some info on the difference, and how to become a Scratcher.
mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

Thanks again.

This is heading way off of the original topic but: are there any reference materials I could look at which talk about the specifics of the execution environment? Without resorting to finding and reading source (and I never learned how to do anything with Flash so I expect that experience to be painful), I'd love more info like what you've been sharing, and it makes total sense that the Scratch team wouldn't put that info in front of most Scratch users.

For example: there's apparently pens and stamping, but is all of that handled in a single layer? I assume Scratch uses a buffer or layer for pens and stamping, one for the current background, one for any video overlay, and one for each sprite and clone – and then composites them by alpha blend and Z buffer. Close?

You mentioned everything is single threaded, but how is task switching and scheduling handled when multiple long-running scripts are competing for time?

Please link me instead of explaining in the thread. You've been amazingly generous with your time and I don't want to take any more of it. No need to answer tonight, and thanks in advance.

(Edit: well that was fast. Now to see what cloud variables can do. :-) )

Last edited by mspencer712 (Sept. 16, 2017 03:31:43)

_nix
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

mspencer712 wrote:

This is heading way off of the original topic but: are there any reference materials I could look at which talk about the specifics of the execution environment? Without resorting to finding and reading source (and I never learned how to do anything with Flash so I expect that experience to be painful), I'd love more info like what you've been sharing, and it makes total sense that the Scratch team wouldn't put that info in front of most Scratch users.
Unfortunately there's no official reference like that, as far as I know. Everything I know is stuff I've picked up by making things with Scratch and seeing how other people program. TheLogFather does have some interesting studios that cover lots of smaller details like these:

Useful HowTos & Custom Blocks: https://scratch.mit.edu/studios/357190/
Speed tests - how to do things faster: https://scratch.mit.edu/studios/795672/
..and Scratch Glitches: https://scratch.mit.edu/studios/1678536/

Looking at those will probably help you pick up some things!

(Actually, I've been meaning to make a reference like you described, myself. It's been an idea in the back of my head for a long time. Here's some work I did a while ago; it doesn't have any actual content, though.)

mspencer712 wrote:

(Edit: well that was fast. Now to see what cloud variables can do. :-) )
Hehe. Have fun with them! Cloud variables can't store normal text, but you can convert normal text strings into digit-only strings and then store that in a cloud variable. (You can also store hex digits – so, 0-F – in cloud data. Here's a demo.)
_nix
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

mspencer712 wrote:

For example: there's apparently pens and stamping, but is all of that handled in a single layer? I assume Scratch uses a buffer or layer for pens and stamping, one for the current background, one for any video overlay, and one for each sprite and clone – and then composites them by alpha blend and Z buffer. Close?
I'm not certain what alpha blend and Z buffers are; but Scratch does have a separate layer for each of the things you listed. You can (more or less) control the order of sprite/clone layers by using the “go back (1) layers” and “go to front” blocks. (I was going to find a project I made a while ago which demos this, but I can't find it..!)

The pen layer always displays behind all sprites and clones, which is a little bit unfortunate, because it means you can't use the pen to draw UI which is above a clone or sprite. So if you're making a game that relies on that, you actually need to make everything rendered with pen..! Here's an example of that: https://scratch.mit.edu/projects/150986523/ (Hint: the stamp block is your friend.)

You mentioned everything is single threaded, but how is task switching and scheduling handled when multiple long-running scripts are competing for time?
Here's a post by TheLogFather (the same person who made the projects in the studios I linked!) which'll be helpful: https://scratch.mit.edu/discuss/post/2576750/

Please link me instead of explaining in the thread. You've been amazingly generous with your time and I don't want to take any more of it. No need to answer tonight, and thanks in advance.
I was going to find a link to the sprite layering demo but I couldn't find it

I'd love to work on the reference project I linked before some more. It's not like I don't have spare time, so, it's possible..!

Edit: oops, a double post! Sorry :)

Last edited by _nix (Sept. 16, 2017 13:27:25)

mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

Alpha blending is a way of mixing colors using transparency values.

Suppose you have two different colors, from two contributing sources, but they both want to be drawn to the same screen pixel. How do you handle them? It depends on what kind of physical light interaction you're trying to simulate.

Are the two sources opaque? Then they can't both be shown. One must be “on top” of the other, and the color of the one which is on top is the one that gets drawn.

Are the two sources transparent? Then your job is more difficult. You need to mix the two color values to get an average. If each source contributes equally then the usual method is: for each color channel (R, G, and B) square each of the color values, take the arithmetic mean across each same color (so average all the R values, all the G values, all the B values – don't mix colors together or you'll get a mess), and then take the square root. This is done because normal 0-255 intensity scales for color are not linear – they're logarithmic. RMS (root mean square) preserves the scale in this case.

Are the two sources transparent in unequal ways? Does the transparency vary pixel-by-pixel? Clever graphics types have come up with a solution: a fourth color value for transparency. Instead of storing RGB you store ARGB: alpha (transparency – what % of fully transparent or fully opaque the pixel is), red, green, and blue. Then when mixing colors you include the alpha channel in your math, and you end up with pleasant-looking blended results.

Let's back up to the opaque example. So there's no blending: either something is covered up or it's visible. Suppose you have ten things to draw, each of which might overlap with each other. How do you decide whose pixels get drawn?

One method is to keep a Z-buffer for the whole screen. A Z-buffer is essentially a log or record of the “who occludes who” decision process. Every competing source is given a “distance”. Things which are on top (closer to the viewer) have a smaller distance, and things which are on the bottom (farther from the viewer) have a larger distance. If you want to avoid problems, everything should have its own unique distance.

Start your Z buffer initialized to a high value. Maybe every pixel gets a 255, which might be the farthest away anything will ever be in your scene.

Then you loop through each thing you're considering drawing. Which screen pixels does your thing want to be drawn on? OK for each of those pixels, is the Z buffer value higher than your thing's distance value? If the Z buffer value is higher, your thing can be drawn there: overwrite each Z buffer value for each of the pixels you wanted to paint with your thing's distance value. If the distance value is higher, sorry: your thing would have already been covered up by something else, so you don't get to draw there.

When you're done, the Z buffer shows the distance of the closest object at every screen pixel. You can then go back through your objects and, where the Z buffer values match, draw the color from that object.

(made another thing)
_nix
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

mspencer712 wrote:

Alpha blending is a way of mixing colors using transparency values.
Thanks for that whole explanation! It's very clear.

(A while ago, I made a “list of interesting posts”. Here's an updated index if you're curious. It is mostly actual suggestions to Scratch, though!)
mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

Going back to my original topic: I started working on a stupid-sized project and I seem to be having contention or timing issues.

Unfortunately the current project is a pain to unravel without documentation, as it's currently five things working together:

* a GUI system for taking bitmap images from Windows Forms (winforms) buttons and check-boxes, and simulating the mouse hover and input focus locking behavior
* A min heap, blatantly copied from my previous project
* A separate GUI handler for clicking to set the values of grid squares, to open (passable), blocked, start, or finish.
* An implementation of A* path-finding (or “undirected graph path search” if you're math-y) using the above min heap
* A presenter for the grid square values and most recently found path

Some of these things are fighting and I don't understand why yet, or I've made any of a hundred stupid implementation mistakes and haven't found them yet. What's the best process for seeking help, in a way that isn't too burdensome to the helper:
* Break each part into a separate Scratch project, make test harnesses, and request people view each of the five separately, “and now imagine they're all one project.”
* Just share the one project, and write up a few paragraphs about how it's supposed to work and some use cases.
* In addition to either of the others, write a C# winforms thingey and put it on github or gitlab and say “here's what I'm trying to do.” Hey, I'm a professional – I'm fast and fluent with C#.

Ultimately I'd like to have this project working by Monday evening, which is session 2 of our Google CS First thing with the third graders. And I'd like to seek help in the least annoying way possible. That means NOT just sharing the project as-is and saying “somebody fix it.”

Thanks in advance.
_nix
Scratcher
1000+ posts

How atomic are reads and writes, for synchronization purposes?

My (fairly inexperienced) two cents:

Create separate projects which each have their own test cases (automated tests are obviously the best, but maybe a little bit of a pain to do with Scratch?). Naturally, if one isn't working, the rest of your entire big project probably won't work. So you could even narrow down the issue yourself. Besides that, having the projects separate helps people understand the whole thing a bit better.

If it's timing/competing-for-timing, sharing the original project would be helpful, too. You might not have joined the components together properly (which a programmer more experienced with timing and Scratch in general might be able to see a solution for clearly).

Also, explaining the project using plain old simple terms is probably useful. Describe what your objective is, and the steps you're taking for it. It'll explain it to people reading your question, and to yourself. (I am experienced with that; it usually helps — so does walking through the logic of code, speaking out loud, actually..!)

Unfortunately the Scratch forums are very busy with, well, not-so-detailed questions. Definitely consider posting it in both Help with Scripts and the Advanced Topics! (Maybe post the question in one forum, and if it doesn't get any attention for a long while, make the topic in the other forum… but I don't really know the best thing to do, since you're working on a pretty tight schedule!)
mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

I'll do that. Thanks.
MartinBraendli2
Scratcher
100+ posts

How atomic are reads and writes, for synchronization purposes?

Synchronizing in Scratch is really hard. Even after >1000h of experience, I still rely on trial and error when trying to get multiple “Sprites” (in Scratchs meaning of the word) to execute in the right order. Part of the problem is, that the execution order depends on what layer a sprite is, which can change during runtime.
That's the reason why I try to avoid the problem as much as possible. I usually have one master sprite that does all the heavy lifting. Not only is it where all the logic happens, but it also orchestrates all the slave sprites. The slaves are usually only there for user interaction (click detection, displaying stuff, sound etc.). If its a for example a game, there is only one main loop, which is in the master sprite. After it has done all the game logic, it sends a couple of broadcasts, so that enemies, objects and UI can change visibility, position and costume.
Advantages:
- You can always use real functions with parameters instead of tinkering with broadcasts and global vars
- Full control over the order of the flow
- You can use local variables/ lists (which can be accessed faster than global one's)
Disadvantages:
- You might end up with a LOT of scripts in one sprite, which makes navigating to the needed sprite harder
- You end up with a lot of variables that you can't hide (scrolling problem)

My 3d renderer is an example for this master/slave principle. The sprite “Renderer” does almost everything (except parsing the .obj and loading the bitmaps, which only has to be done once). The other sprites handle only their own appearance.
FandomTrashXxX
Scratcher
3 posts

How atomic are reads and writes, for synchronization purposes?

Why is everyone here so intelligent, like dang. I am not at your level.
mspencer712
Scratcher
9 posts

How atomic are reads and writes, for synchronization purposes?

To be fair: you can get to where we are. It gets easier with time and practice. Also, I'm forty years old and I write software for a living. It's not fair to measure your progress against mine.

(And I deliberately phrased the thread title so it would attract computer science-y folks, because I felt like I had a question which could only be answered by someone who knows what a semaphore is AND who has a lot of experience with Scratch. That may have been selfish of me and I apologize.)

Powered by DjangoBB