leopard icon indicating copy to clipboard operation
leopard copied to clipboard

Investigate "wait" block to avoid busy-waiting

Open towerofnix opened this issue 1 year ago • 3 comments

Following #212, the current implementation for Sprite.wait will busy-wait if no other active threads are requesting a screen refresh.

Our Sprite.wait is (loosely?) modeled after scratch-vm: wait, stack timer, Timer.

If it's possible to implement "wait" in terms of a promise, this would avoid busy-waiting. However, we obviously need to retain behavior semantics, i.e. not break any existing behavior.

I don't know if there have been any previous efforts or discussion, particularly in scratch-vm, to implement "wait" in terms of a promise (and thus consideration of compatibility concerns). We did a cursory look for pull requests and didn't spot anything, but it might be worth a closer look!

towerofnix avatar Jul 16 '24 17:07 towerofnix

I believe it was previously implemented as a promise, but changed for compatibility reasons. The "say/think for () secs" block should probably be changed to use a timer as well, but 3.0 currently doesn't.

adroitwhiz avatar Jul 16 '24 17:07 adroitwhiz

Thanks! See https://github.com/scratchfoundation/scratch-vm/issues/904#issuecomment-362680457 in particular (this comment predates the fix linked above, which basically implements that proposal, just not for say/think).

In any event, "wait" in 3.0 is now in terms of the custom nowObj which gets the runtime's currentmsecs. It is updated at the start of each frame.

If dating back to review scratch-flash behavior, https://github.com/scratchfoundation/scratch-flash/pull/1396 is possibly of interest. There is some further discussion and issue-linking in https://github.com/scratchfoundation/scratch-vm/issues/2138, more to do with run w/o screen refresh than timing behavior in particular.

towerofnix avatar Jul 16 '24 18:07 towerofnix

I guess the problem is that the only way to wait a precise amount of time in JavaScript is to, well, busy-wait that amount of time. If you're right that "wait" only busy-waits https://github.com/leopard-js/leopard/issues/212#issuecomment-2231400457

in the absence of any redraw requests

(which makes sense to us—after all at this point Scratch is deliberately waiting til the next frame), then this is precisely an issue when the next execution could be at exactly the specified delay time.

If there's no way to get around it then that's probably that, but it's irritating. Not a serious issue in most projects (which always have stuff moving and updating the screen), but still.

This answer on Stack Overflow is a good answer as of 2014: https://stackoverflow.com/a/21117513/ — and the situation doesn't seem to have changed much, except that setImmediate whent nowhere. Particularly this point:

If you are using setTimeout() on a slower cadence, e.g. once every 300 milliseconds, you could use a solution similar to what user1213320 suggests, where you monitor how long it was from the last timestamp your timer ran and compensate for any delay. One improvement is that you could use the new High Resolution Time interface (aka window.performance.now()) instead of Date.now() to get greater-than-millisecond resolution for the current time.

Also room to look at tock, mainly developed 2014-2015. And a 2010 sitepoint article that inspired/directed tock as well as user1213320's linked answer. Would be nice to find any more recent resources about this problem, still.

towerofnix avatar Jul 16 '24 18:07 towerofnix