node-serialport icon indicating copy to clipboard operation
node-serialport copied to clipboard

Serial port commnunication does not work inside a Worker Thread

Open hugo-a-garcia opened this issue 5 years ago • 12 comments

Summary of Problem

Opening a serial port from within a worker thread fails with:

Error: Module did not self-register.

Code to Reproduce the Issue

github repo: hugo-a-garcia/serial-worker

const {
   Worker, isMainThread, parentPort, workerData
} = require('worker_threads');

const SerialPort = require("serialport");
   const Readline = SerialPort.parsers.Readline;

if (isMainThread) {
   console.log("In main thread");

   const worker = new Worker(__filename);
   worker.on('message', msg => {
      console.log(msg);
   });
   worker.on('error', err => {
      console.error("Oops " + err)
   });
   worker.on('exit', (code) => {
      if (code !== 0)
         console.log(`Worker stopped with exit code ${code}`);
   });

} else {
   console.log("In worker thread");
 
   const port = new SerialPort("/dev/ttyACM0", {
       baudRate: 9600
   });
   
   const parser = new Readline
   port.pipe(parser);
   parser.on("data", (data) => console.log(data));
   
   // port.on('readable', function () {
   //     console.log(port.read().toString());
   // });
   
   port.on('error', function (err) {
       console.log('Error: ', err.message);
   });

   parentPort.postMessage("Done.");
}

Versions, Operating System and Hardware

  • SerialPort@? 7.1.5
  • Node.js v? v12.9.1
  • Linux
  • Hardware and chipset? Arduino Uno connected to laptop

Possible related issue: N-API support for node-serialport #1186

hugo-a-garcia avatar Sep 24 '19 08:09 hugo-a-garcia

I don't know if this would be realted to N-API. I do think you might have found a bug in workers however. =)

reconbot avatar Sep 25 '19 03:09 reconbot

Ok. I submitted a bug report at node:

https://github.com/nodejs/node/issues/29698

hugo-a-garcia avatar Sep 25 '19 09:09 hugo-a-garcia

Not a bug in Node.js. :) Quoting the Worker threads documentation:

Native add-ons can only be loaded from multiple threads if they fulfill certain conditions.

In short, your addon needs to be declared as context-aware and support multiple threads natively. For N-API, it’s assumed that that’s the case.

Just taking a quick look at the source code here, at least the usage of uv_default_loop() is problematic and will mean that the code won’t work on worker threads as-is anyway.

addaleax avatar Sep 25 '19 10:09 addaleax

Thanks @addaleax, my knowledge around figuring out how to fix this is not great. There’s no reason why we need the default event loop. And there’s no shared context between serial ports so they could in theory work in workers. I imagine the only thing we’d need to do in the cleanup hook is close the port if it’s open. (Open state is normally kept in JS)

Any pointers would be much appreciated. Thanks! 🙏

reconbot avatar Sep 25 '19 11:09 reconbot

@reconbot You can probably use Node’s GetCurrentEventLoop() and AddEnvironmentCleanupHook()/RemoveEnvironmentCleanupHook() functions to achieve what you need – I don’t think there are NAN wrappers for them, though.

addaleax avatar Sep 25 '19 11:09 addaleax

Got to start moving to NAPI anyway 🤷‍♂️

reconbot avatar Sep 26 '19 02:09 reconbot

Really tried to understand what has to be done to implement the cleanupHooks, but actually I'm lost in all this c++. This is not my domain :) I now forward all calls from workers to serial port running in the main thread using the message channel. This is pretty ugly but I'm fine with it at the moment O:-)

meisterlampe avatar Nov 13 '19 11:11 meisterlampe

Hello. Sad news for me. Everything works if I don't use worker_threads (test.js)

const SerialPort = require('serialport')
const port = new SerialPort('/dev/tty.usbserial-A7TRF2V', { baudRate: 9600 })
port.on('data', (data) => { console.log(data) })

But if I run the same code through worker_threads I get a very sad message (main.js)

const {  Worker } = require('worker_threads')
const worker = new Worker('./test.js', {})
worker.on('message', () => { })
bash-3.2$ node ./main.js 
Segmentation fault: 11

In some ways, I found out the essence of the error

FATAL ERROR: v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope
 1: 0x100c22043 node::Abort() (.cold.1) [/usr/local/bin/node]
 2: 0x10008710d node::FatalError(char const*, char const*) [/usr/local/bin/node]
 3: 0x100087276 node::OnFatalError(char const*, char const*) [/usr/local/bin/node]
 4: 0x10018e2fc v8::Utils::ReportApiFailure(char const*, char const*) [/usr/local/bin/node]
 5: 0x10028bd36 v8::internal::HandleScope::Extend(v8::internal::Isolate*) [/usr/local/bin/node]
 6: 0x100405e0c v8::internal::JSReceiver::GetCreationContext() [/usr/local/bin/node]
 7: 0x1001a0fef v8::Object::CreationContext() [/usr/local/bin/node]
 8: 0x100003e0b node::MakeCallback(v8::Isolate*, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) [/usr/local/bin/node]
 9: 0x10428d125 init(v8::Local<v8::Object>) [/Users/borisbobylev/vrack/devices/rs485/node_modules/@serialport/bindings/build/Release/bindings.node]
10: 0x10428a90b EIO_AfterOpen(uv_work_s*) [/Users/borisbobylev/vrack/devices/rs485/node_modules/@serialport/bindings/build/Release/bindings.node]
11: 0x10070f124 uv__work_done [/usr/local/bin/node]
12: 0x100712753 uv__async_io [/usr/local/bin/node]
13: 0x100722080 uv__io_poll [/usr/local/bin/node]
14: 0x100712b8a uv_run [/usr/local/bin/node]
15: 0x1000bdb79 node::NodeMainInstance::Run() [/usr/local/bin/node]
16: 0x100061cf0 node::Start(int, char**) [/usr/local/bin/node]
17: 0x7fff67a2dcc9 start [/usr/lib/system/libdyld.dylib]
Abort trap: 6

It would be great if there was someone who could fix it. It's bad that there is code that can kill the main process.

OS: MacOS Node: v14.6.0 Serialport: 9.0.0

Thanks you.

ponikrf avatar Aug 06 '20 08:08 ponikrf

@ponikrf and anyone hitting this, you can use tiny-worker, it appears to work fine with this and other native addons as a workaround until the worker_threads support is implemented

ValentineStone avatar May 14 '21 10:05 ValentineStone

Based on the discussion, it sounds like migration from NAN to N-API is a stepping stone to resolving this. Is that still understood to be the case?

I believe there was a previous endevour to migrate to N-API https://github.com/serialport/node-serialport/issues/1186

While that attempt was a while ago, I wonder what code or lessons can be gleaned from that previous work?

GazHank avatar Jun 24 '21 17:06 GazHank

Hi @hugo-a-garcia would you be able to take a look at PR https://github.com/serialport/node-serialport/pull/2305 and give it a try?

While that PR isn't directly trying to address this issue, I think it might be easier to fix this issue in the N-API version. As such if you are able to share info of any errors you find, or perhaps a sample project you are trying to get to work then it might help fix this issue in the next major version of the package :-)

GazHank avatar Sep 29 '21 18:09 GazHank

I am encountering this in a different situation. The callstack seems different as well than what was posted here previously. On the other hand I am not trying to use any worker threads in code that uses node serialport.

My setup is something like this:

  1. rust napi native module that uses serial ports
  2. testing with ava the module
  3. spawning socat for emulating serial port socat PTY,link=/dev/ttyS10,echo=1 PTY,link=/dev/ttyS11,echo=0
  4. the rust addon connects to ttyS10 and writes to it
  5. the test code connects with node serialport to ttyS11
  6. shortly after when I attempt to read in ava from ttyS11 I get this error message
FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place
 1: 0xb02930 node::Abort() [node]
 2: 0xa18149 node::FatalError(char const*, char const*) [node]
 3: 0xcdceaa v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: 0xcde40c v8::HandleScope::HandleScope(v8::Isolate*) [node]
 5: 0xab7a86 napi_open_handle_scope [node]
 6: 0x7f24abc1793b Poller::onData(uv_poll_s*, int, int) [/app/node_modules/@serialport/bindings-cpp/prebuilds/linux-x64/node.napi.glibc.node]

Now the rust napi native module that I am attempting to test does use threads, so I think the two somehow conflict, but I hardly grasp how.

archfz avatar Aug 22 '22 22:08 archfz

Hello guys, so it is working now?

dj-fiorex avatar Mar 03 '23 22:03 dj-fiorex

Doesn't look like it. I'm getting the same error:

npx ts-node src/index.ts 
Worker Received: test
Serial baud rate: 115200
Record duration: 60
writing: ATZ

readResponse Timeout after 5 secs.
Error: Unable to reset factory default
writing: ATE1

FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place
 1: 0xb7a940 node::Abort() [node]
 2: 0xa8e72f node::FatalError(char const*, char const*) [node]
 3: 0xd5c6aa v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: 0xd5dc0c v8::HandleScope::HandleScope(v8::Isolate*) [node]
 5: 0xb28b6a napi_open_handle_scope [node]
 6: 0x7f95a42185eb Poller::onData(uv_poll_s*, int, int) [/home/foureight84/projects/node-callattendant/node_modules/@serialport/bindings-cpp/prebuilds/linux-x64/node.napi.glibc.node]
 7: 0x16710a4  [node]
 8: 0x165f4ce uv_run [node]
 9: 0xabda6d node::SpinEventLoop(node::Environment*) [node]
10: 0xbc1164 node::NodeMainInstance::Run() [node]
11: 0xb35bc8 node::LoadSnapshotDataAndRun(node::SnapshotData const**, node::InitializationResult const*) [node]
12: 0xb3976f node::Start(int, char**) [node]
13: 0x7f95a796718a  [/lib/x86_64-linux-gnu/libc.so.6]
14: 0x7f95a7967245 __libc_start_main [/lib/x86_64-linux-gnu/libc.so.6]
15: 0xabbdee _start [node]
Aborted

foureight84 avatar Jul 06 '23 17:07 foureight84

Interested in this

ScreamZ avatar Nov 15 '23 23:11 ScreamZ

We landed napi support and support workers as of 2 years ago.

reconbot avatar Nov 16 '23 12:11 reconbot

This still isn't working for me. I get the same error as this:

Doesn't look like it. I'm getting the same error:

npx ts-node src/index.ts 
Worker Received: test
Serial baud rate: 115200
Record duration: 60
writing: ATZ

readResponse Timeout after 5 secs.
Error: Unable to reset factory default
writing: ATE1

FATAL ERROR: HandleScope::HandleScope Entering the V8 API without proper locking in place
 1: 0xb7a940 node::Abort() [node]
 2: 0xa8e72f node::FatalError(char const*, char const*) [node]
 3: 0xd5c6aa v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: 0xd5dc0c v8::HandleScope::HandleScope(v8::Isolate*) [node]
 5: 0xb28b6a napi_open_handle_scope [node]
 6: 0x7f95a42185eb Poller::onData(uv_poll_s*, int, int) [/home/foureight84/projects/node-callattendant/node_modules/@serialport/bindings-cpp/prebuilds/linux-x64/node.napi.glibc.node]
 7: 0x16710a4  [node]
 8: 0x165f4ce uv_run [node]
 9: 0xabda6d node::SpinEventLoop(node::Environment*) [node]
10: 0xbc1164 node::NodeMainInstance::Run() [node]
11: 0xb35bc8 node::LoadSnapshotDataAndRun(node::SnapshotData const**, node::InitializationResult const*) [node]
12: 0xb3976f node::Start(int, char**) [node]
13: 0x7f95a796718a  [/lib/x86_64-linux-gnu/libc.so.6]
14: 0x7f95a7967245 __libc_start_main [/lib/x86_64-linux-gnu/libc.so.6]
15: 0xabbdee _start [node]
Aborted

jsharf avatar Apr 04 '24 01:04 jsharf