-
Notifications
You must be signed in to change notification settings - Fork 9
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
Make global variables thread-safe in the extension #19
base: master
Are you sure you want to change the base?
Make global variables thread-safe in the extension #19
Conversation
/* note that ICC (linux) and Clang are covered by __GNUC__ */ | ||
# define RB_THREAD_LOCAL_SPECIFIER __thread | ||
# endif | ||
#endif |
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 logic is borrowed from the thread_pthread.h
file. On Ruby master it was removed in ruby/ruby@f99af43 and RB_THREAD_LOCAL_SPECIFIER
is declared instead.
So on Ruby 3.4 we rely on the available out of the box RB_THREAD_LOCAL_SPECIFIER
and on earlier versions the previous logic is used. This way thread-local declarations should work on both existing Ruby versions and Ruby 3.4.
fef87d6
to
4cecd26
Compare
#ifdef EASYWIN /*Easy Win */ | ||
static int end_check; | ||
static RB_THREAD_LOCAL_SPECIFIER int end_check; | ||
#endif /*Easy Win */ |
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 didn't analyse thoroughly which global variables are modified and which aren't so updated declaration of all the static global variables.
The issue is easily reproducible on TruffleRuby with the following code: require 'nkf'
threads = []
10.times do
th = Thread.new do
1000.times { NKF.guess("Hello world") }
end
threads << th
end
threads.each(&:join) It should be run with a
When it's run with nkf without the fix the issue is leads to the following error:
So the |
Other than pinning this code to C11+, it will also incur a performance penalty for most accesses: https://david-grs.github.io/tls_performance_overhead_cost_linux/ Because the nkf extension is loaded as a dynamic library, most optimizations of the TLS won't work and it will revert to a slow-path thread-local lookup (2x slower in the above example). I have to wonder if it wouldn't make more sense to just move these variables into a proper CRuby thread-local, which would avoid the C11 requirement, fix the issue, and be more explicit in form. I did not research what the impact is on Windows or platforms other than those covered in the above article. JRuby doesn't use this code, so it doesn't matter to us, but I happened to stumble on this issue while reviewing stdlib C exts today. I look forward to the day this extension just goes away. 😀 |
Regarding C11 I would expect Regarding the speed, Ractor uses TLS too (and there can be a dynamic libruby.so) so I think it's an insignificant overhead, especially for NKF. I'm not sure why the upstream NKF authors chose to pass state in global variables and not parameters, but that's anyway not optimal for performance, even before this change. |
4cecd26
to
8404e0f
Compare
Would it be possible to mark the C-extension with
since this PR makes it threadsafe/Ractor-safe? |
This PR makes it thread-safe but not Ractor-safe, because there is still global state per thread, and multiple Ractors can run on the same native thread with |
What is the upstream of |
There are global variables in the native extension what make it not thread-safe on TruffleRuby or any other alternative Ruby implementation that doesn't have GIL.
The issue was reported in oracle/truffleruby#3625.
The approach is to declare these global variables as thread local either with C11's
_Thread_local
or GCC-specific__thread
declaration.