Skip to content
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

C++ Addon - How to check if running in the main thread or not from a Node.js native addon? #3356

Closed
JCMais opened this issue May 6, 2021 · 6 comments
Labels

Comments

@JCMais
Copy link

JCMais commented May 6, 2021

  • Node.js Version: v16
  • OS: Unix / Windows
  • Scope (install, code, runtime, meta, other?): code, runtime

This is somewhat relevant to #1886

I have an addon that needs to call a cleanup function only in the main thread (libcurl cleanup), currently, the only ways to add a cleanup hook are using AtExit or AddEnvironmentCleanupHook, but those run for each thread, including the main one.

Is there any way for me to check if the code is running in the main thread or not?

I see that there is a is_main_thread() function available inside the Environment instance (added with Node.js v11): https://github.com/nodejs/node/blob/f607e169612ff60a00f456f3c698a6b41792a8f5/src/env-inl.h#L707-L709

However, the add-on code cannot access the Environment class itself, as it is not exposed by Node.js.

Theoretically, I could retrieve this by calling the Node.js worker_thread module directly from the addon, but this does not seem right, as it would add unnecessary context-switching.

@addaleax
Copy link
Member

addaleax commented May 6, 2021

@JCMais Can you provide a bit more context around what you are looking for exactly? It sounds a bit like you’re not necessarily looking for cleanup on the main-thread level, but for doing cleanup on a process level. In that case, normally I would suggest using static constructors/destructors; however, since you mentioned libcurl, it doesn’t seem like they’re recommending that:

https://curl.se/libcurl/c/curl_global_init.html:

If you are initializing libcurl from a Windows DLL you should not initialize it from DllMain or a static initializer because Windows holds the loader lock during that time and it could cause a deadlock.

https://curl.se/libcurl/c/curl_global_cleanup.html:

We recommend you do not run libcurl from any module that may be unloaded dynamically. This behavior may be addressed in the future.

I’m surprised to see libcurl being so restrictive, but their docs are basically telling you not to use it from an addon at all.

Practically speaking, I think you could probably add a counter that is shared between threads that keeps track of the # of times your addon has been loaded/unloaded and act accordingly? Something like this, basically:

class EnsureLibcurlInit {
 public:
  static void InitLibcurl() {
    if (counter++ == 0)
      curl_global_init();
  }

  static void CleanupLibcurl() {
    if (--counter == 0)
      curl_global_cleanup();
  }

 private:
  static std::atomic_uint32_t counter {0};
};

std::atomic_uint32_t EnsureLibcurlInit::counter {0};


NODE_MODULE_INIT(/* exports, module, context */) {
  Isolate* isolate = context->GetIsolate();

  EnsureLibcurlInit::InitLibcurl();
  AddEnvironmentCleanupHook(isolate, [](void*) {
    EnsureLibcurlInit::CleanupLibcurl();
  }, nullptr);
  
  // ... initialize JS bindings ...
}

@JCMais
Copy link
Author

JCMais commented May 6, 2021

Hi @addaleax , basically I'm trying to make my library (node-libcurl) work with the threading support now available in Node.js.

Right now curl_global_init is called directly from JS using a function exposed by the addon:
https://github.com/JCMais/node-libcurl/blob/1563bb7699eb9b22989058a0d018c59a460aaf37/lib/Curl.ts#L73-L75

So it is easy to make sure that this only runs in the main thread (by checking isMainThread).

Now about curl_global_cleanup,

We recommend you do not run libcurl from any module that may be unloaded dynamically. This behavior may be addressed in the future.

I think I missed that part of the documentation, or just ignored it when I first read it. In this case, it seems that it is not feasible to add support for worker threads at all. I will try to get in touch with the maintainers of libcurl and check if this is still the case.

In the meantime, ignoring the above point, curl_global_cleanup is currently called using the AtExit hook, which does not really work with worker threads (as each thread calls the AtExit callback, and thus curl_global_cleanup).

https://github.com/JCMais/node-libcurl/blob/b8d0fac0e820645088ad4ab68c790b24fb3d4d15/src/node_libcurl.cc#L22-L26

I will take a look at using a shared counter like you said, thank you for the suggestion!

@addaleax
Copy link
Member

addaleax commented May 6, 2021

So it is easy to make sure that this only runs in the main thread (by checking isMainThread).

That kind of begs the question, what happens if the first time the addon is loaded happens on a worker thread?

@JCMais
Copy link
Author

JCMais commented May 6, 2021

That kind of begs the question, what happens if the first time the addon is loaded happens on a worker thread?

Good thinking, in this case, it is necessary to require the addon or call globalInit manually in the main thread, as libcurl cannot be initialized outside of the main thread.

Copy link

github-actions bot commented May 9, 2024

It seems there has been no activity on this issue for a while, and it is being closed in 30 days. If you believe this issue should remain open, please leave a comment.
If you need further assistance or have questions, you can also search for similar issues on Stack Overflow.
Make sure to look at the README file for the most updated links.

@github-actions github-actions bot added the stale label May 9, 2024
Copy link

github-actions bot commented Jun 9, 2024

It seems there has been no activity on this issue for a while, and it is being closed. If you believe this issue should remain open, please leave a comment.
If you need further assistance or have questions, you can also search for similar issues on Stack Overflow.
Make sure to look at the README file for the most updated links.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jun 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants