concurrent-ruby 0.4.1 → 0.5.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +31 -33
- data/lib/concurrent.rb +11 -3
- data/lib/concurrent/actor.rb +29 -29
- data/lib/concurrent/agent.rb +98 -16
- data/lib/concurrent/atomic.rb +125 -0
- data/lib/concurrent/channel.rb +36 -1
- data/lib/concurrent/condition.rb +67 -0
- data/lib/concurrent/copy_on_notify_observer_set.rb +80 -0
- data/lib/concurrent/copy_on_write_observer_set.rb +94 -0
- data/lib/concurrent/count_down_latch.rb +60 -0
- data/lib/concurrent/dataflow.rb +85 -0
- data/lib/concurrent/dereferenceable.rb +69 -31
- data/lib/concurrent/event.rb +27 -21
- data/lib/concurrent/future.rb +103 -43
- data/lib/concurrent/ivar.rb +78 -0
- data/lib/concurrent/mvar.rb +154 -0
- data/lib/concurrent/obligation.rb +94 -9
- data/lib/concurrent/postable.rb +11 -9
- data/lib/concurrent/promise.rb +101 -127
- data/lib/concurrent/safe_task_executor.rb +28 -0
- data/lib/concurrent/scheduled_task.rb +60 -54
- data/lib/concurrent/stoppable.rb +2 -2
- data/lib/concurrent/supervisor.rb +36 -29
- data/lib/concurrent/thread_local_var.rb +117 -0
- data/lib/concurrent/timer_task.rb +28 -30
- data/lib/concurrent/utilities.rb +1 -1
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/agent_spec.rb +121 -230
- data/spec/concurrent/atomic_spec.rb +201 -0
- data/spec/concurrent/condition_spec.rb +171 -0
- data/spec/concurrent/copy_on_notify_observer_set_spec.rb +10 -0
- data/spec/concurrent/copy_on_write_observer_set_spec.rb +10 -0
- data/spec/concurrent/count_down_latch_spec.rb +125 -0
- data/spec/concurrent/dataflow_spec.rb +160 -0
- data/spec/concurrent/dereferenceable_shared.rb +145 -0
- data/spec/concurrent/event_spec.rb +44 -9
- data/spec/concurrent/fixed_thread_pool_spec.rb +0 -1
- data/spec/concurrent/future_spec.rb +184 -69
- data/spec/concurrent/ivar_spec.rb +192 -0
- data/spec/concurrent/mvar_spec.rb +380 -0
- data/spec/concurrent/obligation_spec.rb +193 -0
- data/spec/concurrent/observer_set_shared.rb +233 -0
- data/spec/concurrent/postable_shared.rb +3 -7
- data/spec/concurrent/promise_spec.rb +270 -192
- data/spec/concurrent/safe_task_executor_spec.rb +58 -0
- data/spec/concurrent/scheduled_task_spec.rb +142 -38
- data/spec/concurrent/thread_local_var_spec.rb +113 -0
- data/spec/concurrent/thread_pool_shared.rb +2 -3
- data/spec/concurrent/timer_task_spec.rb +31 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/functions.rb +4 -0
- data/spec/support/less_than_or_equal_to_matcher.rb +5 -0
- metadata +50 -30
- data/lib/concurrent/contract.rb +0 -21
- data/lib/concurrent/event_machine_defer_proxy.rb +0 -22
- data/md/actor.md +0 -404
- data/md/agent.md +0 -142
- data/md/channel.md +0 -40
- data/md/dereferenceable.md +0 -49
- data/md/future.md +0 -125
- data/md/obligation.md +0 -32
- data/md/promise.md +0 -217
- data/md/scheduled_task.md +0 -156
- data/md/supervisor.md +0 -246
- data/md/thread_pool.md +0 -225
- data/md/timer_task.md +0 -191
- data/spec/concurrent/contract_spec.rb +0 -34
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +0 -240
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'concurrent/safe_task_executor'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Concurrent
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              describe SafeTaskExecutor do
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                describe '#execute' do
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  context 'happy execution' do
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    let(:task) { Proc.new { 42 } }
         | 
| 13 | 
            +
                    let(:executor) { SafeTaskExecutor.new(task) }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    it 'should return success' do
         | 
| 16 | 
            +
                      success, value, reason = executor.execute
         | 
| 17 | 
            +
                      success.should be_true
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    it 'should return task value' do
         | 
| 21 | 
            +
                      success, value, reason = executor.execute
         | 
| 22 | 
            +
                      value.should eq 42
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    it 'should return a nil reason' do
         | 
| 26 | 
            +
                      success, value, reason = executor.execute
         | 
| 27 | 
            +
                      reason.should be_nil
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  context 'failing execution' do
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    let(:task) { Proc.new { raise StandardError.new('an error') } }
         | 
| 35 | 
            +
                    let(:executor) { SafeTaskExecutor.new(task) }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    it 'should return false success' do
         | 
| 38 | 
            +
                      success, value, reason = executor.execute
         | 
| 39 | 
            +
                      success.should be_false
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    it 'should return a nil value' do
         | 
| 43 | 
            +
                      success, value, reason = executor.execute
         | 
| 44 | 
            +
                      value.should be_nil
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    it 'should return the reason' do
         | 
| 48 | 
            +
                      success, value, reason = executor.execute
         | 
| 49 | 
            +
                      reason.should be_a(StandardError)
         | 
| 50 | 
            +
                      reason.message.should eq 'an error'
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
            end
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 2 | 
             
            require 'timecop'
         | 
| 3 | 
            +
            require_relative 'dereferenceable_shared'
         | 
| 3 4 | 
             
            require_relative 'obligation_shared'
         | 
| 4 5 |  | 
| 5 6 | 
             
            module Concurrent
         | 
| @@ -14,77 +15,183 @@ module Concurrent | |
| 14 15 | 
             
                  let!(:rejected_reason) { StandardError.new('mojo jojo') }
         | 
| 15 16 |  | 
| 16 17 | 
             
                  let(:pending_subject) do
         | 
| 17 | 
            -
                    ScheduledTask.new(1){ fulfilled_value }
         | 
| 18 | 
            +
                    ScheduledTask.new(1){ fulfilled_value }.execute
         | 
| 18 19 | 
             
                  end
         | 
| 19 20 |  | 
| 20 21 | 
             
                  let(:fulfilled_subject) do
         | 
| 21 | 
            -
                    ScheduledTask.new(0.1){ fulfilled_value }.tap | 
| 22 | 
            +
                    ScheduledTask.new(0.1){ fulfilled_value }.execute.tap{ sleep(0.2) }
         | 
| 22 23 | 
             
                  end
         | 
| 23 24 |  | 
| 24 25 | 
             
                  let(:rejected_subject) do
         | 
| 25 | 
            -
                    ScheduledTask.new(0.1){ raise rejected_reason }.tap | 
| 26 | 
            +
                    ScheduledTask.new(0.1){ raise rejected_reason }.execute.tap{ sleep(0.2) }
         | 
| 26 27 | 
             
                  end
         | 
| 27 28 |  | 
| 28 29 | 
             
                  it_should_behave_like :obligation
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  # dereferenceable
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def dereferenceable_subject(value, opts = {})
         | 
| 34 | 
            +
                    ScheduledTask.execute(0.1, opts){ value }.tap{ sleep(0.2) }
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def dereferenceable_observable(opts = {})
         | 
| 38 | 
            +
                    ScheduledTask.new(0.1, opts){ 'value' }
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def execute_dereferenceable(subject)
         | 
| 42 | 
            +
                    subject.execute
         | 
| 43 | 
            +
                    sleep(0.2)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  it_should_behave_like :dereferenceable
         | 
| 29 47 | 
             
                end
         | 
| 30 48 |  | 
| 31 49 | 
             
                context '#initialize' do
         | 
| 32 50 |  | 
| 33 | 
            -
                  it 'accepts a number of seconds (from now) as the  | 
| 51 | 
            +
                  it 'accepts a number of seconds (from now) as the schedule time' do
         | 
| 34 52 | 
             
                    Timecop.freeze do
         | 
| 35 53 | 
             
                      now = Time.now
         | 
| 36 | 
            -
                      task = ScheduledTask.new(60){ nil }
         | 
| 54 | 
            +
                      task = ScheduledTask.new(60){ nil }.execute
         | 
| 37 55 | 
             
                      task.schedule_time.to_i.should eq now.to_i + 60
         | 
| 38 56 | 
             
                    end
         | 
| 39 57 | 
             
                  end
         | 
| 40 58 |  | 
| 41 59 | 
             
                  it 'accepts a time object as the schedule time' do
         | 
| 42 60 | 
             
                    schedule = Time.now + (60*10)
         | 
| 43 | 
            -
                    task = ScheduledTask.new(schedule){ nil }
         | 
| 61 | 
            +
                    task = ScheduledTask.new(schedule){ nil }.execute
         | 
| 44 62 | 
             
                    task.schedule_time.should eq schedule
         | 
| 45 63 | 
             
                  end
         | 
| 46 64 |  | 
| 47 65 | 
             
                  it 'raises an exception when seconds is less than zero' do
         | 
| 48 66 | 
             
                    expect {
         | 
| 49 67 | 
             
                      ScheduledTask.new(-1){ nil }
         | 
| 50 | 
            -
                    }.to raise_error( | 
| 68 | 
            +
                    }.to raise_error(ScheduledTask::SchedulingError)
         | 
| 51 69 | 
             
                  end
         | 
| 52 70 |  | 
| 53 71 | 
             
                  it 'raises an exception when schedule time is in the past' do
         | 
| 54 72 | 
             
                    expect {
         | 
| 55 73 | 
             
                      ScheduledTask.new(Time.now - 60){ nil }
         | 
| 56 | 
            -
                    }.to raise_error( | 
| 74 | 
            +
                    }.to raise_error(ScheduledTask::SchedulingError)
         | 
| 57 75 | 
             
                  end
         | 
| 58 76 |  | 
| 59 77 | 
             
                  it 'raises an exception when no block given' do
         | 
| 60 78 | 
             
                    expect {
         | 
| 61 79 | 
             
                      ScheduledTask.new(1)
         | 
| 62 | 
            -
                    }.to raise_error( | 
| 80 | 
            +
                    }.to raise_error(ScheduledTask::SchedulingError)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  it 'sets the initial state to :unscheduled' do
         | 
| 84 | 
            +
                    task = ScheduledTask.new(1){ nil }
         | 
| 85 | 
            +
                    task.should be_unscheduled
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  it 'sets the #schedule_time to nil prior to execution' do
         | 
| 89 | 
            +
                    task = ScheduledTask.new(1){ nil }
         | 
| 90 | 
            +
                    task.schedule_time.should be_nil
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                context 'instance #execute' do
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  it 'does nothing unless the state is :unscheduled' do
         | 
| 97 | 
            +
                    Thread.should_not_receive(:new).with(any_args)
         | 
| 98 | 
            +
                    task = ScheduledTask.new(1){ nil }
         | 
| 99 | 
            +
                    task.instance_variable_set(:@state, :pending)
         | 
| 100 | 
            +
                    task.execute
         | 
| 101 | 
            +
                    task.instance_variable_set(:@state, :rejected)
         | 
| 102 | 
            +
                    task.execute
         | 
| 103 | 
            +
                    task.instance_variable_set(:@state, :fulfilled)
         | 
| 104 | 
            +
                    task.execute
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  it 'calculates the #schedule_time on execution' do
         | 
| 108 | 
            +
                    Timecop.freeze do
         | 
| 109 | 
            +
                      now = Time.now
         | 
| 110 | 
            +
                      task = ScheduledTask.new(5){ nil }
         | 
| 111 | 
            +
                      Timecop.travel(10)
         | 
| 112 | 
            +
                      task.execute
         | 
| 113 | 
            +
                      task.schedule_time.to_i.should eq now.to_i + 15
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  it 'raises an exception if expected schedule time is in the past' do
         | 
| 118 | 
            +
                    Timecop.freeze do
         | 
| 119 | 
            +
                      schedule = Time.now + (10)
         | 
| 120 | 
            +
                      task = ScheduledTask.new(schedule){ nil }
         | 
| 121 | 
            +
                      Timecop.travel(60)
         | 
| 122 | 
            +
                      expect {
         | 
| 123 | 
            +
                        task.execute
         | 
| 124 | 
            +
                      }.to raise_error(ScheduledTask::SchedulingError)
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  it 'spawns a new thread when a block was given on construction' do
         | 
| 129 | 
            +
                    Thread.should_receive(:new).with(any_args)
         | 
| 130 | 
            +
                    task = ScheduledTask.new(1){ nil }
         | 
| 131 | 
            +
                    task.execute
         | 
| 63 132 | 
             
                  end
         | 
| 64 133 |  | 
| 65 | 
            -
                  it 'sets the  | 
| 134 | 
            +
                  it 'sets the sate to :pending' do
         | 
| 66 135 | 
             
                    task = ScheduledTask.new(1){ nil }
         | 
| 136 | 
            +
                    task.execute
         | 
| 67 137 | 
             
                    task.should be_pending
         | 
| 68 138 | 
             
                  end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  it 'returns self' do
         | 
| 141 | 
            +
                    task = ScheduledTask.new(1){ nil }
         | 
| 142 | 
            +
                    task.execute.should eq task
         | 
| 143 | 
            +
                  end
         | 
| 144 | 
            +
                end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                context 'class #execute' do
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  it 'creates a new ScheduledTask' do
         | 
| 149 | 
            +
                    task = ScheduledTask.execute(1){ nil }
         | 
| 150 | 
            +
                    task.should be_a(ScheduledTask)
         | 
| 151 | 
            +
                  end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                  it 'passes the block to the new ScheduledTask' do
         | 
| 154 | 
            +
                    @expected = false
         | 
| 155 | 
            +
                    task = ScheduledTask.execute(0.1){ @expected = true }
         | 
| 156 | 
            +
                    sleep(0.2)
         | 
| 157 | 
            +
                    @expected.should be_true
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  it 'calls #execute on the new ScheduledTask' do
         | 
| 161 | 
            +
                    task = ScheduledTask.new(0.1){ nil }
         | 
| 162 | 
            +
                    ScheduledTask.stub(:new).with(any_args).and_return(task)
         | 
| 163 | 
            +
                    task.should_receive(:execute).with(no_args)
         | 
| 164 | 
            +
                    ScheduledTask.execute(0.1){ nil }
         | 
| 165 | 
            +
                  end
         | 
| 69 166 | 
             
                end
         | 
| 70 167 |  | 
| 71 168 | 
             
                context '#cancel' do
         | 
| 72 169 |  | 
| 73 170 | 
             
                  it 'returns false if the task has already been performed' do
         | 
| 74 | 
            -
                    task = ScheduledTask.new(0.1){ 42 }
         | 
| 171 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }.execute
         | 
| 75 172 | 
             
                    sleep(0.2)
         | 
| 76 173 | 
             
                    task.cancel.should be_false
         | 
| 77 174 | 
             
                  end
         | 
| 78 175 |  | 
| 79 176 | 
             
                  it 'returns false if the task is already in progress' do
         | 
| 80 | 
            -
                    task = ScheduledTask.new(0.1){ sleep(1); 42 }
         | 
| 177 | 
            +
                    task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
         | 
| 81 178 | 
             
                    sleep(0.2)
         | 
| 82 179 | 
             
                    task.cancel.should be_false
         | 
| 83 180 | 
             
                  end
         | 
| 84 181 |  | 
| 182 | 
            +
                  it 'cancels the task if it has not yet scheduled' do
         | 
| 183 | 
            +
                    @expected = true
         | 
| 184 | 
            +
                    task = ScheduledTask.new(0.1){ @expected = false }
         | 
| 185 | 
            +
                    task.cancel
         | 
| 186 | 
            +
                    task.execute
         | 
| 187 | 
            +
                    sleep(0.5)
         | 
| 188 | 
            +
                    @expected.should be_true
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
             | 
| 85 192 | 
             
                  it 'cancels the task if it has not yet started' do
         | 
| 86 193 | 
             
                    @expected = true
         | 
| 87 | 
            -
                    task = ScheduledTask.new(0.3){ @expected = false }
         | 
| 194 | 
            +
                    task = ScheduledTask.new(0.3){ @expected = false }.execute
         | 
| 88 195 | 
             
                    sleep(0.1)
         | 
| 89 196 | 
             
                    task.cancel
         | 
| 90 197 | 
             
                    sleep(0.5)
         | 
| @@ -92,13 +199,13 @@ module Concurrent | |
| 92 199 | 
             
                  end
         | 
| 93 200 |  | 
| 94 201 | 
             
                  it 'returns true on success' do
         | 
| 95 | 
            -
                    task = ScheduledTask.new(0.3){ @expected = false }
         | 
| 202 | 
            +
                    task = ScheduledTask.new(0.3){ @expected = false }.execute
         | 
| 96 203 | 
             
                    sleep(0.1)
         | 
| 97 204 | 
             
                    task.cancel.should be_true
         | 
| 98 205 | 
             
                  end
         | 
| 99 206 |  | 
| 100 207 | 
             
                  it 'sets the state to :cancelled when cancelled' do
         | 
| 101 | 
            -
                    task = ScheduledTask.new(10){ 42 }
         | 
| 208 | 
            +
                    task = ScheduledTask.new(10){ 42 }.execute
         | 
| 102 209 | 
             
                    sleep(0.1)
         | 
| 103 210 | 
             
                    task.cancel
         | 
| 104 211 | 
             
                    task.should be_cancelled
         | 
| @@ -108,7 +215,7 @@ module Concurrent | |
| 108 215 | 
             
                context 'execution' do
         | 
| 109 216 |  | 
| 110 217 | 
             
                  it 'sets the state to :in_progress when the task is running' do
         | 
| 111 | 
            -
                    task = ScheduledTask.new(0.1){ sleep(1); 42 }
         | 
| 218 | 
            +
                    task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
         | 
| 112 219 | 
             
                    sleep(0.2)
         | 
| 113 220 | 
             
                    task.should be_in_progress
         | 
| 114 221 | 
             
                  end
         | 
| @@ -131,44 +238,42 @@ module Concurrent | |
| 131 238 |  | 
| 132 239 | 
             
                  let(:observer) { clazz.new }
         | 
| 133 240 |  | 
| 134 | 
            -
                  it 'returns true for an observer added while : | 
| 135 | 
            -
                    task = ScheduledTask.new(1){ 42 }
         | 
| 241 | 
            +
                  it 'returns true for an observer added while :unscheduled' do
         | 
| 242 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }
         | 
| 136 243 | 
             
                    task.add_observer(observer).should be_true
         | 
| 137 244 | 
             
                  end
         | 
| 138 245 |  | 
| 139 | 
            -
                  it 'returns true for an observer added while : | 
| 140 | 
            -
                    task = ScheduledTask.new(0.1){  | 
| 141 | 
            -
                    sleep(0.2)
         | 
| 246 | 
            +
                  it 'returns true for an observer added while :pending' do
         | 
| 247 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }.execute
         | 
| 142 248 | 
             
                    task.add_observer(observer).should be_true
         | 
| 143 249 | 
             
                  end
         | 
| 144 250 |  | 
| 145 | 
            -
                  it 'returns true for an observer added while  | 
| 146 | 
            -
                    task = ScheduledTask.new(1){ 42 }
         | 
| 251 | 
            +
                  it 'returns true for an observer added while :in_progress' do
         | 
| 252 | 
            +
                    task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
         | 
| 253 | 
            +
                    sleep(0.2)
         | 
| 147 254 | 
             
                    task.add_observer(observer).should be_true
         | 
| 148 255 | 
             
                  end
         | 
| 149 256 |  | 
| 150 257 | 
             
                  it 'returns false for an observer added once :cancelled' do
         | 
| 151 258 | 
             
                    task = ScheduledTask.new(1){ 42 }
         | 
| 152 | 
            -
                    sleep(0.1)
         | 
| 153 259 | 
             
                    task.cancel
         | 
| 154 | 
            -
                    sleep(0.1)
         | 
| 155 260 | 
             
                    task.add_observer(observer).should be_false
         | 
| 156 261 | 
             
                  end
         | 
| 157 262 |  | 
| 158 263 | 
             
                  it 'returns false for an observer added once :fulfilled' do
         | 
| 159 | 
            -
                    task = ScheduledTask.new(0.1){ 42 }
         | 
| 264 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }.execute
         | 
| 160 265 | 
             
                    sleep(0.2)
         | 
| 161 266 | 
             
                    task.add_observer(observer).should be_false
         | 
| 162 267 | 
             
                  end
         | 
| 163 268 |  | 
| 164 269 | 
             
                  it 'returns false for an observer added once :rejected' do
         | 
| 165 | 
            -
                    task = ScheduledTask.new(0.1){ raise StandardError }
         | 
| 270 | 
            +
                    task = ScheduledTask.new(0.1){ raise StandardError }.execute
         | 
| 166 271 | 
             
                    sleep(0.2)
         | 
| 167 272 | 
             
                    task.add_observer(observer).should be_false
         | 
| 168 273 | 
             
                  end
         | 
| 169 274 |  | 
| 170 275 | 
             
                  it 'notifies all observers on fulfillment' do
         | 
| 171 | 
            -
                    task = ScheduledTask.new(0.1){ 42 }
         | 
| 276 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }.execute
         | 
| 172 277 | 
             
                    task.add_observer(observer)
         | 
| 173 278 | 
             
                    sleep(0.2)
         | 
| 174 279 | 
             
                    task.value.should == 42
         | 
| @@ -178,7 +283,7 @@ module Concurrent | |
| 178 283 | 
             
                  end
         | 
| 179 284 |  | 
| 180 285 | 
             
                  it 'notifies all observers on rejection' do
         | 
| 181 | 
            -
                    task = ScheduledTask.new(0.1){ raise StandardError }
         | 
| 286 | 
            +
                    task = ScheduledTask.new(0.1){ raise StandardError }.execute
         | 
| 182 287 | 
             
                    task.add_observer(observer)
         | 
| 183 288 | 
             
                    sleep(0.2)
         | 
| 184 289 | 
             
                    task.value.should be_nil
         | 
| @@ -188,30 +293,29 @@ module Concurrent | |
| 188 293 | 
             
                  end
         | 
| 189 294 |  | 
| 190 295 | 
             
                  it 'does not notify an observer added after fulfillment' do
         | 
| 191 | 
            -
                    observer.should_not_receive(:update).with(any_args | 
| 192 | 
            -
                    task = ScheduledTask.new(0.1){ 42 }
         | 
| 296 | 
            +
                    observer.should_not_receive(:update).with(any_args)
         | 
| 297 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }.execute
         | 
| 193 298 | 
             
                    sleep(0.2)
         | 
| 194 299 | 
             
                    task.add_observer(observer)
         | 
| 195 300 | 
             
                    sleep(0.1)
         | 
| 196 301 | 
             
                  end
         | 
| 197 302 |  | 
| 198 303 | 
             
                  it 'does not notify an observer added after rejection' do
         | 
| 199 | 
            -
                    observer.should_not_receive(:update).with(any_args | 
| 200 | 
            -
                    task = ScheduledTask.new(0.1){ raise StandardError }
         | 
| 304 | 
            +
                    observer.should_not_receive(:update).with(any_args)
         | 
| 305 | 
            +
                    task = ScheduledTask.new(0.1){ raise StandardError }.execute
         | 
| 201 306 | 
             
                    sleep(0.2)
         | 
| 202 307 | 
             
                    task.add_observer(observer)
         | 
| 203 308 | 
             
                    sleep(0.1)
         | 
| 204 309 | 
             
                  end
         | 
| 205 310 |  | 
| 206 311 | 
             
                  it 'does not notify an observer added after cancellation' do
         | 
| 207 | 
            -
                    observer.should_not_receive(:update).with(any_args | 
| 208 | 
            -
                    task = ScheduledTask.new(0. | 
| 209 | 
            -
                    sleep(0.1)
         | 
| 312 | 
            +
                    observer.should_not_receive(:update).with(any_args)
         | 
| 313 | 
            +
                    task = ScheduledTask.new(0.1){ 42 }.execute
         | 
| 210 314 | 
             
                    task.cancel
         | 
| 211 | 
            -
                    sleep(0.1)
         | 
| 212 315 | 
             
                    task.add_observer(observer)
         | 
| 213 | 
            -
                    sleep(0. | 
| 316 | 
            +
                    sleep(0.2)
         | 
| 214 317 | 
             
                  end
         | 
| 318 | 
            +
             | 
| 215 319 | 
             
                end
         | 
| 216 320 | 
             
              end
         | 
| 217 321 | 
             
            end
         | 
| @@ -0,0 +1,113 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'rbconfig'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Concurrent
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              describe ThreadLocalVar do
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                subject{ ThreadLocalVar.new }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                context '#initialize' do
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  it 'can set an initial value' do
         | 
| 13 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 14 | 
            +
                    v.value.should eq 14
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  it 'sets nil as a default initial value' do
         | 
| 18 | 
            +
                    v = ThreadLocalVar.new
         | 
| 19 | 
            +
                    v.value.should be_nil
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  it 'sets the same initial value for all threads' do
         | 
| 23 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 24 | 
            +
                    t1 = Thread.new { v.value }
         | 
| 25 | 
            +
                    t2 = Thread.new { v.value }
         | 
| 26 | 
            +
                    t1.value.should eq 14
         | 
| 27 | 
            +
                    t2.value.should eq 14
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  if jruby?
         | 
| 31 | 
            +
                    it 'uses ThreadLocalJavaStorage' do
         | 
| 32 | 
            +
                      subject.class.ancestors.should include(Concurrent::ThreadLocalJavaStorage)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  elsif rbx? || RbConfig::CONFIG['ruby_version'] =~ /^1\.9/
         | 
| 35 | 
            +
                    it 'uses ThreadLocalOldStorage' do
         | 
| 36 | 
            +
                      subject.class.ancestors.should include(Concurrent::ThreadLocalOldStorage)
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    it 'uses ThreadLocalNewStorage' do
         | 
| 40 | 
            +
                      subject.class.ancestors.should include(Concurrent::ThreadLocalNewStorage)
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                context '#value' do
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  it 'returns the current value' do
         | 
| 48 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 49 | 
            +
                    v.value.should eq 14
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  it 'returns the value after modification' do
         | 
| 53 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 54 | 
            +
                    v.value = 2
         | 
| 55 | 
            +
                    v.value.should eq 2
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                context '#value=' do
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  it 'sets a new value' do
         | 
| 63 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 64 | 
            +
                    v.value = 2
         | 
| 65 | 
            +
                    v.value.should eq 2
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  it 'returns the new value' do
         | 
| 69 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 70 | 
            +
                    (v.value = 2).should eq 2
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  it 'does not modify the initial value for other threads' do
         | 
| 74 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 75 | 
            +
                    v.value = 2
         | 
| 76 | 
            +
                    t = Thread.new { v.value }
         | 
| 77 | 
            +
                    t.value.should eq 14
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  it 'does not modify the value for other threads' do
         | 
| 81 | 
            +
                    v = ThreadLocalVar.new(14)
         | 
| 82 | 
            +
                    v.value = 2
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    b1 = CountDownLatch.new(2)
         | 
| 85 | 
            +
                    b2 = CountDownLatch.new(2)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    t1 = Thread.new do
         | 
| 88 | 
            +
                      b1.count_down
         | 
| 89 | 
            +
                      b1.wait
         | 
| 90 | 
            +
                      v.value = 1
         | 
| 91 | 
            +
                      b2.count_down
         | 
| 92 | 
            +
                      b2.wait
         | 
| 93 | 
            +
                      v.value
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    t2 = Thread.new do
         | 
| 97 | 
            +
                      b1.count_down
         | 
| 98 | 
            +
                      b1.wait
         | 
| 99 | 
            +
                      v.value = 2
         | 
| 100 | 
            +
                      b2.count_down
         | 
| 101 | 
            +
                      b2.wait
         | 
| 102 | 
            +
                      v.value
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    t1.value.should eq 1
         | 
| 106 | 
            +
                    t2.value.should eq 2
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            end
         |