diff --git a/lib/concurrent-ruby/concurrent/atomic/constant_thread_local_var.rb b/lib/concurrent-ruby/concurrent/atomic/constant_thread_local_var.rb new file mode 100644 index 000000000..dfb02999a --- /dev/null +++ b/lib/concurrent-ruby/concurrent/atomic/constant_thread_local_var.rb @@ -0,0 +1,45 @@ +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? + 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/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/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