BruceSherwood/glowscript

rate()-limited function returns early

Closed this issue · 6 comments

Context

#! /usr/bin/python
from __future__ import print_function
from visual import rate
def func():
  for ii in range(30):
    rate(30)
    print(".", sep='', end='')
  print("!!!returning from func()")
func()
print("***returned  from func()")

Desired, Expected, Behavior (standard python)

..............................!!!returning from func()
***returned  from func()

Observed Behavior (in the browser)

***returned  from func()
..............................!!!returning from func()

In more complicated, compute-intensive examples, I observe that the func returns partway through the loop, i.e. with some dots before the return and some after. The first time you get bitten by this bug, it is astonishingly confusing and hard to debug.

I surmise this is some sort of streamline issue.

That is indeed counterintuitive and presumably an error in the way the Streamline library works. I wonder whether it is even fixable in the current environment.

Streamline reminds me of the proverbial dog walking on its hind legs: One is not surprised that it works badly; one is surprised that it ever works at all. I agree that trying to fix it is almost certainly not worth the trouble. It would be an unwise use of resources.

  • Changing the asynchronous execution model is not easy, and will never be easy. Javascript doesn't support sleep(), and probably never will. The only way a snippet of code can suspend execution is by returning.
  • On the other hand, it is certainly doable. Javascript is, after all, Turing-complete, or close enough.

Probably the easiest way to do this is by compiling the python code, using an industrial-strength compiler than knows how to do a CPS-transform. A compiled function containing N sleep() statements will have N+1 possible continuations: One for each sleep() statement (for resuming after sleep) plus one for the final result, i.e. the nominal result of the function. This idea has been around for about 40 years.

https://en.wikipedia.org/wiki/Continuation-passing_style

https://www.google.com/search?q=python+CPS-transform+%22sleep%22+OR+%22suspend%22

It is possible that for the kinds of programs you're writing you should structure them in asynchronous style, which is feasible in VPython and which gives you more control. You may have seen this example:

http://www.glowscript.org/#/user/GlowScriptDemos/folder/Examples/program/Bounce-Callbacks-VPython/edit

This seems to be related to the problem I am having. I tried to use the asynchronous style, similar to the example you posted on Sep 13, 2015 on Bounce-Callbacks, to make an interactive that restarts an animation possibly changing some parameters. I'm referring to lines 151 to 173 in my code here, if you are interested: http://www.glowscript.org/#/user/owendix/folder/Interactives/program/TripleElasticBallInteractive/edit
I found that every time I restarted with any of the buttons, the frequency argument in rate(frequency, move), was effectively increased. The value wasn't literally changed (I printed it), but it kept running faster and faster with each button push. My kluge was to literally reduce the variable I use for frequency to counteract this increase whenever a button was pressed (all three buttons had the same effect on the apparent rate, my changes occur in the button definitions at the bottom). After some trial, error, and hypothesis testing for how the frequency in rate changes each restart, I determined that the function to compensate for the increase delta-t = 1/frequency = m*(# of restarts)+b and based off of my empirical estimates of the apparent rate, it meant that every function call: frequency = frequency0/(#-of-restarts + 1). After many many restarts, now, there seems to be no slow-down or speed-up.

The only reason why I did this, though, was because I kept getting asynchronous errors using rate(frequency, wait) and a while loop in place of def move():...; move(). Though I got it to work once, the buttons didn't interrupt the while loop. If you have a more elegant suggestion than my kluge of adapting the rate, I would love the advice. I am new to callbacks, as of this morning. Also, please pardon the late intrusion. I am not sure of github etiquette.

No, your problem is not related to this issue. When you click Restart you start ANOTHER move, so now there are two moves with the same rate, which moves the balls twice as fast. The fix is easy. First, I think it would be cleaner code if you unindented the move function. You would still end Start_Sim() with move(). Create a global variable "stop = False", and in move(), before the rate statement, insert the following, to stop the repeated call to move():

if stop:
    stop = False # restore to original value before exiting move()
    return

and in Restart_Sim() and other event-bound functions insert "stop = True".

I noticed a (harmless) typo in move, in that you specify the_Rate instead of the_rate as a global. Your program does read the_rate despite it not being declared as a global, because it is only if you write into an external variable that you have to make it global; a global variable that is only read need not be described as global.

I don't understand why you experienced buttons not interrupting a while loop. There are lots of demo programs at glowscript.org where that works.

Thank you very much! Your solution works perfectly. I wasn't aware that
previous copies of move would continue to execute.

On Tuesday, September 27, 2016, BruceSherwood notifications@github.com
wrote:

No, your problem is not related to this issue. When you click Restart you
start ANOTHER move, so now there are two moves with the same rate, which
moves the balls twice as fast. The fix is easy. First, I think it would be
cleaner code if you unindented the move function. You would still end
Start_Sim() with move(). Create a global variable "stop = False", and in
move(), before the rate statement, insert the following, to stop the
repeated call to move():

if stop:
stop = False # restore to original value before exiting move()
return

and in Restart_Sim() and other event-bound functions insert "stop = True".

I noticed a (harmless) typo in move, in that you specify the_Rate instead
of the_rate as a global. Your program does read the_rate despite it not
being declared as a global, because it is only if you write into an
external variable that you have to make it global; a global variable that
is only read need not be described as global.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#38 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AG3VUTWR2qQF9HtnaqiRWNADfnwmYN2Cks5quUcFgaJpZM4F8RC4
.