Global variable reads should behave as if they are volatile, so they always get the latest value.
However, it seems the invokedynamic implementation is not thread-safe or make some of the reads just get stale values, and never see the new value.
See bug_jruby_globals.rb to reproduce.
It accepts a number of threads as an argument.
Locally, the script never terminates with 4 threads, it does 1 to ~5 iterations and gets stuck.
Passing -Xinvokedynamic.global.maxfail=0 solves the problem so it seems related to invokedynamic globals. Running on MRI or TruffleRuby (which also treat global variables as constants until they are changed) also works fine.
There seems to be other thread safety issues in the implementation of globals:
nil (if the invalidator is not checked such as on the slow path) when the variable was already set.Nice find, thanks. I feel like the way our globals are structured needs a reboot (the current way is WAY over-abstracted) but I'll have a look at fixing this in place for now.
A fix is in for 9.1.14 that basically just disables the caching like @eregon did. I am resolving this as fixed and will file a separate bug for the many race conditions we need to deal with.
I've updated the code in #5536 to gracefully fall back on a volatile field when a global is modified more than MAXFAIL times. That PR appears to allow this script to run safely with any number of threads all contending the globals (resolving #4808).
Most helpful comment
Nice find, thanks. I feel like the way our globals are structured needs a reboot (the current way is WAY over-abstracted) but I'll have a look at fixing this in place for now.