threads.js icon indicating copy to clipboard operation
threads.js copied to clipboard

FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place

Open ashishchandr70 opened this issue 4 years ago • 7 comments

Hi,

I am using threads.js for a multi-player game. The main thread spawns two separate workers who each execute the same function.

The two worker threads go ahead and do what they need to do and I await a websocket message to resolve a promise which indicates that the processing is complete.

However, when I call Thread.terminate, I immediately get the following error:

FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place
 1: 0xa18150 node::Abort() [node]
 2: 0xa1855c node::OnFatalError(char const*, char const*) [node]
 3: 0xb9638a v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: 0xb9792e v8::HandleScope::HandleScope(v8::Isolate*) [node]
 5: 0xacd89e node::worker::Worker::JoinThread() [node]
 6: 0x9baed0 node::Environment::stop_sub_worker_contexts() [node]
 7: 0x9bafe0 node::Environment::Stop() [node]
 8: 0x9e5e79 node::Stop(node::Environment*) [node]
 9: 0xad2926 node::worker::Worker::StopThread(v8::FunctionCallbackInfo<v8::Value> const&) [node]
10: 0xc02529  [node]
11: 0xc04317 v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node]
12: 0x1409e59  [node]
Aborted (core dumped)
npm ERR! Test failed.  See above for more details.
Waiting for the debugger to disconnect...

Here is my code from the master (main thread):

try {
            let battleMessage = await new Promise(async (resolve, reject) => {
                const ws = new WSClient(WebSocket, `${mockurl.replace('http', 'ws')}/ws`, ['BattleCompleted', 'BattleCreated']);

                ws.on('msg', async (message) => {
                    console.log(`WS Message:`, message);
                    if (message.subject === 'BattleCompleted') {
                       resolve(message);
                    }
                });

                playerThread1 = await spawn(new Worker(`../scripts/player-setup`));
                playerThread2 = await spawn(new Worker(`../scripts/player-setup`));

                Thread.events(playerThread1).subscribe(event => {
                    console.log(`playerThread1 event: ${JSON.stringify(event,null,4)}`);
                    switch (event.type) {
                    case 'message':
                        if (event.data.type === 'result' && event.data.complete === true) {
                            console.log(`playerThread1 has completed processing`);
                        }
                        else if(event.data.type === 'uncaughtError') console.error(`Error from playerThread1: ${JSON.stringify(event.data.error,null,4)}`);
                        break;
                    default:
                        break;
    
    
                    }
                });
                Thread.events(playerThread2).subscribe(event => {
                    console.log(`playerThread2 event: ${JSON.stringify(event,null,4)}`);
                    switch (event.type) {
                    case 'message':
                        if (event.data.type === 'result' && event.data.complete === true) {
                            console.log(`playerThread2 has completed processing`);
                        }
                        else if(event.data.type === 'uncaughtError') console.error(`Error from playerThread2: ${JSON.stringify(event.data.error,null,4)}`);
                        break;
                    default:
                        break;
    
    
                    }
                }); 
                
                let player1 = await playerThread1.executePvPBattle(Loaded.location, { playerName: 'SuperElf' });
                let player2 = await playerThread2.executePvPBattle(Loaded.location, {
                    playerName: 'Frodo'
                });                
            });

            let winner = JSON.parse(battleMessage.payload);
            console.log(`Winner: ${winner}`);

        }
        catch (e) {
            console.error(`Error received ${e}`);
        }
        finally {
            await Thread.terminate(playerThread1);
            await Thread.terminate(playerThread2);
        }

Everything works normally and I get the console.log for the winner. However, the moment it tries to execute await Thread.terminate(playerThread1); I get the V8 API error.

ashishchandr70 avatar Jan 10 '21 02:01 ashishchandr70

Hey @ashishchandr70, thanks for reporting!

What version of node.js and threads.js are you running?

andywer avatar Jan 10 '21 21:01 andywer

Hi @andywer Thanks for getting back on a Sunday!

Node: v12.20.0 Threads: 1.6.3

ashishchandr70 avatar Jan 10 '21 21:01 ashishchandr70

I get the same error

node 14.15.4

FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place
 1: 0xa04200 node::Abort() [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 2: 0x94e4e9 node::FatalError(char const*, char const*) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 3: 0xb794da v8::Utils::ReportApiFailure(char const*, char const*) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 4: 0xb7ab6c v8::HandleScope::HandleScope(v8::Isolate*) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 5: 0x96a439 node::InternalCallbackScope::InternalCallbackScope(node::Environment*, v8::Local<v8::Object>, node::async_context const&, int) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 6: 0x96b172 node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 7: 0x96b51c node::MakeCallback(v8::Isolate*, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
 8: 0x7f15c6890903  [/media/workspace/drivenow-frontend/node_modules/grpc/src/node/extension_binary/node-v83-linux-x64-glibc/grpc_node.node]
 9: 0x7f15c689b3f3  [/media/workspace/drivenow-frontend/node_modules/grpc/src/node/extension_binary/node-v83-linux-x64-glibc/grpc_node.node]
10: 0x13838b9  [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
11: 0x137c5d8 uv_run [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
12: 0xa44974 node::NodeMainInstance::Run() [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
13: 0x9d1e15 node::Start(int, char**) [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
14: 0x7f15cdb910b3 __libc_start_main [/lib/x86_64-linux-gnu/libc.so.6]
15: 0x9694cc  [/home/cameronbraid/.nvm/versions/node/v14.15.4/bin/node]
Aborted (core dumped)

node 12.18.2

FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place
 1: 0xa0bb60 node::Abort() [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
 2: 0xa0bf6c node::OnFatalError(char const*, char const*) [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
 3: 0xb81eca v8::Utils::ReportApiFailure(char const*, char const*) [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
 4: 0xb8346e v8::HandleScope::HandleScope(v8::Isolate*) [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
 5: 0x97a9ee node::InternalCallbackScope::InternalCallbackScope(node::Environment*, v8::Local<v8::Object>, node::async_context const&, int) [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
 6: 0x97c065 node::MakeCallback(v8::Isolate*, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
 7: 0x7fa1b18798a3  [/media/workspace/drivenow-frontend/node_modules/grpc/src/node/extension_binary/node-v72-linux-x64-glibc/grpc_node.node]
 8: 0x7fa1b1884213  [/media/workspace/drivenow-frontend/node_modules/grpc/src/node/extension_binary/node-v72-linux-x64-glibc/grpc_node.node]
 9: 0x133b24c  [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
10: 0x1333dd8 uv_run [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
11: 0xa4ecf5 node::NodeMainInstance::Run() [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
12: 0x9dcc68 node::Start(int, char**) [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
13: 0x7fa1b48180b3 __libc_start_main [/lib/x86_64-linux-gnu/libc.so.6]
14: 0x979215  [/home/cameronbraid/.nvm/versions/node/v12.18.2/bin/node]
Aborted (core dumped)

cameronbraid avatar Feb 04 '21 06:02 cameronbraid

Looks to me as if the native code in your grpc package is not comfortable being run in a worker thread.

Forcing the use of tiny-worker instead of worker threads might solve it, but unfortunately #290 is not merged yet…

andywer avatar Feb 04 '21 08:02 andywer

I tried a proof of concept with worker_threads directly and got the same error, so tiny may be the route to go.

Is there any way to force tiny to be used, like somehow making the worker_threads module not resolve ?

cameronbraid avatar Feb 05 '21 01:02 cameronbraid

like somehow making the worker_threads module not resolve ?

Would be pretty hacky, but making the import from worker_threads throw would probably work if tiny-worker is installed.

andywer avatar Feb 06 '21 16:02 andywer

I had a go ad trying to alias the module with babel, didn't get it working in the end.

I'll just wait for that PR to land

thanks

Cameron

cameronbraid avatar Feb 07 '21 06:02 cameronbraid

Closing this as all associated PRs have been addressed.

ashishchandr70 avatar Mar 01 '23 18:03 ashishchandr70