concurrent-ruby 0.6.0.pre.2 → 0.6.0

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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -8
  3. data/lib/concurrent.rb +2 -0
  4. data/lib/concurrent/actor/actor.rb +3 -3
  5. data/lib/concurrent/actor/postable.rb +1 -1
  6. data/lib/concurrent/actors.rb +0 -3
  7. data/lib/concurrent/actress.rb +75 -0
  8. data/lib/concurrent/actress/ad_hoc.rb +14 -0
  9. data/lib/concurrent/actress/context.rb +96 -0
  10. data/lib/concurrent/actress/core.rb +204 -0
  11. data/lib/concurrent/actress/core_delegations.rb +37 -0
  12. data/lib/concurrent/actress/doc.md +53 -0
  13. data/lib/concurrent/actress/envelope.rb +25 -0
  14. data/lib/concurrent/actress/errors.rb +14 -0
  15. data/lib/concurrent/actress/reference.rb +64 -0
  16. data/lib/concurrent/actress/type_check.rb +48 -0
  17. data/lib/concurrent/agent.rb +20 -11
  18. data/lib/concurrent/async.rb +54 -25
  19. data/lib/concurrent/atomic/atomic.rb +48 -0
  20. data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
  21. data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
  22. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
  23. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
  24. data/lib/concurrent/atomic/event.rb +11 -16
  25. data/lib/concurrent/atomics.rb +1 -0
  26. data/lib/concurrent/channel/channel.rb +4 -2
  27. data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
  28. data/lib/concurrent/configuration.rb +59 -47
  29. data/lib/concurrent/delay.rb +28 -12
  30. data/lib/concurrent/dereferenceable.rb +6 -6
  31. data/lib/concurrent/errors.rb +30 -0
  32. data/lib/concurrent/executor/executor.rb +11 -4
  33. data/lib/concurrent/executor/immediate_executor.rb +1 -0
  34. data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
  35. data/lib/concurrent/executor/one_by_one.rb +24 -12
  36. data/lib/concurrent/executor/per_thread_executor.rb +1 -0
  37. data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
  38. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
  39. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
  40. data/lib/concurrent/executor/timer_set.rb +1 -1
  41. data/lib/concurrent/future.rb +0 -2
  42. data/lib/concurrent/ivar.rb +31 -6
  43. data/lib/concurrent/logging.rb +17 -0
  44. data/lib/concurrent/mvar.rb +45 -0
  45. data/lib/concurrent/obligation.rb +61 -20
  46. data/lib/concurrent/observable.rb +7 -0
  47. data/lib/concurrent/promise.rb +1 -0
  48. data/lib/concurrent/runnable.rb +2 -2
  49. data/lib/concurrent/supervisor.rb +1 -2
  50. data/lib/concurrent/timer_task.rb +17 -13
  51. data/lib/concurrent/tvar.rb +113 -73
  52. data/lib/concurrent/utility/processor_count.rb +141 -116
  53. data/lib/concurrent/utility/timeout.rb +4 -5
  54. data/lib/concurrent/version.rb +1 -1
  55. data/spec/concurrent/actor/postable_shared.rb +1 -1
  56. data/spec/concurrent/actress_spec.rb +191 -0
  57. data/spec/concurrent/async_spec.rb +35 -3
  58. data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
  59. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
  60. data/spec/concurrent/atomic/atomic_spec.rb +133 -0
  61. data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
  62. data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
  63. data/spec/concurrent/configuration_spec.rb +5 -2
  64. data/spec/concurrent/delay_spec.rb +14 -0
  65. data/spec/concurrent/exchanger_spec.rb +4 -9
  66. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
  67. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
  68. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
  69. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
  70. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
  71. data/spec/concurrent/ivar_spec.rb +2 -2
  72. data/spec/concurrent/obligation_spec.rb +113 -24
  73. data/spec/concurrent/observable_shared.rb +4 -0
  74. data/spec/concurrent/observable_spec.rb +8 -3
  75. data/spec/concurrent/runnable_spec.rb +2 -2
  76. data/spec/concurrent/scheduled_task_spec.rb +1 -0
  77. data/spec/concurrent/supervisor_spec.rb +26 -11
  78. data/spec/concurrent/timer_task_spec.rb +36 -35
  79. data/spec/concurrent/tvar_spec.rb +1 -1
  80. data/spec/concurrent/utility/timer_spec.rb +8 -8
  81. data/spec/spec_helper.rb +8 -18
  82. data/spec/support/example_group_extensions.rb +48 -0
  83. metadata +23 -16
  84. data/lib/concurrent/actor/actor_context.rb +0 -77
  85. data/lib/concurrent/actor/actor_ref.rb +0 -67
  86. data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
  87. data/lib/concurrent_ruby_ext.bundle +0 -0
  88. data/spec/concurrent/actor/actor_context_spec.rb +0 -29
  89. data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
  90. data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
  91. data/spec/support/functions.rb +0 -25
@@ -0,0 +1,48 @@
1
+ module Concurrent
2
+
3
+ class MutexAtomic
4
+
5
+ def initialize(init = nil)
6
+ @value = init
7
+ @mutex = Mutex.new
8
+ end
9
+
10
+ def value
11
+ @mutex.lock
12
+ @value
13
+ ensure
14
+ @mutex.unlock
15
+ end
16
+
17
+ def value=(value)
18
+ @mutex.lock
19
+ @value = value
20
+ ensure
21
+ @mutex.unlock
22
+ end
23
+
24
+ def modify
25
+ @mutex.lock
26
+ result = yield @value
27
+ @value = result
28
+ ensure
29
+ @mutex.unlock
30
+ end
31
+
32
+ def compare_and_set(expect, update)
33
+ @mutex.lock
34
+ if @value == expect
35
+ @value = update
36
+ true
37
+ else
38
+ false
39
+ end
40
+ ensure
41
+ @mutex.unlock
42
+ end
43
+ end
44
+
45
+ class Atomic < MutexAtomic
46
+ end
47
+
48
+ end
@@ -14,7 +14,7 @@ module Concurrent
14
14
  #
15
15
  # Creates a new `AtomicBoolean` with the given initial value.
16
16
  #
17
- # @param [Boolean] init the initial value
17
+ # @param [Boolean] initial the initial value
18
18
  def initialize(initial = false)
19
19
  @value = !!initial
20
20
  @mutex = Mutex.new
@@ -27,9 +27,9 @@ module Concurrent
27
27
  # @return [Boolean] the current value
28
28
  def value
29
29
  @mutex.lock
30
- result = @value
30
+ @value
31
+ ensure
31
32
  @mutex.unlock
32
- result
33
33
  end
34
34
 
35
35
  # @!macro [attach] atomic_boolean_method_value_eq
@@ -42,9 +42,9 @@ module Concurrent
42
42
  def value=(value)
43
43
  @mutex.lock
44
44
  @value = !!value
45
- result = @value
45
+ @value
46
+ ensure
46
47
  @mutex.unlock
47
- result
48
48
  end
49
49
 
50
50
  # @!macro [attach] atomic_boolean_method_is_true
@@ -54,9 +54,9 @@ module Concurrent
54
54
  # @return [Boolean] true if the current value is `true`, else false
55
55
  def true?
56
56
  @mutex.lock
57
- result = @value
57
+ @value
58
+ ensure
58
59
  @mutex.unlock
59
- result
60
60
  end
61
61
 
62
62
  # @!macro [attach] atomic_boolean_method_is_false
@@ -66,9 +66,9 @@ module Concurrent
66
66
  # @return [Boolean] true if the current value is `false`, else false
67
67
  def false?
68
68
  @mutex.lock
69
- result = !@value
69
+ !@value
70
+ ensure
70
71
  @mutex.unlock
71
- result
72
72
  end
73
73
 
74
74
  # @!macro [attach] atomic_boolean_method_make_true
@@ -80,9 +80,9 @@ module Concurrent
80
80
  @mutex.lock
81
81
  old = @value
82
82
  @value = true
83
- @mutex.unlock
84
-
85
83
  !old
84
+ ensure
85
+ @mutex.unlock
86
86
  end
87
87
 
88
88
  # @!macro [attach] atomic_boolean_method_make_false
@@ -94,9 +94,9 @@ module Concurrent
94
94
  @mutex.lock
95
95
  old = @value
96
96
  @value = false
97
- @mutex.unlock
98
-
99
97
  old
98
+ ensure
99
+ @mutex.unlock
100
100
  end
101
101
  end
102
102
 
@@ -29,10 +29,9 @@ module Concurrent
29
29
  # @return [Fixnum] the current value
30
30
  def value
31
31
  @mutex.lock
32
- result = @value
32
+ @value
33
+ ensure
33
34
  @mutex.unlock
34
-
35
- result
36
35
  end
37
36
 
38
37
  # @!macro [attach] atomic_fixnum_method_value_eq
@@ -47,10 +46,9 @@ module Concurrent
47
46
  def value=(value)
48
47
  raise ArgumentError.new('value must be a Fixnum') unless value.is_a?(Fixnum)
49
48
  @mutex.lock
50
- result = @value = value
49
+ @value = value
50
+ ensure
51
51
  @mutex.unlock
52
-
53
- result
54
52
  end
55
53
 
56
54
  # @!macro [attach] atomic_fixnum_method_increment
@@ -61,10 +59,8 @@ module Concurrent
61
59
  def increment
62
60
  @mutex.lock
63
61
  @value += 1
64
- result = @value
62
+ ensure
65
63
  @mutex.unlock
66
-
67
- result
68
64
  end
69
65
 
70
66
  alias_method :up, :increment
@@ -77,10 +73,8 @@ module Concurrent
77
73
  def decrement
78
74
  @mutex.lock
79
75
  @value -= 1
80
- result = @value
76
+ ensure
81
77
  @mutex.unlock
82
-
83
- result
84
78
  end
85
79
 
86
80
  alias_method :down, :decrement
@@ -95,17 +89,15 @@ module Concurrent
95
89
  #
96
90
  # @return [Boolean] true if the value was updated else false
97
91
  def compare_and_set(expect, update)
98
-
99
92
  @mutex.lock
100
93
  if @value == expect
101
94
  @value = update
102
- result = true
95
+ true
103
96
  else
104
- result = false
97
+ false
105
98
  end
99
+ ensure
106
100
  @mutex.unlock
107
-
108
- result
109
101
  end
110
102
  end
111
103
 
@@ -28,11 +28,14 @@ module Concurrent
28
28
  func = :call
29
29
  end
30
30
 
31
- @mutex.lock
32
- @observers[observer] = func
33
- @mutex.unlock
31
+ begin
32
+ @mutex.lock
33
+ @observers[observer] = func
34
+ ensure
35
+ @mutex.unlock
36
+ end
34
37
 
35
- observer
38
+ observer
36
39
  end
37
40
 
38
41
  # @param [Object] observer the observer to remove
@@ -27,13 +27,15 @@ module Concurrent
27
27
  func = :call
28
28
  end
29
29
 
30
- @mutex.lock
31
- new_observers = @observers.dup
32
- new_observers[observer] = func
33
- @observers = new_observers
34
- @mutex.unlock
35
-
36
- observer
30
+ begin
31
+ @mutex.lock
32
+ new_observers = @observers.dup
33
+ new_observers[observer] = func
34
+ @observers = new_observers
35
+ observer
36
+ ensure
37
+ @mutex.unlock
38
+ end
37
39
  end
38
40
 
39
41
  # @param [Object] observer the observer to remove
@@ -43,9 +45,9 @@ module Concurrent
43
45
  new_observers = @observers.dup
44
46
  new_observers.delete(observer)
45
47
  @observers = new_observers
46
- @mutex.unlock
47
-
48
48
  observer
49
+ ensure
50
+ @mutex.unlock
49
51
  end
50
52
 
51
53
  # Deletes all observers
@@ -91,15 +93,15 @@ module Concurrent
91
93
 
92
94
  def observers
93
95
  @mutex.lock
94
- o = @observers
96
+ @observers
97
+ ensure
95
98
  @mutex.unlock
96
-
97
- o
98
99
  end
99
100
 
100
101
  def observers=(new_set)
101
102
  @mutex.lock
102
103
  @observers = new_set
104
+ ensure
103
105
  @mutex.unlock
104
106
  end
105
107
 
@@ -107,9 +109,9 @@ module Concurrent
107
109
  @mutex.lock
108
110
  old_observers = @observers
109
111
  @observers = {}
110
- @mutex.unlock
111
-
112
112
  old_observers
113
+ ensure
114
+ @mutex.unlock
113
115
  end
114
116
  end
115
117
  end
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- require 'concurrent/utilities'
3
2
  require 'concurrent/atomic/condition'
4
3
 
5
4
  module Concurrent
@@ -29,10 +28,9 @@ module Concurrent
29
28
  # @return [Boolean] indicating whether or not the `Event` has been set
30
29
  def set?
31
30
  @mutex.lock
32
- result = @set
31
+ @set
32
+ ensure
33
33
  @mutex.unlock
34
-
35
- result
36
34
  end
37
35
 
38
36
  # Trigger the event, setting the state to `set` and releasing all threads
@@ -45,25 +43,24 @@ module Concurrent
45
43
  @set = true
46
44
  @condition.broadcast
47
45
  end
48
- @mutex.unlock
49
-
50
46
  true
47
+ ensure
48
+ @mutex.unlock
51
49
  end
52
50
 
53
51
  def try?
54
52
  @mutex.lock
55
53
 
56
54
  if @set
57
- result = false
55
+ false
58
56
  else
59
57
  @set = true
60
58
  @condition.broadcast
61
- result = true
59
+ true
62
60
  end
63
61
 
62
+ ensure
64
63
  @mutex.unlock
65
-
66
- result
67
64
  end
68
65
 
69
66
  # Reset a previously set event back to the `unset` state.
@@ -73,9 +70,9 @@ module Concurrent
73
70
  def reset
74
71
  @mutex.lock
75
72
  @set = false
76
- @mutex.unlock
77
-
78
73
  true
74
+ ensure
75
+ @mutex.unlock
79
76
  end
80
77
 
81
78
  # Wait a given number of seconds for the `Event` to be set by another
@@ -93,11 +90,9 @@ module Concurrent
93
90
  end
94
91
  end
95
92
 
96
- result = @set
97
-
93
+ @set
94
+ ensure
98
95
  @mutex.unlock
99
-
100
- result
101
96
  end
102
97
  end
103
98
  end
@@ -1,3 +1,4 @@
1
+ require 'concurrent/atomic/atomic'
1
2
  require 'concurrent/atomic/atomic_boolean'
2
3
  require 'concurrent/atomic/atomic_fixnum'
3
4
  require 'concurrent/atomic/condition'
@@ -1,7 +1,9 @@
1
+ require 'concurrent/ivar'
2
+
1
3
  module Concurrent
2
4
  module Channel
3
5
 
4
- class Probe < IVar
6
+ class Probe < Concurrent::IVar
5
7
 
6
8
  def initialize(value = NO_VALUE, opts = {})
7
9
  super(value, opts)
@@ -36,4 +38,4 @@ module Concurrent
36
38
  result
37
39
  end
38
40
  end
39
- end
41
+ end
@@ -31,7 +31,7 @@ module Concurrent
31
31
  @mutex.synchronize { @buffer.full? }
32
32
  end
33
33
 
34
- # @param [Object] value. This methods blocks until an empty slot is available
34
+ # @param [Object] value the value to be inserted
35
35
  # @return [Boolean] true if value has been inserted, false otherwise
36
36
  def put(value)
37
37
  @mutex.synchronize do
@@ -1,64 +1,45 @@
1
1
  require 'thread'
2
+ require 'concurrent/delay'
3
+ require 'concurrent/errors'
2
4
  require 'concurrent/executor/thread_pool_executor'
3
5
  require 'concurrent/executor/timer_set'
4
6
  require 'concurrent/utility/processor_count'
5
7
 
6
8
  module Concurrent
7
-
8
- # An error class to be raised when errors occur during configuration.
9
- ConfigurationError = Class.new(StandardError)
10
-
11
- class << self
12
- attr_accessor :configuration
13
- end
14
-
15
- # Perform gem-level configuration.
16
- #
17
- # @yield the configuration commands
18
- # @yieldparam [Configuration] the current configuration object
19
- def self.configure
20
- (@mutex ||= Mutex.new).synchronize do
21
- yield(configuration)
22
-
23
- # initialize the global thread pools if necessary
24
- configuration.global_task_pool
25
- configuration.global_operation_pool
26
- configuration.global_timer_set
27
- end
28
- end
9
+ extend Logging
29
10
 
30
11
  # A gem-level configuration object.
31
12
  class Configuration
32
13
 
14
+ # a proc defining how to log messages, its interface has to be:
15
+ # lambda { |level, progname, message = nil, &block| _ }
16
+ attr_accessor :logger
17
+
33
18
  # Create a new configuration object.
34
19
  def initialize
35
- @cores ||= Concurrent::processor_count
20
+ @global_task_pool = Delay.new { new_task_pool }
21
+ @global_operation_pool = Delay.new { new_operation_pool }
22
+ @global_timer_set = Delay.new { Concurrent::TimerSet.new }
23
+ @logger = no_logger
24
+ end
25
+
26
+ # if assigned to {#logger}, it will log nothing.
27
+ def no_logger
28
+ lambda { |level, progname, message = nil, &block| }
36
29
  end
37
30
 
38
31
  # Global thread pool optimized for short *tasks*.
39
32
  #
40
33
  # @return [ThreadPoolExecutor] the thread pool
41
34
  def global_task_pool
42
- @global_task_pool ||= Concurrent::ThreadPoolExecutor.new(
43
- min_threads: [2, @cores].max,
44
- max_threads: [20, @cores * 15].max,
45
- idletime: 2 * 60, # 2 minutes
46
- max_queue: 0, # unlimited
47
- overflow_policy: :abort # raise an exception
48
- )
35
+ @global_task_pool.value
49
36
  end
50
37
 
51
38
  # Global thread pool optimized for long *operations*.
52
39
  #
53
40
  # @return [ThreadPoolExecutor] the thread pool
54
41
  def global_operation_pool
55
- @global_operation_pool ||= Concurrent::ThreadPoolExecutor.new(
56
- min_threads: [2, @cores].max,
57
- max_threads: [2, @cores].max,
58
- idletime: 10 * 60, # 10 minutes
59
- max_queue: [20, @cores * 15].max,
60
- overflow_policy: :abort # raise an exception
61
- )
42
+ @global_operation_pool.value
62
43
  end
63
44
 
64
45
  # Global thread pool optimized for *timers*
@@ -67,7 +48,7 @@ module Concurrent
67
48
  #
68
49
  # @see Concurrent::timer
69
50
  def global_timer_set
70
- @global_timer_set ||= Concurrent::TimerSet.new
51
+ @global_timer_set.value
71
52
  end
72
53
 
73
54
  # Global thread pool optimized for short *tasks*.
@@ -83,10 +64,10 @@ module Concurrent
83
64
  #
84
65
  # @return [ThreadPoolExecutor] the new thread pool
85
66
  #
86
- # @raise [ConfigurationError] if this thread pool has already been set
67
+ # @raise [Concurrent::ConfigurationError] if this thread pool has already been set
87
68
  def global_task_pool=(executor)
88
- raise ConfigurationError.new('global task pool was already set') unless @global_task_pool.nil?
89
- @global_task_pool = executor
69
+ @global_task_pool.reconfigure { executor } or
70
+ raise ConfigurationError.new('global task pool was already set')
90
71
  end
91
72
 
92
73
  # Global thread pool optimized for long *operations*.
@@ -102,11 +83,43 @@ module Concurrent
102
83
  #
103
84
  # @return [ThreadPoolExecutor] the new thread pool
104
85
  #
105
- # @raise [ConfigurationError] if this thread pool has already been set
86
+ # @raise [Concurrent::ConfigurationError] if this thread pool has already been set
106
87
  def global_operation_pool=(executor)
107
- raise ConfigurationError.new('global operation pool was already set') unless @global_operation_pool.nil?
108
- @global_operation_pool = executor
88
+ @global_operation_pool.reconfigure { executor } or
89
+ raise ConfigurationError.new('global operation pool was already set')
109
90
  end
91
+
92
+ def new_task_pool
93
+ Concurrent::ThreadPoolExecutor.new(
94
+ min_threads: [2, Concurrent.processor_count].max,
95
+ max_threads: [20, Concurrent.processor_count * 15].max,
96
+ idletime: 2 * 60, # 2 minutes
97
+ max_queue: 0, # unlimited
98
+ overflow_policy: :abort # raise an exception
99
+ )
100
+ end
101
+
102
+ def new_operation_pool
103
+ Concurrent::ThreadPoolExecutor.new(
104
+ min_threads: [2, Concurrent.processor_count].max,
105
+ max_threads: [2, Concurrent.processor_count].max,
106
+ idletime: 10 * 60, # 10 minutes
107
+ max_queue: [20, Concurrent.processor_count * 15].max,
108
+ overflow_policy: :abort # raise an exception
109
+ )
110
+ end
111
+ end
112
+
113
+ # create the default configuration on load
114
+ @configuration = Configuration.new
115
+ singleton_class.send :attr_reader, :configuration
116
+
117
+ # Perform gem-level configuration.
118
+ #
119
+ # @yield the configuration commands
120
+ # @yieldparam [Configuration] the current configuration object
121
+ def self.configure
122
+ yield(configuration)
110
123
  end
111
124
 
112
125
  private
@@ -125,12 +138,11 @@ module Concurrent
125
138
  executor.kill
126
139
  end
127
140
  true
128
- rescue
141
+ rescue => ex
142
+ log DEBUG, ex
129
143
  false
130
144
  end
131
145
 
132
- # create the default configuration on load
133
- self.configuration = Configuration.new
134
146
 
135
147
  # set exit hook to shutdown global thread pools
136
148
  at_exit do