Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronously calling a callback function in Spidermonkey JS engine

Using Spidermonkey v27:

What is the proper way to "retain" and then asynchronously call a temporary JS function from C++?

JS code:

myFunction(function(){
    console.log("The function works");
});

C++ code:

bool js_myFunction(JSContext* cx, uint32_t argc, jsval* vp)
{
    if (argc == 1)
    {
        implement_async_function(cx, vp);
        JS_SET_RVAL(cx, vp, JSVAL_NULL);
        return true;
    }
    return false;
}

static JSContext* savedContext;
static jsval* savedVal;
void implement_async_function(JSContext* cx, jsval* vp)
{
    // if the function is called immediately, everything is okay!
    jsval retVal;
    JS_CallFunctionValue(cx, nullptr, *vp, 0, nullptr, &retVal);
    // "The function works"

    // if some work has to be done before calling the callback...
    savedContext = cx;
    savedVal = vp;
    start_async_process();
}

void async_process_complete()
{
    jsval retVal;
    JS_CallFunctionValue(savedContext, nullptr, *savedVal, 0, nullptr, &retVal);
    // "<no filename="filename">:0:true is not a function"
    // or else it crashes...
}

void alternate_implement_async_function(JSContext* cx, jsval* vp)
{
    // also tried this:
    savedContext = cx;
    savedVal = vp;
    JS_AddValueRoot(savedContext, savedVal);
    start_async_process();

    // and this:
    savedContext = cx;
    savedVal = new jsval(*vp);
    JS_AddValueRoot(savedContext, savedVal);
    start_async_process();

    // and using JS::RootedValue

    // and using JS::Heap<JS::Value>

    // and using the global context instead of the saved context
}

I've read through SpiderMonkey's documents:

https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_Reference/JS::Value

https://developer.mozilla.org/en-US/docs/SpiderMonkey/GC_Rooting_Guide

https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS_AddRoot

https://developer.mozilla.org/en-US/docs/SpiderMonkey/JSAPI_Reference/JS_CallFunctionValue

And checked this StackOverflow post Spidermonkey and Garbage Collection

And tried even making the JS callback function a global function which should never be garbage collected. (This is not what I want though.)

like image 703
Nathanael Weiss Avatar asked Nov 10 '22 10:11

Nathanael Weiss


1 Answers

You didn't mention the behavior of your program when trying to call the function asynchronously, but I assume that it would seg fault when trying to call the function since the GC might have moved it. You need a way to store the function object (it's not a value as in your code). JS::Rooted is not acceptable in this situation since stack rooting expects gc things to root in a LIFO ordering. Thus, you need to store it in a JS::Heap<JSObject*> handle. The JS::Heap handle expects to live on the heap instead of the stack (maybe in a list or tree structure). The caveat with JS::Heap handles is that they need to be traced by the GC so the underlying objects can be relocated and your handle updated. Tracing is discussed in this question.

Assuming your object is now stored properly and traced, you should be able to call it at any time without any issue.

like image 173
jwilm Avatar answered Nov 14 '22 22:11

jwilm