From 9182bc3dd2576f409a6d01fb5c08d392670e90a2 Mon Sep 17 00:00:00 2001 From: Patrick Carlisle Date: Mon, 9 Sep 2019 11:51:24 -0700 Subject: [PATCH] (PUP-9931) Use ruby threadlocal implementation If we cycle out JRuby instances while using the Java ThreadLocal class, we end up retaining references to the old JRuby instances. A simple solution to this is to use the native ruby ThreadLocalVar implementation, so that when a JRuby instance is GCd everything stored on the ruby Thread class will be collected as well. --- lib/puppet/context.rb | 8 ++++---- lib/puppet/thread_local.rb | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 lib/puppet/thread_local.rb diff --git a/lib/puppet/context.rb b/lib/puppet/context.rb index 90871e3f5e5..4839619f782 100644 --- a/lib/puppet/context.rb +++ b/lib/puppet/context.rb @@ -1,4 +1,4 @@ -require 'concurrent' +require 'puppet/thread_local' # Puppet::Context is a system for tracking services and contextual information # that puppet needs to be able to run. Values are "bound" in a context when it is created @@ -21,12 +21,12 @@ class DuplicateRollbackMarkError < Puppet::Error; end # @api private def initialize(initial_bindings) - @stack = Concurrent::ThreadLocalVar.new(EmptyStack.new.push(initial_bindings)) + @stack = Puppet::ThreadLocal.new(EmptyStack.new.push(initial_bindings)) # By initializing @rollbacks to nil and creating a hash lazily when #mark or # #rollback are called we ensure that the hashes are never shared between # threads and it's safe to mutate them - @rollbacks = Concurrent::ThreadLocalVar.new(nil) + @rollbacks = Puppet::ThreadLocal.new(nil) end # @api private @@ -39,7 +39,7 @@ def push(overrides, description = '') # # @api private def unsafe_push_global(overrides, description = '') - @stack = Concurrent::ThreadLocalVar.new( + @stack = Puppet::ThreadLocal.new( @stack.value.push(overrides, description) ) end diff --git a/lib/puppet/thread_local.rb b/lib/puppet/thread_local.rb new file mode 100644 index 00000000000..41c39d5fba2 --- /dev/null +++ b/lib/puppet/thread_local.rb @@ -0,0 +1,7 @@ +require 'concurrent' + +# We want to use the pure Ruby implementation even on JRuby. If we use the Java +# implementation of ThreadLocal, we end up leaking references to JRuby instances +# and preventing them from being garbage collected. +class Puppet::ThreadLocal < Concurrent::RubyThreadLocalVar +end