Discuss Scratch

Aclassifier
New to Scratch
4 posts

Sending and waiting for messages in Scratch (its runtime)

I am preparing a guest lecture in the real-time programming course at NTNU in Trondheim. I am a retired programmer, who have coded safety-critical embedded systems (mostly fire detection) for 40 years.

I wondered how the runtime of Scratch works, but it was difficult to pick up the full story from . But I do see that Scratch is being translated to Javascript and that in scratch-vm Node.js is used.

I wondered how the application level synchronising of page 71 (of the Norwegian version at least) of Computer Coding for Kids
 by Carol Vorderman works? (“Send message Hello and wait”). Is it an asynchronous message sent and an asynchronous ack message waited for? I once tried to collect some info about Node.js in . Will the Java Virtual Machine do the event loop also for Scratch? Is there any type of Go-type concurrency in there (like zero-buffered synchronous channels, CSP)? Core.asynch is a CSP-type library for Clojure, both developed by Rich Hickey. Would any thougths from this have diffused into the Scratch runtime?

Or am I thinking too difficult? I know the above is messy, it only reflects why I ask.. That the Scratch «process model» is a big main with multiple loops and a separate scheduler built for Scratch only?

I would be very happy to get me on track on some of the points here.

https://github.com/LLK
http://www.teigfam.net/oyvind/home/technology/084-csp-on-node-js-and-clojurescript-by-javascript/ Disclaimer: no money, salary, gifts or anything else then fun and expenses on my blog notes!

Last edited by Aclassifier (April 24, 2019 08:19:31)

novice27b
Scratcher
1000+ posts

Sending and waiting for messages in Scratch (its runtime)

I think you are expecting too much of scratch

My understanding (which may not be totally correct) is that there is a single main loop, which iterates through all the active “threads” and executes each one in turn, until it yields. A thread that is waiting for something will yield immediately.

“broadcast (thing) and wait” yields the current thread, and anything listening for the “when I receive (thing)” will become active. Execution of the broadcasting thread doesn't resume until all of the “when I receive (thing)” threads have completed. I haven't looked at the implementation, but I assume there is some kind of counter which records how many of these “listening” threads are currently executing for a given event.

i use arch btw
Aclassifier
New to Scratch
4 posts

Sending and waiting for messages in Scratch (its runtime)

novice27b wrote:

I think you are expecting too much of scratch
Thanks a lot!

I guess that if it works according to spec than that's all that's needed. The functionality you describe certainly gives Scratch a “process model”. And there would be big loops in every “task”, and waiting and sending and timeouts and waiting on (keyboard/mouse) events. Much more than many other languages.

So each task must (weaker: would (weaker: may)) deal with only its own things. That's what concurrency is about (as opposed to parallelism).

I guess what you are saying is that this is handled in runtime JavaScript scheduler code coming with Scratch, and not handled over to Node.js?
_nix
Scratcher
1000+ posts

Sending and waiting for messages in Scratch (its runtime)

This thread is really interesting to me, even if the vocabulary or concepts are a little over my head.. but I've shared solid, real examples of how Scratch executes projects many times before, and I hope that might be helpful here!

Suppose you have these two scripts (for our sake in the same sprite):

when green flag clicked
set [number v] to [10]

when this sprite clicked
change [number v] by (number) // double it

when this sprite clicked
change [number v] by (5) // add a constant value

When you click the sprite (after pressing the green flag to initially set it to 10), what will the number variable be set to? There are two possible values here, and the resulting one depends on what order Scratch executes the scripts in:
  • 25 - 10 doubled is 20, plus 5 is 25.
  • 30 - 10 plus 5 is 15, doubled is 30.
In general, we consider the order that scripts are executed in to be arbitrary. We can predict the order according to two factors: sprites which are higher (according to the Looks layer blocks, or that you've dragged to be above other sprites) execute before those that are lower, and within those, older scripts are executed first. So, as according to those rules, the order that scripts execute in won't change from tick to tick. We'll treat the order as arbitrary but consistent for our discussion. But hold on, what's this about “ticks”…?

Let's look at these scripts:

when green flag clicked
set [fruit v] to [apple]

when green flag clicked
wait (0) secs
set [fruit v] to [banana]

After these scripts have finished executing, what will the fruit variable's value be? You'd probably guess it's still arbitrary – i.e, it depends on which script runs first – but in this case it's actually always going to be “banana”. Let's look at why. When we click the flag, Scratch responds by searching for all “when green flag clicked” blocks and creates corresponding thread objects, which it stores internally in an ordered list. (As you'd figure, this list's order is arbitrary, per the rules described earlier. It represents the order that scripts will be executed in.) It's important to note that these are only thread objects – they are not actual new threads or anything like that. For our discussion, they're actually just references to the current block which is running in the thread object's corresponding script.

So, after we click our green flag, we'll have two thread objects stored internally – each pointing to its corresponding “when green flag clicked” block. At this point, Scratch begins executing its threads. To do this, it takes the first thread in its list, and runs that thread's corresponding blocks one by one until it reaches a point called a yield. At that point it moves on to the next thread, until it has done so for every thread. Threads which don't have any remaining blocks are removed from the list.

A yield is simply a flag set on the thread object, to say that it should move onto the next thread. This flag is set by a variety of Scratch blocks, but the two types which matter most are “wait 0 secs” and loops. The “wait” block actually always sets this yield flag, even if its passed value is zero; this is why it's useful as a simple “force a yield right now” block. Loop blocks, such as “repeat” and “forever”, set the yield flag when their iteration has finished (once the final block contained within the loop has executed).

Once Scratch has “ticked” every single thread (gone through each of them and executed their blocks each until reaching a yield, the process above), it will immediately do this tick process again, repeating until every single thread has finished executing and been removed from the list. (The exception is that it will pause for a moment and allow the screen to catch up if the “screen refresh” flag has been set by any Scratch blocks, such as the Motion and Looks blocks. Then it will continue ticking.)

So, at the end of the first tick, Scratch will have gone through and executed each of the two threads. For the “apple” thread, it first goes past the “when green flag clicked” block since it doesn't have any side-effects (besides reacting to the flag getting pressed), then evaluates “set fruit to apple”. Since that's the last block in the script, the thread is completed; Scratch removes that thread object from its list. For the “banana” thread, it skips past “when flag clicked” again, then reaches “wait 0 secs”. Since the block's value is zero, Scratch moves on immediately – but the “wait” block has the additional side-effect of setting the thread's yield flag. So, Scratch stops and moves onto the next thread. Remember, the order Scratch starts threads is effectively arbitrary! If the banana thread came first, Scratch moves on to the apple thread; otherwise, it has just finished the last thread, so it stops here, to begin the next tick imminently.

That's a lot to process, but in effect, at the end of the first tick, Scratch will have set the fruit variable to “apple” in one thread, and gone just past the “wait 0 secs” block in the next thread, having reached a yield there.

On the next tick, there is still one thread remaining – the one which reached a yield the previous tick. So Scratch continues that one, running the block – the fruit variable is set to “banana”. That's the thread's last block, so it is completed and removed from the list of threads; and since the thread list is now empty, the project is finished. So we see the final variable value is “banana” – on the first tick it is always set to apple, and on the second it's set to banana.

This is just a single example, but virtually every Scratch project's execution follows these rules. If you're curious or confused by anything here, I'd be happy to explain if I can!

══ trans autistic lesbian enbydoggirls // harbinger of the waking world, just like you // 16 17 18, she/they
sparrows one word to the paragraph // Johnny Crocker, R.I.P. // <3 // ~(quasar) nebula
Aclassifier
New to Scratch
4 posts

Sending and waiting for messages in Scratch (its runtime)

Dear Scratcher. Sorry, I did not see this before now! I will study your reply over the week-end and come back.
_nix
Scratcher
1000+ posts

Sending and waiting for messages in Scratch (its runtime)

Aclassifier wrote:

Dear Scratcher. Sorry, I did not see this before now! I will study your reply over the week-end and come back.
No worries!

══ trans autistic lesbian enbydoggirls // harbinger of the waking world, just like you // 16 17 18, she/they
sparrows one word to the paragraph // Johnny Crocker, R.I.P. // <3 // ~(quasar) nebula
Aclassifier
New to Scratch
4 posts

Sending and waiting for messages in Scratch (its runtime)

Thanks, Scratcher

Very well explained! I guess the problem here is that scratch allows side effect on a global variable, “fruit” as exercised by these “thread objects” that even have “yield”? Interesting to know.

I remember the “par” statement in occam, when I ran it on an old PC, even with 386, in the early nineties. If the parallel (most often they were concurrent) processes/task accessed a global variable it would not allow a single one like “fruit” if there were Read/Write (CREW) problems. The compiler would stop me. But if I had a bitmap buffer where each task processed their part of the picture, I could hear how the compiler worked and worked to figure out if there were overlapping handling. Most often a system was built in a 1-3 seconds, but this could take up to a minute. This was not allowed. But, some times it could not decide, and I was told that the compiler had inserted run-time checks. That being said, most often processes were used as being water tight and channels would be used between them. As in Go now.

Why does Scratch even allow this? Because concurrency is kind of hidden in the language? Would it be possible to have it force a user not to wait for the same event at two places? And of course, not allow situations that would depend on the sequence. In your case the single “yield” by the “wait(0)” must be the last, but they could both have arbitrary yields and then “fruit” is not defined as by code inspection.

I guess
(pick random () to (10))
is a better way…

Powered by DjangoBB

Standard | Mobile