Interrupting Infinite Loops

256 views
Skip to first unread message

Sphaerica

unread,
Dec 22, 2011, 9:55:32 AM12/22/11
to Skulpt
Is there a way to interrupt a program, in case the Python code goes
into an infinite loop? If so, what is the key combination?

Sphaerica

unread,
Dec 22, 2011, 2:52:40 PM12/22/11
to Skulpt
Okay... this is way harder than I thought it would be.

From what I can see looking at the code, the answer to my question is
"no, once a Skulpt program starts, it controls the universe."

I made a pretty simple change, by modifying the four
Compiler.prototype._jump routines to also add the line:

::: out("if(Sk.userSkInterrupt != null){/*user interrupt*/
console.log('Skulpt Python Program interrupted by user
request.');throw new Error('Program interrupted by user request.')}");

I added a way to set Sk.userSkInterrupt to a value by clicking an
"Interrupt" button on my page.

It didn't work. I'm running Mac OS X, and tried Firefox and Safari.
It actually messed up my OS, which is dang hard to do on a Mac. If I
put an infinite loop in, it seems that javascript wasn't surrendering
any CPU time at all, so the browser wouldn't even respond to the
button click. Safari would let me close the window, but it hung all
of Firefox, requiring a "Force Quit."

I tried a simpler alternative... I added the following to the web
page, just before the eval call to run the compiled Python-Javascript:

::: setTimeout('Sk.userSkInterrupt=1;alert("interrupt issued")',
1000);

In theory, this should have set the interrupt flag after a second and
halted the infinite loop using the _jump code above. It didn't.

So it seems that Javascript (or maybe the eval function specifically?)
is so badly behaved that it won't even yield to a Javascript timeout.

The only real answer here is not doable... to rewrite the compiler to
generate the code in chunks that periodically terminate and then
resume after a brief pause using a javascript setTimeout. This means
a total rewrite of how everything is done, so that's not happening.

The poor man's alternative would be to allow the user to configure an
"infinite loop check" time into Skulpt (Sk.timeout), and change the
Compiler.prototype._jump statements (or maybe better yet the
individual potential looping constructs, like "while" and "for") to
check if the system time has exceeded the loop-check time, and if so
to perform some action (at a minimum throw an exception to terminate
the program, or maybe issue an alert asking the user if they want to
terminate, and if so throw the interrupt exception). If I get clever,
I could make the action configurable, perhaps through Sk.configure.

If you have any ideas I'm all ears. Feel free to e-mail me if you
care to.

I do have one question: For irrelevant reasons I tried to change the
script that runs the code in page to break it up, so that:

eval(Sk.importMainWithBody("<stdin>", false,
mainEditor.getCode()));

became

var mycode = (Sk.importMainWithBody("<stdin>", false,
mainEditor.getCode()));
eval(mycode);

Surprisingly, this executed the code twice, as if the second eval
statement were superfluous. What's going on there?

-- Bob

Sphaerica

unread,
Dec 22, 2011, 4:22:09 PM12/22/11
to Skulpt
Okay. I did this. Let me know if you want the code.

To import.js, in the _importModuleInternal function, I changed the
following line to set the exec start time as the very first step when
the program begins:

var finalcode = 'Sk.execStart = new Date();\n' + co.code;

In compile.js, I added this function:

Compiler.prototype._interruptTest = function() { // Added by RNL
out("if (Sk.execStart === undefined) {Sk.execStart=new Date()}");
out("if (Sk.execLimit != null && new Date() - Sk.execStart >
Sk.execLimit) {console.log('Skulpt Python Program exceeded run time
limit.');throw new Error('Program exceeded run time limit.')}");
}

It sets Sk.execStart in case for some reason it hasn't been set, and
then it checks the run time.

For now, I put calls to _interruptTest into all of the _jump
functions, but later I'll move it just to the loops. It might also be
quickened by doing some optimizing, but overall I'm not concerned
about performance nearly as much as hanging people's browsers, so this
works for me.

If Sk.execLimit is not set it does nothing.

If Sk.execLimit is set, then Skulpt checks the number of milliseconds
the program has been running against that value and terminates with an
exception if that time limit is exceeded.

Let me know if you have any questions.

Dave Pritchard

unread,
Apr 17, 2012, 11:44:46 AM4/17/12
to sku...@googlegroups.com
I'm interested in playing around with this code eventually. I'd also like to add the ability to limit the total number of steps a program can take, and the memory that it uses... basically to mimic things you would normally do with setrlimit. If we can get all of that working then I could use it in place of server-side safeexec'd for this project I am involved with:

- Dave
Reply all
Reply to author
Forward
0 new messages