1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
require_relative 'ivar_shared'
module Concurrent
RSpec.describe IVar do
let!(:value) { 10 }
let!(:fulfilled_value) { 10 }
let(:rejected_reason) { StandardError.new('Boom!') }
subject { IVar.new(value) }
let(:pending_subject) do
ivar = IVar.new
in_thread do
sleep(0.1)
ivar.set(fulfilled_value)
end
ivar
end
let(:fulfilled_subject) do
IVar.new.set(fulfilled_value)
end
let(:rejected_subject) do
IVar.new.fail(rejected_reason)
end
it_should_behave_like :ivar do
subject{ IVar.new }
def dereferenceable_subject(value, opts = {})
IVar.new(value, opts)
end
def dereferenceable_observable(opts = {})
IVar.new(NULL, opts)
end
def execute_dereferenceable(subject)
subject.set('value')
end
def trigger_observable(observable)
observable.set('value')
end
end
context '#initialize' do
it 'does not have to set an initial value' do
i = IVar.new
expect(i).to be_incomplete
end
it 'does not set an initial value if you pass NULL' do
i = IVar.new(NULL)
expect(i).to be_incomplete
end
it 'can set an initial value' do
i = IVar.new(14)
expect(i).to be_complete
expect(i.value).to eq 14
end
it 'can set an initial value with a block' do
i = IVar.new{ 42 }
expect(i).to be_complete
expect(i.value).to eq 42
end
it 'raises an exception if given both a value and a block' do
expect {
IVar.new(42){ 42 }
}.to raise_error(ArgumentError)
end
end
context 'observation' do
let(:clazz) do
Class.new do
attr_reader :value
attr_reader :reason
attr_reader :count
define_method(:update) do |time, value, reason|
@count ||= 0
@count = @count.to_i + 1
@value = value
@reason = reason
end
end
end
let(:observer) { clazz.new }
it 'notifies all observers on #set' do
i = IVar.new
i.add_observer(observer)
i.set(42)
expect(observer.value).to eq(42)
expect(observer.reason).to be_nil
end
context 'deadlock avoidance' do
def reentrant_observer(i)
obs = ::Object.new
obs.define_singleton_method(:update) do |time, value, reason|
@value = i.value
end
obs.define_singleton_method(:value) { @value }
obs
end
it 'should notify observers outside mutex lock' do
i = IVar.new
obs = reentrant_observer(i)
i.add_observer(obs)
i.set(42)
expect(obs.value).to eq 42
end
it 'should notify a new observer added after fulfillment outside lock' do
i = IVar.new
i.set(42)
obs = reentrant_observer(i)
i.add_observer(obs)
expect(obs.value).to eq 42
end
end
end
end
end
|