NativeScript / ios-jsc

NativeScript for iOS using JavaScriptCore
http://docs.nativescript.org/runtimes/ios
Apache License 2.0
299 stars 59 forks source link

Microtasks in dismissed context EXC_BAD_ACCESS code1 #878

Open jhoopmann opened 6 years ago

jhoopmann commented 6 years ago

Promises in dedicated terminated threads traps exc_bad_access in microtask loop GlobalObject.mm cause of dismissed context or invalid function reference (?)

0x1018cef78 <+440>: bl 0x101fe4794 ; JSC::JSInternalPromise::then(JSC::ExecState*, JSC::JSFunction*, JSC::JSFunction*) 0x1018cef7c <+444>: ldr x8, [x19, x23] 0x1018cef80 <+448>: cbz x8, 0x1018cef8c ; <+460> 0x1018cef84 <+452>: ldr x0, [x8] 0x1018cef88 <+456>: b 0x1018cef90 ; <+464> 0x1018cef8c <+460>: mov x0, #0x0 0x1018cef90 <+464>: bl 0x10188e9dc ; NativeScript::GlobalObject::drainMicrotasks() -> 0x1018cef94 <+468>: ldr x20, sp, #0x8 0x1018cef98 <+472>: cbz x20, 0x1018cf098 ; <+728> 0x1018cef9c <+476>: mov x21, #-0x1000000000000

Is there any possibility to stop/remove/handle microtasks from stack which are created in dedicated but dismissed thread to prevent running into invalid ref trap?

Maybe context ref could be added to task and checked for validity before execution.

tdermendjiev commented 6 years ago

Hi @jhoopmann, Would you be able to provide a reproducible scenario of the issues you're having?

jhoopmann commented 6 years ago

Hi @tdermendjiev ,

i just instanced a new Worker doing 1 fetch call wrapped into Promise which resolving callback sends message to uithread. While fetching i'm going to terminate dedicated thread so microtask created within got invalid cause of dismissed parent context (Thread). ->run call on task from collection in drainMicrotasks() ends in exc code 1.

Happens everywhere i'm using Promises / Timeouts - Microtasks - in dedicated terminated threads.

ios-runtime 3.2.0. Tried newest, but there's no such fix / change in code.

jhoopmann commented 6 years ago

example: `private async test() { let fn = knownFolders.temp().path + '/' + 'test.js';

    console.log(fn);

    await (File.fromPath(fn)).writeText(
        `
    require('globals');

    var post = function(dat) {
        postMessage(dat);
    }

    onmessage = function(dat) {
        console.log('incoming' + dat.data.toString());

        try {
            (new Promise(
                function(
                    resolve,
                    reject
                ) {
                    setTimeout( /* or fetch or what ever microservice creating function */
                        function() {
                            resolve('resolved');                            
                        },
                        3000
                    );
                }
            )).then(
                function(dat) {
                    post(dat.toString());
                },
                function(err) {
                    post(err.toString());
                }
            );
        } catch(ex) {
            post(ex.toString());                
        }
    }
`
    );

    console.log('create worker instance...');

    let worker = new Worker(fn);
    worker.onmessage = function (msg) {
        console.log(JSON.stringify(msg));
    };
    worker.onerror = function(err) {
        console.log(err.toString());
    };

    console.log('posting message...');

    worker.postMessage(
        'rofl'
    );
    setTimeout(
        () => {
            console.log('terminating worker...');
            worker.terminate()

        },
        1000
    );
}`
jhoopmann commented 6 years ago

Solved by directly suspending worker thread in WorkerMessagingProxy::parentTerminateWorkerThread and clearing up instead waiting for parent loop to execute prepended task for stopping worker run loop.

Maybe not the best solution, but works if you really want to hard break up some operations.