Skip to content

Instantly share code, notes, and snippets.

@apaprocki
Created October 12, 2012 15:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save apaprocki/3879707 to your computer and use it in GitHub Desktop.
Save apaprocki/3879707 to your computer and use it in GitHub Desktop.
V8::TerminateException with TryCatch
#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <v8.h>
using namespace v8;
static void *run(void *ptr) {
// Terminate the execution on the main thread in one second.
sleep(1);
V8::TerminateExecution();
pthread_exit(0);
}
int main(void) {
pthread_t thread;
HandleScope scope;
Persistent<Context> context = Context::New();
Context::Scope contextscope(context);
Local<String> source = String::New("while(true) {}");
Local<Script> script = Script::Compile(source);
// Catch any exceptions, kick off thread, and execute infinite loop.
TryCatch trycatch;
pthread_create(&thread, 0, &run, 0);
Local<Value> result = script->Run();
// Result will be that execution is *not* terminating and an
// exception has been caught.
std::cout << "IsExecutionTerminating "
<< V8::IsExecutionTerminating() << std::endl;
std::cout << "TryCatch.HasCaught() "
<< trycatch.HasCaught() << std::endl;
if (trycatch.HasCaught()) {
// Inspection of the exception is that it is simply null.
// There is no real way to tell that execution terminated,
// unless you implicitly check for null and empty message/stack??
std::cout << "TryCatch.Exception->IsNull() " <<
trycatch.Exception()->IsNull() << std::endl;
std::cout << "TryCatch.Message.IsEmpty() " <<
trycatch.Message().IsEmpty() << std::endl;
std::cout << "TryCatch.StackTrace.IsEmpty() " <<
trycatch.StackTrace().IsEmpty() << std::endl;
}
context.Dispose();
pthread_join(thread, 0);
return 0;
}
// Output:
// IsExecutionTerminating 0
// TryCatch.HasCaught() 1
// TryCatch.Exception->IsNull() 1
// TryCatch.Message.IsEmpty() 1
// TryCatch.StackTrace.IsEmpty() 1
@mraleph
Copy link

mraleph commented Oct 12, 2012

I think this is exactly how it is intended to work.

IsExecutionTerminating returns true if and only if there are still JavaScript frames above that has to be terminated. It becomes false once it discards the last JS frame.

HasCaught returns true (CanContinue should return false) because it caught terminate execution exception. However such exception has no meaningful JS object representation so we stash null into Exception() and leave message empty.

@apaprocki
Copy link
Author

Ok. When this happens, and there are JS frames still on the stack, are you allowed to ThrowException if you desire to actually translate it into an exception that can be caught? Or can you only do that after the last JS frame has handled the termination and IsExecutionTerminating returns false? Based on the language in the header, I'm guessing you can't do this:

{
   TryCatch try_catch;
   script->Run();
   // execution was terminated
}

if (V8::IsExecutionTerminating()) {
    return ThrowExeception(Exception::Error(String::New("Execution terminated")));
}

When I try the above, an exception is thrown, but the exception value IsUndefined(). (This behavior seems strange, but I guess is acceptable if it is undefined behavior to call ThrowException when IsExecutionTerminating.)

I'm trying to do all this because I want vm.runInThisContext(script, name, timeout) to be able to throw to the caller if the timeout expires. But because there are still Node JS frames in the stack when the script is terminated, there is no way to recover at the runInThisContext boundary and throw something at that exact point.

I guess I'm looking for some kind of checkpoint concept, where TerminateExecution will only terminate up until the first checkpoint it finds and then resumes normally, only terminating fully up the stack if no such checkpoint exists.

@mraleph
Copy link

mraleph commented Oct 12, 2012

I don't think you can overwrite exception with your own or stop unwinding the stack while there are still JS frames alive... at least this does not seem like an intended variant of usage for this API at the moment.

You should file a bug against V8 and describe your pattern to V8 team so that they might decide to extend functionality to accomodate it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment