From 53e31a5c562535dca95a24f34fd837298e07e6ee Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Fri, 13 Sep 2019 17:26:34 +1000 Subject: [PATCH 1/2] FEATURE: add interface compatible constant version of thread local var ThreadLocalVar can be expensive as it spins a thread in finalizers. In some cases consumers may want to swap in a constant value implementations that is very cheap. `Concurrent::ConstantThreadLocalVar` is interface compatible with AbstractThreadLocalVar, however does not allow any changing of .value --- lib/concurrent-ruby/concurrent/atomics.rb | 1 + .../atomic/constant_thread_local_var.rb | 35 ++++++++++++ .../atomic/constant_thread_local_var_spec.rb | 56 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 lib/concurrent/atomic/constant_thread_local_var.rb create mode 100644 spec/concurrent/atomic/constant_thread_local_var_spec.rb diff --git a/lib/concurrent-ruby/concurrent/atomics.rb b/lib/concurrent-ruby/concurrent/atomics.rb index 16cbe6610..cb6d06b36 100644 --- a/lib/concurrent-ruby/concurrent/atomics.rb +++ b/lib/concurrent-ruby/concurrent/atomics.rb @@ -8,3 +8,4 @@ require 'concurrent/atomic/reentrant_read_write_lock' require 'concurrent/atomic/semaphore' require 'concurrent/atomic/thread_local_var' +require 'concurrent/atomic/constant_thread_local_var' diff --git a/lib/concurrent/atomic/constant_thread_local_var.rb b/lib/concurrent/atomic/constant_thread_local_var.rb new file mode 100644 index 000000000..310e865e9 --- /dev/null +++ b/lib/concurrent/atomic/constant_thread_local_var.rb @@ -0,0 +1,35 @@ +require 'concurrent/atomic/abstract_thread_local_var' + +module Concurrent + class ConstantThreadLocalVar < AbstractThreadLocalVar + def value + default + end + + def value=(value) + if value != default + raise ArgumentError, "Constant thread local vars may not be altered" + end + end + + def bind(value) + self.value = value + if block_given? + yield + end + end + + protected + def allocate_storage + # nothing to do + end + + def default + if @default_block + @default_block.call + else + @default + end + end + end +end diff --git a/spec/concurrent/atomic/constant_thread_local_var_spec.rb b/spec/concurrent/atomic/constant_thread_local_var_spec.rb new file mode 100644 index 000000000..45ea43776 --- /dev/null +++ b/spec/concurrent/atomic/constant_thread_local_var_spec.rb @@ -0,0 +1,56 @@ +require 'rbconfig' + +module Concurrent + + require 'concurrent/atomic/constant_thread_local_var' + + RSpec.describe ConstantThreadLocalVar do + + context "#value" do + it 'can return default value' do + v = described_class.new("apple") + expect(v.value).to eq("apple") + + v = described_class.new do + "orange" + end + expect(v.value).to eq("orange") + end + end + + context "#value=" do + it 'can set value to same value' do + v = described_class.new("apple") + v.value = "apple" + end + + it 'will raise an ArgumentError when attempting to change immutable value' do + v = described_class.new do + "apple" + end + + expect do + v.value = "orange" + end.to raise_error(ArgumentError) + end + end + + context '#bind' do + it 'will raise when attempting to bind to a different value' do + v = described_class.new("apple") + expect do + v.bind("orange") {} + end.to raise_error(ArgumentError) + end + + it 'works when bind value is the same' do + + v = described_class.new("apple") + v.bind("apple") do + expect(v.value).to eq("apple") + end + end + end + end + +end From 99dc326a0b9c7954bf40ddd574c4d501def1c343 Mon Sep 17 00:00:00 2001 From: Petr Chalupa Date: Mon, 10 Feb 2020 21:18:36 +0100 Subject: [PATCH 2/2] Fix placement and add documentation --- .../concurrent/atomic/constant_thread_local_var.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) rename lib/{ => concurrent-ruby}/concurrent/atomic/constant_thread_local_var.rb (56%) diff --git a/lib/concurrent/atomic/constant_thread_local_var.rb b/lib/concurrent-ruby/concurrent/atomic/constant_thread_local_var.rb similarity index 56% rename from lib/concurrent/atomic/constant_thread_local_var.rb rename to lib/concurrent-ruby/concurrent/atomic/constant_thread_local_var.rb index 310e865e9..dfb02999a 100644 --- a/lib/concurrent/atomic/constant_thread_local_var.rb +++ b/lib/concurrent-ruby/concurrent/atomic/constant_thread_local_var.rb @@ -1,17 +1,26 @@ require 'concurrent/atomic/abstract_thread_local_var' module Concurrent + # Has the same interface as {ThreadLocalVar} but the value can never be changed. + # The value is always the default provided to the constructor. class ConstantThreadLocalVar < AbstractThreadLocalVar + + # @note the value is always equal to default value + # @!macro thread_local_var_method_get def value default end + # @!macro thread_local_var_method_set + # @raise ArgumentError if the value is not equal to the default def value=(value) if value != default raise ArgumentError, "Constant thread local vars may not be altered" end end + # @!macro thread_local_var_method_bind + # @raise ArgumentError if the value is not equal to the default def bind(value) self.value = value if block_given? @@ -20,6 +29,7 @@ def bind(value) end protected + def allocate_storage # nothing to do end