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
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 75d1509eb5bc369de5272cdaf90447e357c45c6a
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: c2ae01438d9848893ab00a08880368bf82662f75
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 95702184ad897fd9874ae2f8bcaddcf0501a166f1dfad0fa37fc5be2a6c7b0d133bf74cd843753880135c3b4d8b63c8d515b08cf2b9d692fdfaf629533502fe2
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: dba5b747f74d44cf63475eb7a1dc081b3fea7fc7218ef016773c37a4f19db0af410ed6e8cb621d0dcdc363406ef3b63afe430bf8ae54916b2eab7374c7e887fc
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,4 +1,7 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Concurrent Ruby 
     | 
| 
      
 1 
     | 
    
         
            +
            # Concurrent Ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            [](http://badge.fury.io/rb/concurrent-ruby) [](https://travis-ci.org/jdantonio/concurrent-ruby?branch=master) [](https://coveralls.io/r/jdantonio/concurrent-ruby) [](https://codeclimate.com/github/jdantonio/concurrent-ruby) [](https://gemnasium.com/jdantonio/concurrent-ruby)
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            ***NOTE:*** *A few API updates in v0.5.0 are not backward-compatible. Please see the [release notes](https://github.com/jdantonio/concurrent-ruby/wiki/API-Updates-in-v0.5.0).*
         
     | 
| 
       2 
5 
     | 
    
         | 
| 
       3 
6 
     | 
    
         
             
            Modern concurrency tools for Ruby. Inspired by
         
     | 
| 
       4 
7 
     | 
    
         
             
            [Erlang](http://www.erlang.org/doc/reference_manual/processes.html),
         
     | 
| 
         @@ -21,26 +24,36 @@ The design goals of this gem are: 
     | 
|
| 
       21 
24 
     | 
    
         | 
| 
       22 
25 
     | 
    
         
             
            ## Features & Documentation
         
     | 
| 
       23 
26 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
            *  
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
               
     | 
| 
       35 
     | 
    
         
            -
               
     | 
| 
      
 27 
     | 
    
         
            +
            Please see the [Concurrent Ruby Wiki](https://github.com/jdantonio/concurrent-ruby/wiki)
         
     | 
| 
      
 28 
     | 
    
         
            +
            or the [API documentation](http://rubydoc.info/github/jdantonio/concurrent-ruby/master/frames)
         
     | 
| 
      
 29 
     | 
    
         
            +
            for more information or join our [mailing list](http://groups.google.com/group/concurrent-ruby).
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            There are many concurrency abstractions in this library. These abstractions can be broadly categorized
         
     | 
| 
      
 32 
     | 
    
         
            +
            into several general categories:
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            * Asynchronous concurrency abstractions including [Actor](https://github.com/jdantonio/concurrent-ruby/wiki/Actor),
         
     | 
| 
      
 35 
     | 
    
         
            +
              [Agent](https://github.com/jdantonio/concurrent-ruby/wiki/Agent), [Channel](https://github.com/jdantonio/concurrent-ruby/wiki/Channel),
         
     | 
| 
      
 36 
     | 
    
         
            +
              [Future](https://github.com/jdantonio/concurrent-ruby/wiki/Future), [Promise](https://github.com/jdantonio/concurrent-ruby/wiki/Promise),
         
     | 
| 
      
 37 
     | 
    
         
            +
              [ScheculedTask](https://github.com/jdantonio/concurrent-ruby/wiki/ScheduledTask),
         
     | 
| 
      
 38 
     | 
    
         
            +
              and [TimerTask](https://github.com/jdantonio/concurrent-ruby/wiki/TimerTask) 
         
     | 
| 
      
 39 
     | 
    
         
            +
            * Erlang-inspired [Supervisor](https://github.com/jdantonio/concurrent-ruby/wiki/Supervisor) and other lifecycle classes/mixins
         
     | 
| 
      
 40 
     | 
    
         
            +
              for managing long-running threads
         
     | 
| 
      
 41 
     | 
    
         
            +
            * Thread-safe variables including [M-Structures](https://github.com/jdantonio/concurrent-ruby/wiki/MVar-(M-Structure)),
         
     | 
| 
      
 42 
     | 
    
         
            +
              [I-Structures](https://github.com/jdantonio/concurrent-ruby/wiki/IVar-(I-Structure)),
         
     | 
| 
      
 43 
     | 
    
         
            +
              [thread-local variables](https://github.com/jdantonio/concurrent-ruby/wiki/ThreadLocalVar),
         
     | 
| 
      
 44 
     | 
    
         
            +
              and atomic counters
         
     | 
| 
      
 45 
     | 
    
         
            +
            * Thread synchronization classes and algorithms including [dataflow](https://github.com/jdantonio/concurrent-ruby/wiki/Dataflow), 
         
     | 
| 
      
 46 
     | 
    
         
            +
              timeout, condition, countdown latch, dependency counter, and event
         
     | 
| 
      
 47 
     | 
    
         
            +
            * Java-inspired [thread pools](https://github.com/jdantonio/concurrent-ruby/wiki/Thread%20Pools)
         
     | 
| 
      
 48 
     | 
    
         
            +
            * And many more...
         
     | 
| 
       36 
49 
     | 
    
         | 
| 
       37 
50 
     | 
    
         
             
            ### Semantic Versioning
         
     | 
| 
       38 
51 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
            This gem adheres to the rules of [semantic versioning](http://semver.org/).
         
     | 
| 
       40 
53 
     | 
    
         | 
| 
       41 
54 
     | 
    
         
             
            ### Supported Ruby versions
         
     | 
| 
       42 
55 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
            MRI 1.9.2, 1.9.3, 2.0, 2.1,  
     | 
| 
      
 56 
     | 
    
         
            +
            MRI 1.9.2, 1.9.3, 2.0, 2.1, JRuby (1.9 mode), and Rubinius 2.x. This library is pure Ruby and has no gem dependencies.
         
     | 
| 
       44 
57 
     | 
    
         
             
            It should be fully compatible with any Ruby interpreter that is 1.9.x compliant.
         
     | 
| 
       45 
58 
     | 
    
         | 
| 
       46 
59 
     | 
    
         
             
            ### Example
         
     | 
| 
         @@ -106,6 +119,9 @@ These tools will help ease the burden, but at the end of the day it is essential 
     | 
|
| 
       106 
119 
     | 
    
         
             
            ## Contributors
         
     | 
| 
       107 
120 
     | 
    
         | 
| 
       108 
121 
     | 
    
         
             
            * [Michele Della Torre](https://github.com/mighe)
         
     | 
| 
      
 122 
     | 
    
         
            +
            * [Chris Seaton](https://github.com/chrisseaton)
         
     | 
| 
      
 123 
     | 
    
         
            +
            * [Giuseppe Capizzi](https://github.com/gcapizzi)
         
     | 
| 
      
 124 
     | 
    
         
            +
            * [Brian Shirai](https://github.com/brixen)
         
     | 
| 
       109 
125 
     | 
    
         
             
            * [Chip Miller](https://github.com/chip-miller)
         
     | 
| 
       110 
126 
     | 
    
         
             
            * [Jamie Hodge](https://github.com/jamiehodge)
         
     | 
| 
       111 
127 
     | 
    
         
             
            * [Zander Hill](https://github.com/zph)
         
     | 
| 
         @@ -118,24 +134,6 @@ These tools will help ease the burden, but at the end of the day it is essential 
     | 
|
| 
       118 
134 
     | 
    
         
             
            4. Push to the branch (`git push origin my-new-feature`)
         
     | 
| 
       119 
135 
     | 
    
         
             
            5. Create new Pull Request
         
     | 
| 
       120 
136 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
            ### Conference Presentations
         
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
     | 
    
         
            -
            I've given several conference presentations on concurrent programming with this gem.
         
     | 
| 
       124 
     | 
    
         
            -
            Check them out:
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
            * ["Advanced Concurrent Programming in Ruby"](http://rubyconf.org/program#jerry-dantonio)
         
     | 
| 
       127 
     | 
    
         
            -
              at [RubyConf 2013](http://rubyconf.org/)
         
     | 
| 
       128 
     | 
    
         
            -
              used [this](https://github.com/jdantonio/concurrent-ruby-presentation) version of the presentation
         
     | 
| 
       129 
     | 
    
         
            -
              and is available for viewing on [Confreaks](http://www.confreaks.com/videos/2872-rubyconf2013-advanced-concurrent-programming-in-ruby)
         
     | 
| 
       130 
     | 
    
         
            -
            * ["Advanced Multithreading in Ruby"](http://cascadiaruby.com/#advanced-multithreading-in-ruby)
         
     | 
| 
       131 
     | 
    
         
            -
              at [Cascadia Ruby 2013](http://cascadiaruby.com/)
         
     | 
| 
       132 
     | 
    
         
            -
              used [this](https://github.com/jdantonio/concurrent-ruby-presentation/tree/cascadia-ruby-2013) version of the presentation
         
     | 
| 
       133 
     | 
    
         
            -
              and is available for viewing on [Confreaks](http://www.confreaks.com/videos/2790-cascadiaruby2013-advanced-multithreading-in-ruby)
         
     | 
| 
       134 
     | 
    
         
            -
            * [Cleveland Ruby Brigade](http://www.meetup.com/ClevelandRuby/events/149981942/) meetup in December of 2013
         
     | 
| 
       135 
     | 
    
         
            -
              used [this](https://github.com/jdantonio/concurrent-ruby-presentation/releases/tag/clerb-dec-2013) version of the presentation
         
     | 
| 
       136 
     | 
    
         
            -
            * I'll be giving ["Advanced Concurrent Programming in Ruby"](http://codemash.org/sessions)
         
     | 
| 
       137 
     | 
    
         
            -
              at [CodeMash 2014](http://codemash.org/)
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
137 
     | 
    
         
             
            ## License and Copyright
         
     | 
| 
       140 
138 
     | 
    
         | 
| 
       141 
139 
     | 
    
         
             
            *Concurrent Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
         
     | 
    
        data/lib/concurrent.rb
    CHANGED
    
    | 
         @@ -1,12 +1,21 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'concurrent/version'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'concurrent/atomic'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'concurrent/count_down_latch'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'concurrent/condition'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'concurrent/copy_on_notify_observer_set'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'concurrent/copy_on_write_observer_set'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'concurrent/safe_task_executor'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'concurrent/ivar'
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       3 
11 
     | 
    
         
             
            require 'concurrent/actor'
         
     | 
| 
       4 
12 
     | 
    
         
             
            require 'concurrent/agent'
         
     | 
| 
       5 
     | 
    
         
            -
            require 'concurrent/contract'
         
     | 
| 
       6 
13 
     | 
    
         
             
            require 'concurrent/channel'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'concurrent/dataflow'
         
     | 
| 
       7 
15 
     | 
    
         
             
            require 'concurrent/dereferenceable'
         
     | 
| 
       8 
16 
     | 
    
         
             
            require 'concurrent/event'
         
     | 
| 
       9 
17 
     | 
    
         
             
            require 'concurrent/future'
         
     | 
| 
      
 18 
     | 
    
         
            +
            require 'concurrent/mvar'
         
     | 
| 
       10 
19 
     | 
    
         
             
            require 'concurrent/obligation'
         
     | 
| 
       11 
20 
     | 
    
         
             
            require 'concurrent/postable'
         
     | 
| 
       12 
21 
     | 
    
         
             
            require 'concurrent/promise'
         
     | 
| 
         @@ -14,6 +23,7 @@ require 'concurrent/runnable' 
     | 
|
| 
       14 
23 
     | 
    
         
             
            require 'concurrent/scheduled_task'
         
     | 
| 
       15 
24 
     | 
    
         
             
            require 'concurrent/stoppable'
         
     | 
| 
       16 
25 
     | 
    
         
             
            require 'concurrent/supervisor'
         
     | 
| 
      
 26 
     | 
    
         
            +
            require 'concurrent/thread_local_var'
         
     | 
| 
       17 
27 
     | 
    
         
             
            require 'concurrent/timer_task'
         
     | 
| 
       18 
28 
     | 
    
         
             
            require 'concurrent/utilities'
         
     | 
| 
       19 
29 
     | 
    
         | 
| 
         @@ -23,8 +33,6 @@ require 'concurrent/cached_thread_pool' 
     | 
|
| 
       23 
33 
     | 
    
         
             
            require 'concurrent/fixed_thread_pool'
         
     | 
| 
       24 
34 
     | 
    
         
             
            require 'concurrent/immediate_executor'
         
     | 
| 
       25 
35 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
            require 'concurrent/event_machine_defer_proxy' if defined?(EventMachine)
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
36 
     | 
    
         
             
            # Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
         
     | 
| 
       29 
37 
     | 
    
         
             
            # F#, C#, Java, and classic concurrency patterns.
         
     | 
| 
       30 
38 
     | 
    
         
             
            # 
         
     | 
    
        data/lib/concurrent/actor.rb
    CHANGED
    
    | 
         @@ -19,36 +19,36 @@ module Concurrent 
     | 
|
| 
       19 
19 
     | 
    
         
             
              # 
         
     | 
| 
       20 
20 
     | 
    
         
             
              #   An independent, concurrent, single-purpose, computational entity that communicates exclusively via message passing.
         
     | 
| 
       21 
21 
     | 
    
         
             
              # 
         
     | 
| 
       22 
     | 
    
         
            -
              # The  
     | 
| 
      
 22 
     | 
    
         
            +
              # The +Concurrent::Actor+ class in this library is based solely on the
         
     | 
| 
       23 
23 
     | 
    
         
             
              # {http://www.scala-lang.org/api/current/index.html#scala.actors.Actor Actor} trait
         
     | 
| 
       24 
24 
     | 
    
         
             
              # defined in the Scala standard library. It does not implement all the features of
         
     | 
| 
       25 
     | 
    
         
            -
              # Scala's  
     | 
| 
      
 25 
     | 
    
         
            +
              # Scala's +Actor+ but its behavior for what *has* been implemented is nearly identical.
         
     | 
| 
       26 
26 
     | 
    
         
             
              # The excluded features mostly deal with Scala's message semantics, strong typing,
         
     | 
| 
       27 
27 
     | 
    
         
             
              # and other characteristics of Scala that don't really apply to Ruby.
         
     | 
| 
       28 
28 
     | 
    
         
             
              # 
         
     | 
| 
       29 
     | 
    
         
            -
              # Unlike  
     | 
| 
      
 29 
     | 
    
         
            +
              # Unlike many of the abstractions in this library, +Actor+ takes an *object-oriented*
         
     | 
| 
       30 
30 
     | 
    
         
             
              # approach to asynchronous concurrency, rather than a *functional programming*
         
     | 
| 
       31 
31 
     | 
    
         
             
              # approach.
         
     | 
| 
       32 
32 
     | 
    
         
             
              #   
         
     | 
| 
       33 
     | 
    
         
            -
              # Because  
     | 
| 
       34 
     | 
    
         
            -
              # the  
     | 
| 
       35 
     | 
    
         
            -
              #  
     | 
| 
       36 
     | 
    
         
            -
              # which override it. Generally speaking,  
     | 
| 
      
 33 
     | 
    
         
            +
              # Because +Actor+ mixes in the +Concurrent::Runnable+ module subclasses have access to
         
     | 
| 
      
 34 
     | 
    
         
            +
              # the +#on_error+ method and can override it to implement custom error handling. The
         
     | 
| 
      
 35 
     | 
    
         
            +
              # +Actor+ base class does not use +#on_error+ so as to avoid conflit with subclasses
         
     | 
| 
      
 36 
     | 
    
         
            +
              # which override it. Generally speaking, +#on_error+ should not be used. The +Actor+
         
     | 
| 
       37 
37 
     | 
    
         
             
              # base class provides concictent, reliable, and robust error handling already, and
         
     | 
| 
       38 
38 
     | 
    
         
             
              # error handling specifics are tied to the message posting method. Incorrect behavior
         
     | 
| 
       39 
     | 
    
         
            -
              # in an  
     | 
| 
      
 39 
     | 
    
         
            +
              # in an +#on_error+ override can lead to inconsistent +Actor+ behavior that may lead
         
     | 
| 
       40 
40 
     | 
    
         
             
              # to confusion and difficult debugging.
         
     | 
| 
       41 
41 
     | 
    
         
             
              #   
         
     | 
| 
       42 
     | 
    
         
            -
              # The  
     | 
| 
      
 42 
     | 
    
         
            +
              # The +Actor+ superclass mixes in the Ruby standard library
         
     | 
| 
       43 
43 
     | 
    
         
             
              # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html Observable}
         
     | 
| 
       44 
44 
     | 
    
         
             
              # module to provide consistent callbacks upon message processing completion. The normal
         
     | 
| 
       45 
     | 
    
         
            -
              #  
     | 
| 
       46 
     | 
    
         
            -
              # is added to an  
     | 
| 
      
 45 
     | 
    
         
            +
              # +Observable+ methods, including +#add_observer+ behave normally. Once an observer
         
     | 
| 
      
 46 
     | 
    
         
            +
              # is added to an +Actor+ it will be notified of all messages processed *after*
         
     | 
| 
       47 
47 
     | 
    
         
             
              # addition. Notification will *not* occur for any messages that have already been
         
     | 
| 
       48 
48 
     | 
    
         
             
              # processed.
         
     | 
| 
       49 
49 
     | 
    
         
             
              #   
         
     | 
| 
       50 
50 
     | 
    
         
             
              # Observers will be notified regardless of whether the message processing is successful
         
     | 
| 
       51 
     | 
    
         
            -
              # or not. The  
     | 
| 
      
 51 
     | 
    
         
            +
              # or not. The +#update+ method of the observer will receive four arguments. The
         
     | 
| 
       52 
52 
     | 
    
         
             
              # appropriate method signature is:
         
     | 
| 
       53 
53 
     | 
    
         
             
              #   
         
     | 
| 
       54 
54 
     | 
    
         
             
              #   def update(time, message, result, reason)
         
     | 
| 
         @@ -57,8 +57,8 @@ module Concurrent 
     | 
|
| 
       57 
57 
     | 
    
         
             
              #   
         
     | 
| 
       58 
58 
     | 
    
         
             
              # * The time that message processing was completed
         
     | 
| 
       59 
59 
     | 
    
         
             
              # * An array containing all elements of the original message, in order
         
     | 
| 
       60 
     | 
    
         
            -
              # * The result of the call to  
     | 
| 
       61 
     | 
    
         
            -
              # * Any exception raised by  
     | 
| 
      
 60 
     | 
    
         
            +
              # * The result of the call to +#act+ (will be +nil+ if an exception was raised)
         
     | 
| 
      
 61 
     | 
    
         
            +
              # * Any exception raised by +#act+ (or +nil+ if message processing was successful)
         
     | 
| 
       62 
62 
     | 
    
         
             
              #
         
     | 
| 
       63 
63 
     | 
    
         
             
              # @example Actor Ping Pong
         
     | 
| 
       64 
64 
     | 
    
         
             
              #   class Ping < Concurrent::Actor
         
     | 
| 
         @@ -140,10 +140,10 @@ module Concurrent 
     | 
|
| 
       140 
140 
     | 
    
         | 
| 
       141 
141 
     | 
    
         
             
                # Create a pool of actors that share a common mailbox.
         
     | 
| 
       142 
142 
     | 
    
         
             
                #   
         
     | 
| 
       143 
     | 
    
         
            -
                # Every  
     | 
| 
       144 
     | 
    
         
            -
                # to manage all the messages being sent to an  
     | 
| 
       145 
     | 
    
         
            -
                # is a collection of  
     | 
| 
       146 
     | 
    
         
            -
                # Messages from other threads are all sent to a single queue against which all  
     | 
| 
      
 143 
     | 
    
         
            +
                # Every +Actor+ instance operates on its own thread. When one thread isn't enough capacity
         
     | 
| 
      
 144 
     | 
    
         
            +
                # to manage all the messages being sent to an +Actor+ a *pool* can be used instead. A pool
         
     | 
| 
      
 145 
     | 
    
         
            +
                # is a collection of +Actor+ instances, all of the same type, that shate a message queue.
         
     | 
| 
      
 146 
     | 
    
         
            +
                # Messages from other threads are all sent to a single queue against which all +Actor+s
         
     | 
| 
       147 
147 
     | 
    
         
             
                # load balance.
         
     | 
| 
       148 
148 
     | 
    
         
             
                #
         
     | 
| 
       149 
149 
     | 
    
         
             
                # @param [Integer] count the number of actors in the pool
         
     | 
| 
         @@ -152,7 +152,7 @@ module Concurrent 
     | 
|
| 
       152 
152 
     | 
    
         
             
                # @return [Array] two-element array with the shared mailbox as the first element
         
     | 
| 
       153 
153 
     | 
    
         
             
                #   and an array of actors as the second element
         
     | 
| 
       154 
154 
     | 
    
         
             
                #
         
     | 
| 
       155 
     | 
    
         
            -
                # @raise ArgumentError if  
     | 
| 
      
 155 
     | 
    
         
            +
                # @raise ArgumentError if +count+ is zero or less
         
     | 
| 
       156 
156 
     | 
    
         
             
                #
         
     | 
| 
       157 
157 
     | 
    
         
             
                # @example
         
     | 
| 
       158 
158 
     | 
    
         
             
                #   class EchoActor < Concurrent::Actor
         
     | 
| 
         @@ -191,35 +191,35 @@ module Concurrent 
     | 
|
| 
       191 
191 
     | 
    
         | 
| 
       192 
192 
     | 
    
         
             
                protected
         
     | 
| 
       193 
193 
     | 
    
         | 
| 
       194 
     | 
    
         
            -
                # Actors are defined by subclassing the  
     | 
| 
       195 
     | 
    
         
            -
                # #act method. The #act method can have any signature/arity but  
     | 
| 
      
 194 
     | 
    
         
            +
                # Actors are defined by subclassing the +Concurrent::Actor+ class and overriding the
         
     | 
| 
      
 195 
     | 
    
         
            +
                # #act method. The #act method can have any signature/arity but +def act(*args)+
         
     | 
| 
       196 
196 
     | 
    
         
             
                # is the most flexible and least error-prone signature. The #act method is called in
         
     | 
| 
       197 
     | 
    
         
            -
                # response to a message being post to the  
     | 
| 
      
 197 
     | 
    
         
            +
                # response to a message being post to the +Actor+ instance (see *Behavior* below).
         
     | 
| 
       198 
198 
     | 
    
         
             
                #
         
     | 
| 
       199 
199 
     | 
    
         
             
                # @param [Array] message one or more arguments representing the message sent to the
         
     | 
| 
       200 
200 
     | 
    
         
             
                #   actor via one of the Concurrent::Postable methods
         
     | 
| 
       201 
201 
     | 
    
         
             
                #
         
     | 
| 
       202 
202 
     | 
    
         
             
                # @return [Object] the result obtained when the message is successfully processed
         
     | 
| 
       203 
203 
     | 
    
         
             
                #
         
     | 
| 
       204 
     | 
    
         
            -
                # @raise NotImplementedError unless overridden in the  
     | 
| 
      
 204 
     | 
    
         
            +
                # @raise NotImplementedError unless overridden in the +Actor+ subclass
         
     | 
| 
       205 
205 
     | 
    
         
             
                # 
         
     | 
| 
       206 
206 
     | 
    
         
             
                # @!visibility public
         
     | 
| 
       207 
207 
     | 
    
         
             
                def act(*message)
         
     | 
| 
       208 
208 
     | 
    
         
             
                  raise NotImplementedError.new("#{self.class} does not implement #act")
         
     | 
| 
       209 
209 
     | 
    
         
             
                end
         
     | 
| 
       210 
210 
     | 
    
         | 
| 
       211 
     | 
    
         
            -
                #  
     | 
| 
      
 211 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
       212 
212 
     | 
    
         
             
                def on_run # :nodoc:
         
     | 
| 
       213 
213 
     | 
    
         
             
                  queue.clear
         
     | 
| 
       214 
214 
     | 
    
         
             
                end
         
     | 
| 
       215 
215 
     | 
    
         | 
| 
       216 
     | 
    
         
            -
                #  
     | 
| 
      
 216 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
       217 
217 
     | 
    
         
             
                def on_stop # :nodoc:
         
     | 
| 
       218 
218 
     | 
    
         
             
                  queue.clear
         
     | 
| 
       219 
219 
     | 
    
         
             
                  queue.push(:stop)
         
     | 
| 
       220 
220 
     | 
    
         
             
                end
         
     | 
| 
       221 
221 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
                #  
     | 
| 
      
 222 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
       223 
223 
     | 
    
         
             
                def on_task # :nodoc:
         
     | 
| 
       224 
224 
     | 
    
         
             
                  package = queue.pop
         
     | 
| 
       225 
225 
     | 
    
         
             
                  return if package == :stop
         
     | 
| 
         @@ -235,8 +235,8 @@ module Concurrent 
     | 
|
| 
       235 
235 
     | 
    
         
             
                    if notifier.is_a?(Event) && ! notifier.set?
         
     | 
| 
       236 
236 
     | 
    
         
             
                      package.handler.push(result || ex)
         
     | 
| 
       237 
237 
     | 
    
         
             
                      package.notifier.set
         
     | 
| 
       238 
     | 
    
         
            -
                    elsif package.handler.is_a?( 
     | 
| 
       239 
     | 
    
         
            -
                      package.handler.complete(result, ex)
         
     | 
| 
      
 238 
     | 
    
         
            +
                    elsif package.handler.is_a?(IVar)
         
     | 
| 
      
 239 
     | 
    
         
            +
                      package.handler.complete(! result.nil?, result, ex)
         
     | 
| 
       240 
240 
     | 
    
         
             
                    elsif package.handler.respond_to?(:post) && ex.nil?
         
     | 
| 
       241 
241 
     | 
    
         
             
                      package.handler.post(result)
         
     | 
| 
       242 
242 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -246,7 +246,7 @@ module Concurrent 
     | 
|
| 
       246 
246 
     | 
    
         
             
                  end
         
     | 
| 
       247 
247 
     | 
    
         
             
                end
         
     | 
| 
       248 
248 
     | 
    
         | 
| 
       249 
     | 
    
         
            -
                #  
     | 
| 
      
 249 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
       250 
250 
     | 
    
         
             
                def on_error(time, msg, ex) # :nodoc:
         
     | 
| 
       251 
251 
     | 
    
         
             
                end
         
     | 
| 
       252 
252 
     | 
    
         
             
              end
         
     | 
    
        data/lib/concurrent/agent.rb
    CHANGED
    
    | 
         @@ -13,83 +13,165 @@ module Concurrent 
     | 
|
| 
       13 
13 
     | 
    
         
             
              # will receive the current value of the agent as its sole parameter. The return value of the block
         
     | 
| 
       14 
14 
     | 
    
         
             
              # will become the new value of the agent. Agents support two error handling modes: fail and continue.
         
     | 
| 
       15 
15 
     | 
    
         
             
              # A good example of an agent is a shared incrementing counter, such as the score in a video game.
         
     | 
| 
      
 16 
     | 
    
         
            +
              #
         
     | 
| 
      
 17 
     | 
    
         
            +
              # @example Basic usage
         
     | 
| 
      
 18 
     | 
    
         
            +
              #   score = Concurrent::Agent.new(10)
         
     | 
| 
      
 19 
     | 
    
         
            +
              #   score.value #=> 10
         
     | 
| 
      
 20 
     | 
    
         
            +
              #   
         
     | 
| 
      
 21 
     | 
    
         
            +
              #   score << proc{|current| current + 100 }
         
     | 
| 
      
 22 
     | 
    
         
            +
              #   sleep(0.1)
         
     | 
| 
      
 23 
     | 
    
         
            +
              #   score.value #=> 110
         
     | 
| 
      
 24 
     | 
    
         
            +
              #   
         
     | 
| 
      
 25 
     | 
    
         
            +
              #   score << proc{|current| current * 2 }
         
     | 
| 
      
 26 
     | 
    
         
            +
              #   sleep(0.1)
         
     | 
| 
      
 27 
     | 
    
         
            +
              #   score.value #=> 220
         
     | 
| 
      
 28 
     | 
    
         
            +
              #   
         
     | 
| 
      
 29 
     | 
    
         
            +
              #   score << proc{|current| current - 50 }
         
     | 
| 
      
 30 
     | 
    
         
            +
              #   sleep(0.1)
         
     | 
| 
      
 31 
     | 
    
         
            +
              #   score.value #=> 170
         
     | 
| 
      
 32 
     | 
    
         
            +
              #
         
     | 
| 
      
 33 
     | 
    
         
            +
              # @!attribute [r] timeout
         
     | 
| 
      
 34 
     | 
    
         
            +
              #   @return [Fixnum] the maximum number of seconds before an update is cancelled
         
     | 
| 
       16 
35 
     | 
    
         
             
              class Agent
         
     | 
| 
       17 
     | 
    
         
            -
                include Observable
         
     | 
| 
       18 
36 
     | 
    
         
             
                include Dereferenceable
         
     | 
| 
       19 
37 
     | 
    
         
             
                include UsesGlobalThreadPool
         
     | 
| 
       20 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                # The default timeout value (in seconds); used when no timeout option
         
     | 
| 
      
 40 
     | 
    
         
            +
                # is given at initialization
         
     | 
| 
       21 
41 
     | 
    
         
             
                TIMEOUT = 5
         
     | 
| 
       22 
42 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                attr_reader :initial
         
     | 
| 
       24 
43 
     | 
    
         
             
                attr_reader :timeout
         
     | 
| 
       25 
44 
     | 
    
         | 
| 
      
 45 
     | 
    
         
            +
                # Initialize a new Agent with the given initial value and provided options.
         
     | 
| 
      
 46 
     | 
    
         
            +
                #
         
     | 
| 
      
 47 
     | 
    
         
            +
                # @param [Object] initial the initial value
         
     | 
| 
      
 48 
     | 
    
         
            +
                # @param [Hash] opts the options used to define the behavior at update and deref
         
     | 
| 
      
 49 
     | 
    
         
            +
                # @option opts [Fixnum] :timeout (TIMEOUT) maximum number of seconds before an update is cancelled
         
     | 
| 
      
 50 
     | 
    
         
            +
                # @option opts [String] :dup_on_deref (false) call +#dup+ before returning the data
         
     | 
| 
      
 51 
     | 
    
         
            +
                # @option opts [String] :freeze_on_deref (false) call +#freeze+ before returning the data
         
     | 
| 
      
 52 
     | 
    
         
            +
                # @option opts [String] :copy_on_deref (nil) call the given +Proc+ passing the internal value and
         
     | 
| 
      
 53 
     | 
    
         
            +
                #   returning the value returned from the proc
         
     | 
| 
       26 
54 
     | 
    
         
             
                def initialize(initial, opts = {})
         
     | 
| 
       27 
55 
     | 
    
         
             
                  @value = initial
         
     | 
| 
       28 
56 
     | 
    
         
             
                  @rescuers = []
         
     | 
| 
       29 
     | 
    
         
            -
                  @validator =  
     | 
| 
       30 
     | 
    
         
            -
                  @timeout = opts 
     | 
| 
      
 57 
     | 
    
         
            +
                  @validator = Proc.new { |result| true }
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @timeout = opts.fetch(:timeout, TIMEOUT).freeze
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @observers = CopyOnWriteObserverSet.new
         
     | 
| 
       31 
60 
     | 
    
         
             
                  init_mutex
         
     | 
| 
       32 
61 
     | 
    
         
             
                  set_deref_options(opts)
         
     | 
| 
       33 
62 
     | 
    
         
             
                end
         
     | 
| 
       34 
63 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                 
     | 
| 
      
 64 
     | 
    
         
            +
                # Specifies a block operation to be performed when an update operation raises
         
     | 
| 
      
 65 
     | 
    
         
            +
                # an exception. Rescue blocks will be checked in order they were added. The first
         
     | 
| 
      
 66 
     | 
    
         
            +
                # block for which the raised exception "is-a" subclass of the given +clazz+ will
         
     | 
| 
      
 67 
     | 
    
         
            +
                # be called. If no +clazz+ is given the block will match any caught exception.
         
     | 
| 
      
 68 
     | 
    
         
            +
                # This behavior is intended to be identical to Ruby's +begin/rescue/end+ behavior.
         
     | 
| 
      
 69 
     | 
    
         
            +
                # Any number of rescue handlers can be added. If no rescue handlers are added then
         
     | 
| 
      
 70 
     | 
    
         
            +
                # caught exceptions will be suppressed.
         
     | 
| 
      
 71 
     | 
    
         
            +
                #
         
     | 
| 
      
 72 
     | 
    
         
            +
                # @param [Exception] clazz the class of exception to catch
         
     | 
| 
      
 73 
     | 
    
         
            +
                # @yield the block to be called when a matching exception is caught
         
     | 
| 
      
 74 
     | 
    
         
            +
                # @yieldparam [StandardError] ex the caught exception
         
     | 
| 
      
 75 
     | 
    
         
            +
                #
         
     | 
| 
      
 76 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 77 
     | 
    
         
            +
                #   score = Concurrent::Agent.new(0).
         
     | 
| 
      
 78 
     | 
    
         
            +
                #             rescue(NoMethodError){|ex| puts "Bam!" }.
         
     | 
| 
      
 79 
     | 
    
         
            +
                #             rescue(ArgumentError){|ex| puts "Pow!" }.
         
     | 
| 
      
 80 
     | 
    
         
            +
                #             rescue{|ex| puts "Boom!" }
         
     | 
| 
      
 81 
     | 
    
         
            +
                #   
         
     | 
| 
      
 82 
     | 
    
         
            +
                #   score << proc{|current| raise ArgumentError }
         
     | 
| 
      
 83 
     | 
    
         
            +
                #   sleep(0.1)
         
     | 
| 
      
 84 
     | 
    
         
            +
                #   #=> puts "Pow!"
         
     | 
| 
      
 85 
     | 
    
         
            +
                def rescue(clazz = StandardError, &block)
         
     | 
| 
       36 
86 
     | 
    
         
             
                  unless block.nil?
         
     | 
| 
       37 
87 
     | 
    
         
             
                    mutex.synchronize do
         
     | 
| 
       38 
88 
     | 
    
         
             
                      @rescuers << Rescuer.new(clazz, block)
         
     | 
| 
       39 
89 
     | 
    
         
             
                    end
         
     | 
| 
       40 
90 
     | 
    
         
             
                  end
         
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
      
 91 
     | 
    
         
            +
                  self
         
     | 
| 
       42 
92 
     | 
    
         
             
                end
         
     | 
| 
       43 
93 
     | 
    
         
             
                alias_method :catch, :rescue
         
     | 
| 
       44 
94 
     | 
    
         
             
                alias_method :on_error, :rescue
         
     | 
| 
       45 
95 
     | 
    
         | 
| 
      
 96 
     | 
    
         
            +
                # A block operation to be performed after every update to validate if the new
         
     | 
| 
      
 97 
     | 
    
         
            +
                # value is valid. If the new value is not valid then the current value is not
         
     | 
| 
      
 98 
     | 
    
         
            +
                # updated. If no validator is provided then all updates are considered valid.
         
     | 
| 
      
 99 
     | 
    
         
            +
                #
         
     | 
| 
      
 100 
     | 
    
         
            +
                # @yield the block to be called after every update operation to determine if
         
     | 
| 
      
 101 
     | 
    
         
            +
                #   the result is valid
         
     | 
| 
      
 102 
     | 
    
         
            +
                # @yieldparam [Object] value the result of the last update operation
         
     | 
| 
      
 103 
     | 
    
         
            +
                # @yieldreturn [Boolean] true if the value is valid else false
         
     | 
| 
       46 
104 
     | 
    
         
             
                def validate(&block)
         
     | 
| 
       47 
105 
     | 
    
         
             
                  @validator = block unless block.nil?
         
     | 
| 
       48 
     | 
    
         
            -
                   
     | 
| 
      
 106 
     | 
    
         
            +
                  self
         
     | 
| 
       49 
107 
     | 
    
         
             
                end
         
     | 
| 
       50 
108 
     | 
    
         
             
                alias_method :validates, :validate
         
     | 
| 
       51 
109 
     | 
    
         
             
                alias_method :validate_with, :validate
         
     | 
| 
       52 
110 
     | 
    
         
             
                alias_method :validates_with, :validate
         
     | 
| 
       53 
111 
     | 
    
         | 
| 
      
 112 
     | 
    
         
            +
                # Update the current value with the result of the given block operation
         
     | 
| 
      
 113 
     | 
    
         
            +
                #
         
     | 
| 
      
 114 
     | 
    
         
            +
                # @yield the operation to be performed with the current value in order to calculate
         
     | 
| 
      
 115 
     | 
    
         
            +
                #   the new value
         
     | 
| 
      
 116 
     | 
    
         
            +
                # @yieldparam [Object] value the current value
         
     | 
| 
      
 117 
     | 
    
         
            +
                # @yieldreturn [Object] the new value
         
     | 
| 
       54 
118 
     | 
    
         
             
                def post(&block)
         
     | 
| 
       55 
119 
     | 
    
         
             
                  Agent.thread_pool.post{ work(&block) } unless block.nil?
         
     | 
| 
       56 
120 
     | 
    
         
             
                end
         
     | 
| 
       57 
121 
     | 
    
         | 
| 
      
 122 
     | 
    
         
            +
                # Update the current value with the result of the given block operation
         
     | 
| 
      
 123 
     | 
    
         
            +
                #
         
     | 
| 
      
 124 
     | 
    
         
            +
                # @yield the operation to be performed with the current value in order to calculate
         
     | 
| 
      
 125 
     | 
    
         
            +
                #   the new value
         
     | 
| 
      
 126 
     | 
    
         
            +
                # @yieldparam [Object] value the current value
         
     | 
| 
      
 127 
     | 
    
         
            +
                # @yieldreturn [Object] the new value
         
     | 
| 
       58 
128 
     | 
    
         
             
                def <<(block)
         
     | 
| 
       59 
129 
     | 
    
         
             
                  self.post(&block)
         
     | 
| 
       60 
     | 
    
         
            -
                   
     | 
| 
      
 130 
     | 
    
         
            +
                  self
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                def add_observer(observer, func=:update)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  @observers.add_observer(observer, func)
         
     | 
| 
       61 
135 
     | 
    
         
             
                end
         
     | 
| 
       62 
136 
     | 
    
         | 
| 
       63 
137 
     | 
    
         
             
                alias_method :add_watch, :add_observer
         
     | 
| 
       64 
138 
     | 
    
         | 
| 
      
 139 
     | 
    
         
            +
                def delete_observer(observer)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  @observers.delete_observer(observer)
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
       65 
143 
     | 
    
         
             
                private
         
     | 
| 
       66 
144 
     | 
    
         | 
| 
       67 
     | 
    
         
            -
                #  
     | 
| 
       68 
     | 
    
         
            -
                Rescuer = Struct.new(:clazz, :block)
         
     | 
| 
      
 145 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 146 
     | 
    
         
            +
                Rescuer = Struct.new(:clazz, :block) # :nodoc:
         
     | 
| 
       69 
147 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
                #  
     | 
| 
      
 148 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
       71 
149 
     | 
    
         
             
                def try_rescue(ex) # :nodoc:
         
     | 
| 
       72 
150 
     | 
    
         
             
                  rescuer = mutex.synchronize do
         
     | 
| 
       73 
     | 
    
         
            -
                    @rescuers.find{|r|  
     | 
| 
      
 151 
     | 
    
         
            +
                    @rescuers.find{|r| ex.is_a?(r.clazz) }
         
     | 
| 
       74 
152 
     | 
    
         
             
                  end
         
     | 
| 
       75 
153 
     | 
    
         
             
                  rescuer.block.call(ex) if rescuer
         
     | 
| 
       76 
154 
     | 
    
         
             
                rescue Exception => ex
         
     | 
| 
       77 
155 
     | 
    
         
             
                  # supress
         
     | 
| 
       78 
156 
     | 
    
         
             
                end
         
     | 
| 
       79 
157 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
                #  
     | 
| 
      
 158 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
       81 
159 
     | 
    
         
             
                def work(&handler) # :nodoc:
         
     | 
| 
       82 
160 
     | 
    
         
             
                  begin
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                    should_notify = false
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
       83 
164 
     | 
    
         
             
                    mutex.synchronize do
         
     | 
| 
       84 
165 
     | 
    
         
             
                      result = Concurrent::timeout(@timeout) do
         
     | 
| 
       85 
166 
     | 
    
         
             
                        handler.call(@value)
         
     | 
| 
       86 
167 
     | 
    
         
             
                      end
         
     | 
| 
       87 
     | 
    
         
            -
                      if @validator. 
     | 
| 
      
 168 
     | 
    
         
            +
                      if @validator.call(result)
         
     | 
| 
       88 
169 
     | 
    
         
             
                        @value = result
         
     | 
| 
       89 
     | 
    
         
            -
                         
     | 
| 
      
 170 
     | 
    
         
            +
                        should_notify = true
         
     | 
| 
       90 
171 
     | 
    
         
             
                      end
         
     | 
| 
       91 
172 
     | 
    
         
             
                    end
         
     | 
| 
       92 
     | 
    
         
            -
                     
     | 
| 
      
 173 
     | 
    
         
            +
                    time = Time.now
         
     | 
| 
      
 174 
     | 
    
         
            +
                    @observers.notify_observers{ [time, self.value] } if should_notify
         
     | 
| 
       93 
175 
     | 
    
         
             
                  rescue Exception => ex
         
     | 
| 
       94 
176 
     | 
    
         
             
                    try_rescue(ex)
         
     | 
| 
       95 
177 
     | 
    
         
             
                  end
         
     |