-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Node-API migration #2305
Node-API migration #2305
Conversation
What is needed to get this over the edge? Or is this possibly complete and just needs reviews and approval? I'd love to have this working or test with it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not qualified to review this for c++ correctness but the logic doesn't seem to have changed. This is amazing.
You'll only need the napi package in the bindings package, you can leave the others alone.
I think we'll have to take a look at what prebuild can do to take advantage of napi too.
@GazHank what do you mean by
What's the error handling? Apologies for not reviewing this sooner, I was busy with work and life. I'm so happy to see this PR. |
So the poller is funny. I don't think that NAPI will give you anything you need to do the actual fs polling. However I at one time got assurances from the libuv team that they are ABI stable and will not change libuv's abi until their next major version. So we probably don't have to worry about until LIBUV 2.0 and just replace the nan stuff. |
Why does |
@reconbot, slightly off topic, have you considered adding coverage tests for the C/C++ sources? |
@cinderblock he's still working on this it's a work in progress. I have a suite of integration tests that work against actual hardware. I don't have coverage stats of the c++ however. |
@reconbot no worries on the timing, I feel you on the "other commitments"; I hope to be able to get some quality time on this again in the next few weeks, if I can juggle those other pesky priorities :-) @cinderblock, et al I'll try to address the questions and discussions in the chain, but am away from my dev machine at the moment, so appologies if some of the items will need follow up:
My main interest in getting N-API experts to help with the review is to make sure I'm not reinventing the wheel on any of this stuff :-D Please feel free to query, comment and suggest changes, they are very welcome |
@reconbot Are the GitHub Actions running on hardware that has a serial port to test against? Assuming yes, or even if not, the tests that are run here seem to run against all the target platforms. If |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello from the Node-API team! This is a great start to a migration. Please see my below comments. The main concern I see is unchecked used of napi_*
calls: when calling into the engine, you need to validate the status of napi_ok
before making another call. The node-addon-api library does this under the hood, so if you can use the library instead of the the underlying napi_*
calls, then you wouldn't need to add those checks yourself. This would mean using an AsyncWorker instead of napi_async_work
. Let us know if you have any questions!
Thanks @KevinEady that's super helpful, I'll try to action those changes (I think the napi_ change will be in quite a few places, so will look to update those all at once if I can). I'm still to post the migrated poller.c and poller.h as following the use of the migration tool they still need a lot of work. Would you be able to take a quick look at those file and let me know if there are any previous migrations of projects using similar functionality? |
@GazHank i think logical changes in commits and keep everything on one branch. Is ok 👍 this can be a big pr, it's hard to do this piecemeal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi everybody,
this is a good start for the migration to Node-API. like suggested by @KevinEady it could be a good idea to use Napi::AsyncWorker
instead of the C API.
For example the following structure:
struct VoidBaton {//: public Napi::AsyncResource {
VoidBaton() : // AsyncResource("node-serialport:VoidBaton"),
errorString() {}
int fd = 0;
Napi::FunctionReference callback;
napi_async_work work;
char errorString[ERROR_STRING_SIZE];
};
could became an Napi::AsyncWorker
:
struct VoidBaton : public Napi::AsyncWorker {
VoidBaton(Napi::Function& callback, int fd) : Napi::AsyncWorker(callback, "your resource name"), fd(fd)
errorString() {}
int fd = 0;
char errorString[ERROR_STRING_SIZE];
};
Thrn you should to implement the Napi::AsyncWorker::Execute
method to do the task you need and handle the result ny mtehods Napi::AyncWorker::OnOK
and Napi::AsyncWorker::OnError
.
Thank you all for the comments and feedback, it is very greatly appreciated. I've incorporated most of the feedback so far, and hope to get some time tomorrow to look into the switch to Napi::AsyncWorker as this seems to be a potentially larger and more significant change. |
@@ -76,7 +76,7 @@ int ToDataBitsConstant(int dataBits) { | |||
return -1; | |||
} | |||
|
|||
void EIO_Open(napi_env env, void* req) { | |||
void EIO_Open(void* req) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
env was never used, and I believe it should not be used within the Execute method of the AsyncWorker
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly. It must not be used from Execute
.
@@ -56,7 +56,7 @@ void AsyncCloseCallback(uv_handle_t* handle) { | |||
delete async; | |||
} | |||
|
|||
void EIO_Open(napi_env env, void* req) { | |||
void EIO_Open(void* req) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
env was never used, and I believe it should not be used within the Execute method of the AsyncWorker
packages/bindings/src/serialport.h
Outdated
EIO_Open(this); | ||
} | ||
|
||
void OnError(Napi::Error const &error) override { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic from The EIO_AfterOpen method
packages/bindings/src/serialport.cpp
Outdated
return env.Undefined(); | ||
} | ||
|
||
void EIO_AfterOpen(napi_env n_env, napi_status status, void* req) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic transfererd to the OnError and OnOK within the struct
Hi @KevinEady & @NickNaso I've tried to convert OpenBaton to use AsyncWorker per your suggestions, so would be very greatful if you could take a look at those changes; I think it's probably sensible to include any further revisions to that before I roll out similar changes to the other Structs and Methods. The changes can be found in d042c04 I've added some comments to the commit, but to summarise:
The changes to the Open method are:
This change seems to work based on initial testing, but I've not tried to invoke error conditions, so am not sure if the override of OnError is correct, or the best way to handle things... |
|
Thanks @NickNaso I assume this is preferable to simply including this logic in the OnOK call? |
Yes you are right. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty close 👍
packages/bindings/src/serialport.cpp
Outdated
|
||
uv_queue_work(uv_default_loop(), req, EIO_Update, (uv_after_work_cb)EIO_AfterUpdate); | ||
napi_value resource_name; | ||
napi_create_string_utf8(env, "Update", NAPI_AUTO_LENGTH, &resource_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
napi_create_string_utf8(env, "Update", NAPI_AUTO_LENGTH, &resource_name); | |
NAPI_THROW_IF_FAILED( | |
napi_create_string_utf8(env, "Update", NAPI_AUTO_LENGTH, &resource_name), | |
env.Undefined()); |
Similarly for all other cases where the return value of Node-API calls is being ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, it may be more complicated if a resource has to be torn down before returning, but the pattern is that any and all Node-API calls may fail, and it's a good idea to handle the failure.
packages/bindings/src/serialport.cpp
Outdated
uv_queue_work(uv_default_loop(), req, EIO_Set, (uv_after_work_cb)EIO_AfterSet); | ||
|
||
napi_value resource_name; | ||
napi_create_string_utf8(env, "Set", NAPI_AUTO_LENGTH, &resource_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern repeats for all async work. It may be a good idea to factor it out.
@@ -3,49 +3,48 @@ | |||
|
|||
// Workaround for electron 11 abi issue https://github.com/serialport/node-serialport/issues/2191 | |||
#include <node_version.h> | |||
#if CHECK_NODE_MODULE_VERSION && NODE_MODULE_VERSION == 85 | |||
#if CHECK_NODE_API_MODULE_VERSION && NODE_API_MODULE_VERSION == 85 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will tie the addon to the version of V8 it's built against, rendering it non-ABI stable. Perhaps a runtime check of the arguments might be a better approach. Like,
binding.testArgOrder(1, 2);
and then
Napi::Value TestArgOrder(const Napi::CallbackInfo &args) {
if (args[0].As<Napi::Number>() == 2) global_js_args_order_reversed = true;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @gabrielschulhof I've added this to the TODO list for now, while focussing on broken functionality, but have linked your comment so as to not lose this change.
@@ -76,7 +76,7 @@ int ToDataBitsConstant(int dataBits) { | |||
return -1; | |||
} | |||
|
|||
void EIO_Open(napi_env env, void* req) { | |||
void EIO_Open(void* req) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly. It must not be used from Execute
.
Thanks for all of the feedback! I've migrated all of the common functions to avoid native napi_ calls. The EIO_After functions have been converted to OnError and OnOK within the struct defintions. Instead of invoking the EIO functions within the Execute() function of the struct, I've directly converted the EIO functions into overrides of the Execute() functions. I think this is cleaner and simpler, but would be keen to get feedback on this (I'd be happy to revert this to the approach previously taken with setBaton if that is preferable). ps - I was wondering if Napi::AsyncProgressWorker might be a good fit for the read and write functions for windows, but havent had chance to look into that much as yet. |
Signed-off-by: Gareth Hancock <[email protected]>
Comment out OnError logic
mac errors, override Execute()
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
Signed-off-by: Gareth Hancock <[email protected]>
I rebased your branch and pushed it to https://github.com/serialport/node-serialport/compare/GazHank_napi - feel free to reset your branch to that or squash or we can make a pr from that - up to you |
Thanks @reconbot sorry I've been away for a while, hopefully work and travel will calm down a bit as we move towards the end of the year |
It would be great to have that beta release soon, it would allow me to implement the changes in our app and make some tests out in the field :) |
Yep, I'll be glad to push it into the Tabby nightly and get feedback. |
Migrate from NAN to N-API - this should allow an easier time working with electron and all versions of nodejs * Change dependency to napi * include napi * update shared functionality to NAPI * update unix methods to include napi_env * Convert OpenBaton to AsyncWorker * SetError within Execute() * Replace napi_create_async_work for common methods * remove redundant ByteSize Setting * fix typo * NODE_ADDON_API_ENABLE_MAYBE * optional - remove warnings * Remove OnError logic BREAKING CHANGE: This release switches to NAPI which changes how many binaries are released and will potentially break your build system
This has been released as This is a big day, this has been a major endeavor and a major need of the project for quite a while. I want to thank @GazHank for making it possible. ❤️ |
I'd like to extend a big thank you to everyone who has supported this; it's been wonderful to see the level of community engagement and contribution! |
Parity options do not work on windows, because the temporary string in Fix: diff --git a/node_modules/@serialport/bindings/src/serialport.cpp b/node_modules/@serialport/bindings/src/serialport.cpp
index c48e150..00a5f5a 100644
--- a/node_modules/@serialport/bindings/src/serialport.cpp
+++ b/node_modules/@serialport/bindings/src/serialport.cpp
@@ -269,7 +269,8 @@ Napi::Value Drain(const Napi::CallbackInfo& info) {
}
inline SerialPortParity ToParityEnum(const Napi::String& napistr) {
- const char* str = napistr.Utf8Value().c_str();
+ auto tmp = napistr.Utf8Value();
+ const char* str = tmp.c_str();
size_t count = strlen(str);
SerialPortParity parity = SERIALPORT_PARITY_NONE;
if (!strncasecmp(str, "none", count)) {
|
@Eugeny can you open that in a new issue? I don't want to loose it |
DRAFT PR - FOR REVIEW ONLY
This is an initial draft of the migration to NAPI, inaddition to the automated migration tool quite a lot of the migration has to be hand crafted. So far only the core / architecture agnostic functionality has been updated.
I have tried to keep all changes:
As such some enhancement, improvements or simplifications that we gain from switching to NAPI may not be present (e.g. revised error handling)
Changes:
SerialPort.cpp
Serialport.h
Replace API version check with ABI stable runtime checkChange structs:
Serialport_win
poller
darwin_list
binding.gyp
Please note: This should now be ready for testing. Please let me know if you encounter any issues or unexpected behaviour