Snap's scheduler and primitives are designed such that a wide range of projects with anything from a single sprite with a single stack of blocks to a bunch of sprites and a bunch of parallel scripts will run at roughly the same speed across a wide range of different devices running different operating systems and different web browsers. We made it so that kids on their public-school issued Chromebooks can collaborate with rich kids sporting shiny new Macs, with their nerd classmates on recycled Linux machines and their geek friends on gaming PCs with overclocked CPUs. We're achieving this by slowing Snap's scheduler down so much that it's basically sitting idle for most of the time. The idle time inside each clock cycle is designed to be so long that it serves as a buffer that projects can grow into without slowing down noticeably. Because we love teaching about emergence and music we've also added a "turbo" mode so we can fast forward the drawing of fractals and allocate a higher timing resolution to polyphony. And because we also love teaching recursion we've further added a "warp" block so we can run a loop as a single block without screen refresh.
"Warp" only makes sense if the loop it is wrapped around completes well within the time of a clock tick, otherwise instead of speeding things up it actually slows things down and renders the IDE less responsive to user interactions. This happens if you warp large parts of a script, e.g. very long loops that do a lot, or if you warp parts of several scripts that run in parallel, so the time it takes for one tick to run extends into the scheduler's "idle buffer". At some point, when what should normally be over in 15 ms takes longer than half a second we break up long warped sections and long recursive reporter evaluations by filling in emergency yields, so users get a chance to press the red stop button to get themselves out of infinite recursion because they forgot a base case.
At every display cycle ("frame") our scheduler does a round robin running one atom per process after another. Every process gets to run a step, we're not ever skipping one. But in the case of warp (or if you're maxing out the scheduler in some other way) we'll run as many steps as we can for half a second and then we'll yield every following process at the earliest possible moment. Since we're not skipping any processes it may actually take longer than half a second for the emergency yield to take effect. Also, because this is, after all, an emergency mechanism, protected code sections are then no longer guaranteed to be atomic.
Here's an example of a maxed-out scheduler:
Pressing the green flag button starts three processes, one for each script. At every display cycle the scheduler does a round robin telling each thread to run one atomic step. After each round the thread manager removes the terminated processes before letting the scheduler call the following round.
The first process begins counting up to a Million, which takes a little over a second on my laptop. After half a second the scheduler interrupts the first process and runs the second one. Since the frame already went on for over 500 ms the second thread only runs a few steps before being emergency interrupted itself. The scheduler now turns to the third process and it, too, only runs a few steps before being cut short. If you look closely you'll see that the second and third processes count up the exact same numbers in this frame.
Same thing. The first process still doesn't reach the Million in less than 500 ms and again is emergency-interrupted. Again the second and third threads only run a minimal set of instructions each, because the overall time allocated to the frame has already been maxed out by the time it's their turn. If you look closely you'll see that this time the number of instructions the second and third threads are able to run before being cut short are slightly differing. This is because the scheduler doesn't query the system clock at every single instruction, which would cost too much CPU time itself, but only after about every 100 blocks.
The first process reaches the Million and runs the "stop all" block. The scheduler still completes its current round robin for every thread. The second process doesn't make it up to a Million in time and is cut short. But because the first process didn't take longer than 500 ms the second thread had way more time and was able to perform more instructions than during the first 2 frames before being interrupted. Again, because the scheduler is at this time in emergency mode the third process only gets to run a minimal set of instructions before being cut short.
After the third frame the thread manager sweeps the terminated process off its "turntable" which leaves none to be stepped at the next frame.
Dunno what you mean with "policy", but this part of the evaluator is actually somewhat well documented...