concurrent-ruby 0.7.0.rc1-x86-mingw32 → 0.7.0.rc2-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +3 -2
  3. data/ext/concurrent_ruby_ext/atomic_boolean.c +48 -0
  4. data/ext/concurrent_ruby_ext/atomic_boolean.h +16 -0
  5. data/ext/concurrent_ruby_ext/atomic_fixnum.c +50 -0
  6. data/ext/concurrent_ruby_ext/atomic_fixnum.h +13 -0
  7. data/ext/concurrent_ruby_ext/atomic_reference.c +44 -44
  8. data/ext/concurrent_ruby_ext/atomic_reference.h +8 -0
  9. data/ext/concurrent_ruby_ext/rb_concurrent.c +32 -3
  10. data/ext/concurrent_ruby_ext/ruby_193_compatible.h +28 -0
  11. data/lib/1.9/concurrent_ruby_ext.so +0 -0
  12. data/lib/2.0/concurrent_ruby_ext.so +0 -0
  13. data/lib/concurrent.rb +2 -1
  14. data/lib/concurrent/actor.rb +104 -0
  15. data/lib/concurrent/{actress → actor}/ad_hoc.rb +2 -3
  16. data/lib/concurrent/actor/behaviour.rb +70 -0
  17. data/lib/concurrent/actor/behaviour/abstract.rb +48 -0
  18. data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
  19. data/lib/concurrent/actor/behaviour/buffer.rb +54 -0
  20. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
  21. data/lib/concurrent/actor/behaviour/executes_context.rb +18 -0
  22. data/lib/concurrent/actor/behaviour/linking.rb +42 -0
  23. data/lib/concurrent/actor/behaviour/pausing.rb +77 -0
  24. data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
  25. data/lib/concurrent/actor/behaviour/sets_results.rb +36 -0
  26. data/lib/concurrent/actor/behaviour/supervised.rb +58 -0
  27. data/lib/concurrent/actor/behaviour/supervising.rb +34 -0
  28. data/lib/concurrent/actor/behaviour/terminates_children.rb +13 -0
  29. data/lib/concurrent/actor/behaviour/termination.rb +54 -0
  30. data/lib/concurrent/actor/context.rb +153 -0
  31. data/lib/concurrent/actor/core.rb +213 -0
  32. data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
  33. data/lib/concurrent/{actress → actor}/envelope.rb +1 -1
  34. data/lib/concurrent/actor/errors.rb +27 -0
  35. data/lib/concurrent/actor/internal_delegations.rb +49 -0
  36. data/lib/concurrent/{actress/core_delegations.rb → actor/public_delegations.rb} +11 -13
  37. data/lib/concurrent/{actress → actor}/reference.rb +25 -8
  38. data/lib/concurrent/actor/root.rb +37 -0
  39. data/lib/concurrent/{actress → actor}/type_check.rb +1 -1
  40. data/lib/concurrent/actor/utills.rb +7 -0
  41. data/lib/concurrent/actor/utils/broadcast.rb +36 -0
  42. data/lib/concurrent/actress.rb +2 -224
  43. data/lib/concurrent/agent.rb +10 -12
  44. data/lib/concurrent/atomic.rb +32 -1
  45. data/lib/concurrent/atomic/atomic_boolean.rb +55 -13
  46. data/lib/concurrent/atomic/atomic_fixnum.rb +54 -16
  47. data/lib/concurrent/atomic/synchronization.rb +51 -0
  48. data/lib/concurrent/atomic/thread_local_var.rb +15 -50
  49. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  50. data/lib/concurrent/atomic_reference/ruby.rb +15 -0
  51. data/lib/concurrent/atomics.rb +1 -0
  52. data/lib/concurrent/channel/unbuffered_channel.rb +2 -1
  53. data/lib/concurrent/configuration.rb +6 -3
  54. data/lib/concurrent/dataflow.rb +20 -3
  55. data/lib/concurrent/delay.rb +23 -31
  56. data/lib/concurrent/executor/executor.rb +7 -2
  57. data/lib/concurrent/executor/timer_set.rb +1 -1
  58. data/lib/concurrent/future.rb +2 -1
  59. data/lib/concurrent/lazy_register.rb +58 -0
  60. data/lib/concurrent/options_parser.rb +4 -2
  61. data/lib/concurrent/promise.rb +2 -1
  62. data/lib/concurrent/scheduled_task.rb +6 -5
  63. data/lib/concurrent/tvar.rb +6 -10
  64. data/lib/concurrent/utility/processor_count.rb +4 -2
  65. data/lib/concurrent/version.rb +1 -1
  66. data/lib/concurrent_ruby_ext.so +0 -0
  67. metadata +37 -10
  68. data/lib/concurrent/actress/context.rb +0 -98
  69. data/lib/concurrent/actress/core.rb +0 -228
  70. data/lib/concurrent/actress/errors.rb +0 -14
@@ -38,10 +38,6 @@ module Concurrent
38
38
  include Concurrent::Observable
39
39
  include Logging
40
40
 
41
- # The default timeout value (in seconds); used when no timeout option
42
- # is given at initialization
43
- TIMEOUT = 5
44
-
45
41
  attr_reader :timeout, :task_executor, :operation_executor
46
42
 
47
43
  # Initialize a new Agent with the given initial value and provided options.
@@ -49,8 +45,6 @@ module Concurrent
49
45
  # @param [Object] initial the initial value
50
46
  # @param [Hash] opts the options used to define the behavior at update and deref
51
47
  #
52
- # @option opts [Fixnum] :timeout (TIMEOUT) maximum number of seconds before an update is cancelled
53
- #
54
48
  # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
55
49
  # operation pool (for long-running operations), when `false` will execute the future on the
56
50
  # global task pool (for short-running tasks)
@@ -65,7 +59,6 @@ module Concurrent
65
59
  @value = initial
66
60
  @rescuers = []
67
61
  @validator = Proc.new { |result| true }
68
- @timeout = opts.fetch(:timeout, TIMEOUT).freeze
69
62
  self.observers = CopyOnWriteObserverSet.new
70
63
  @serialized_execution = SerializedExecution.new
71
64
  @task_executor = OptionsParser.get_task_executor_from(opts)
@@ -145,12 +138,19 @@ module Concurrent
145
138
  # Update the current value with the result of the given block operation,
146
139
  # block can do blocking calls
147
140
  #
141
+ # @param [Fixnum, nil] timeout maximum number of seconds before an update is cancelled
142
+ #
148
143
  # @yield the operation to be performed with the current value in order to calculate
149
144
  # the new value
150
145
  # @yieldparam [Object] value the current value
151
146
  # @yieldreturn [Object] the new value
152
147
  # @return [true, nil] nil when no block is given
153
- def post_off(&block)
148
+ def post_off(timeout = nil, &block)
149
+ block = if timeout
150
+ lambda { |value| Concurrent::timeout(timeout) { block.call(value) } }
151
+ else
152
+ block
153
+ end
154
154
  post_on(@operation_executor, &block)
155
155
  end
156
156
 
@@ -203,10 +203,8 @@ module Concurrent
203
203
  validator, value = mutex.synchronize { [@validator, @value] }
204
204
 
205
205
  begin
206
- result, valid = Concurrent::timeout(@timeout) do
207
- result = handler.call(value)
208
- [result, validator.call(result)]
209
- end
206
+ result = handler.call(value)
207
+ valid = validator.call(result)
210
208
  rescue Exception => ex
211
209
  exception = ex
212
210
  end
@@ -20,7 +20,38 @@ if defined? Concurrent::JavaAtomic
20
20
  #
21
21
  # An object reference that may be updated atomically.
22
22
  #
23
- # @since 0.7.0.rc0
23
+ # Testing with ruby 2.1.2
24
+ #
25
+ # *** Sequential updates ***
26
+ # user system total real
27
+ # no lock 0.000000 0.000000 0.000000 ( 0.005502)
28
+ # mutex 0.030000 0.000000 0.030000 ( 0.025158)
29
+ # MutexAtomic 0.100000 0.000000 0.100000 ( 0.103096)
30
+ # CAtomic 0.040000 0.000000 0.040000 ( 0.034012)
31
+ #
32
+ # *** Parallel updates ***
33
+ # user system total real
34
+ # no lock 0.010000 0.000000 0.010000 ( 0.009387)
35
+ # mutex 0.030000 0.010000 0.040000 ( 0.032545)
36
+ # MutexAtomic 0.830000 2.280000 3.110000 ( 2.146622)
37
+ # CAtomic 0.040000 0.000000 0.040000 ( 0.038332)
38
+ #
39
+ # Testing with jruby 1.9.3
40
+ #
41
+ # *** Sequential updates ***
42
+ # user system total real
43
+ # no lock 0.170000 0.000000 0.170000 ( 0.051000)
44
+ # mutex 0.370000 0.010000 0.380000 ( 0.121000)
45
+ # MutexAtomic 1.530000 0.020000 1.550000 ( 0.471000)
46
+ # JavaAtomic 0.370000 0.010000 0.380000 ( 0.112000)
47
+ #
48
+ # *** Parallel updates ***
49
+ # user system total real
50
+ # no lock 0.390000 0.000000 0.390000 ( 0.105000)
51
+ # mutex 0.480000 0.040000 0.520000 ( 0.145000)
52
+ # MutexAtomic 1.600000 0.180000 1.780000 ( 0.511000)
53
+ # JavaAtomic 0.460000 0.010000 0.470000 ( 0.131000)
54
+ #
24
55
  # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
25
56
  # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
26
57
  class Concurrent::Atomic < Concurrent::JavaAtomic
@@ -6,21 +6,32 @@ module Concurrent
6
6
  # boolean and thread-safe and guaranteed to succeed. Reads and writes may block
7
7
  # briefly but no explicit locking is required.
8
8
  #
9
- # @since 0.6.0
9
+ # Testing with ruby 2.1.2
10
+ # Testing with Concurrent::MutexAtomicBoolean...
11
+ # 2.790000 0.000000 2.790000 ( 2.791454)
12
+ # Testing with Concurrent::CAtomicBoolean...
13
+ # 0.740000 0.000000 0.740000 ( 0.740206)
14
+ #
15
+ # Testing with jruby 1.9.3
16
+ # Testing with Concurrent::MutexAtomicBoolean...
17
+ # 5.240000 2.520000 7.760000 ( 3.683000)
18
+ # Testing with Concurrent::JavaAtomicBoolean...
19
+ # 3.340000 0.010000 3.350000 ( 0.855000)
20
+ #
10
21
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html java.util.concurrent.atomic.AtomicBoolean
11
22
  class MutexAtomicBoolean
12
23
 
13
24
  # @!macro [attach] atomic_boolean_method_initialize
14
25
  #
15
- # Creates a new `AtomicBoolean` with the given initial value.
26
+ # Creates a new `AtomicBoolean` with the given initial value.
16
27
  #
17
- # @param [Boolean] initial the initial value
28
+ # @param [Boolean] initial the initial value
18
29
  def initialize(initial = false)
19
30
  @value = !!initial
20
31
  @mutex = Mutex.new
21
32
  end
22
33
 
23
- # @!macro [attach] atomic_boolean_method_value
34
+ # @!macro [attach] atomic_boolean_method_value_get
24
35
  #
25
36
  # Retrieves the current `Boolean` value.
26
37
  #
@@ -32,7 +43,7 @@ module Concurrent
32
43
  @mutex.unlock
33
44
  end
34
45
 
35
- # @!macro [attach] atomic_boolean_method_value_eq
46
+ # @!macro [attach] atomic_boolean_method_value_set
36
47
  #
37
48
  # Explicitly sets the value.
38
49
  #
@@ -47,9 +58,9 @@ module Concurrent
47
58
  @mutex.unlock
48
59
  end
49
60
 
50
- # @!macro [attach] atomic_boolean_method_is_true
61
+ # @!macro [attach] atomic_boolean_method_true_question
51
62
  #
52
- # Is the current value `true`?
63
+ # Is the current value `true`
53
64
  #
54
65
  # @return [Boolean] true if the current value is `true`, else false
55
66
  def true?
@@ -59,9 +70,9 @@ module Concurrent
59
70
  @mutex.unlock
60
71
  end
61
72
 
62
- # @!macro [attach] atomic_boolean_method_is_false
73
+ # @!macro atomic_boolean_method_false_question
63
74
  #
64
- # Is the current value `true`?false
75
+ # Is the current value `false`
65
76
  #
66
77
  # @return [Boolean] true if the current value is `false`, else false
67
78
  def false?
@@ -111,24 +122,24 @@ module Concurrent
111
122
  @atomic = java.util.concurrent.atomic.AtomicBoolean.new(!!initial)
112
123
  end
113
124
 
114
- # @!macro atomic_boolean_method_value
125
+ # @!macro atomic_boolean_method_value_get
115
126
  #
116
127
  def value
117
128
  @atomic.get
118
129
  end
119
130
 
120
- # @!macro atomic_boolean_method_value_eq
131
+ # @!macro atomic_boolean_method_value_set
121
132
  #
122
133
  def value=(value)
123
134
  @atomic.set(!!value)
124
135
  end
125
136
 
126
- # @!macro [attach] atomic_boolean_method_is_true
137
+ # @!macro atomic_boolean_method_true_question
127
138
  def true?
128
139
  @atomic.get
129
140
  end
130
141
 
131
- # @!macro [attach] atomic_boolean_method_is_false
142
+ # @!macro atomic_boolean_method_false_question
132
143
  def false?
133
144
  !@atomic.get
134
145
  end
@@ -148,6 +159,37 @@ module Concurrent
148
159
  class AtomicBoolean < JavaAtomicBoolean
149
160
  end
150
161
 
162
+ elsif defined? Concurrent::CAtomicBoolean
163
+
164
+ # @!macro atomic_boolean
165
+ class CAtomicBoolean
166
+
167
+ # @!method initialize
168
+ # @!macro atomic_boolean_method_initialize
169
+
170
+ # @!method value
171
+ # @!macro atomic_boolean_method_value_get
172
+
173
+ # @!method value=
174
+ # @!macro atomic_boolean_method_value_set
175
+
176
+ # @!method true?
177
+ # @!macro atomic_boolean_method_true_question
178
+
179
+ # @!method false?
180
+ # @!macro atomic_boolean_method_false_question
181
+
182
+ # @!method make_true
183
+ # @!macro atomic_boolean_method_make_true
184
+
185
+ # @!method make_false
186
+ # @!macro atomic_boolean_method_make_false
187
+ end
188
+
189
+ # @!macro atomic_boolean
190
+ class AtomicBoolean < CAtomicBoolean
191
+ end
192
+
151
193
  else
152
194
 
153
195
  # @!macro atomic_boolean
@@ -6,23 +6,38 @@ module Concurrent
6
6
  # fixnum and thread-safe and guaranteed to succeed. Reads and writes may block
7
7
  # briefly but no explicit locking is required.
8
8
  #
9
- # @since 0.5.0
9
+ # Testing with ruby 2.1.2
10
+ # Testing with Concurrent::MutexAtomicFixnum...
11
+ # 3.130000 0.000000 3.130000 ( 3.136505)
12
+ # Testing with Concurrent::CAtomicFixnum...
13
+ # 0.790000 0.000000 0.790000 ( 0.785550)
14
+ #
15
+ # Testing with jruby 1.9.3
16
+ # Testing with Concurrent::MutexAtomicFixnum...
17
+ # 5.460000 2.460000 7.920000 ( 3.715000)
18
+ # Testing with Concurrent::JavaAtomicFixnum...
19
+ # 4.520000 0.030000 4.550000 ( 1.187000)
20
+ #
10
21
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html java.util.concurrent.atomic.AtomicLong
11
22
  class MutexAtomicFixnum
12
23
 
24
+ # http://stackoverflow.com/questions/535721/ruby-max-integer
25
+ MIN_VALUE = -(2**(0.size * 8 -2))
26
+ MAX_VALUE = (2**(0.size * 8 -2) -1)
27
+
13
28
  # @!macro [attach] atomic_fixnum_method_initialize
14
29
  #
15
- # Creates a new `AtomicFixnum` with the given initial value.
30
+ # Creates a new `AtomicFixnum` with the given initial value.
16
31
  #
17
- # @param [Fixnum] init the initial value
18
- # @raise [ArgumentError] if the initial value is not a `Fixnum`
32
+ # @param [Fixnum] init the initial value
33
+ # @raise [ArgumentError] if the initial value is not a `Fixnum`
19
34
  def initialize(init = 0)
20
35
  raise ArgumentError.new('initial value must be a Fixnum') unless init.is_a?(Fixnum)
21
36
  @value = init
22
37
  @mutex = Mutex.new
23
38
  end
24
39
 
25
- # @!macro [attach] atomic_fixnum_method_value
40
+ # @!macro [attach] atomic_fixnum_method_value_get
26
41
  #
27
42
  # Retrieves the current `Fixnum` value.
28
43
  #
@@ -34,7 +49,7 @@ module Concurrent
34
49
  @mutex.unlock
35
50
  end
36
51
 
37
- # @!macro [attach] atomic_fixnum_method_value_eq
52
+ # @!macro [attach] atomic_fixnum_method_value_set
38
53
  #
39
54
  # Explicitly sets the value.
40
55
  #
@@ -106,44 +121,39 @@ module Concurrent
106
121
  # @!macro atomic_fixnum
107
122
  class JavaAtomicFixnum
108
123
 
124
+ MIN_VALUE = Java::JavaLang::Long::MIN_VALUE
125
+ MAX_VALUE = Java::JavaLang::Long::MAX_VALUE
126
+
109
127
  # @!macro atomic_fixnum_method_initialize
110
- #
111
128
  def initialize(init = 0)
112
129
  raise ArgumentError.new('initial value must be a Fixnum') unless init.is_a?(Fixnum)
113
130
  @atomic = java.util.concurrent.atomic.AtomicLong.new(init)
114
131
  end
115
132
 
116
- # @!macro atomic_fixnum_method_value
117
- #
133
+ # @!macro atomic_fixnum_method_value_get
118
134
  def value
119
135
  @atomic.get
120
136
  end
121
137
 
122
- # @!macro atomic_fixnum_method_value_eq
123
- #
138
+ # @!macro atomic_fixnum_method_value_set
124
139
  def value=(value)
125
140
  raise ArgumentError.new('value must be a Fixnum') unless value.is_a?(Fixnum)
126
141
  @atomic.set(value)
127
142
  end
128
143
 
129
144
  # @!macro atomic_fixnum_method_increment
130
- #
131
145
  def increment
132
146
  @atomic.increment_and_get
133
147
  end
134
-
135
148
  alias_method :up, :increment
136
149
 
137
150
  # @!macro atomic_fixnum_method_decrement
138
- #
139
151
  def decrement
140
152
  @atomic.decrement_and_get
141
153
  end
142
-
143
154
  alias_method :down, :decrement
144
155
 
145
156
  # @!macro atomic_fixnum_method_compare_and_set
146
- #
147
157
  def compare_and_set(expect, update)
148
158
  @atomic.compare_and_set(expect, update)
149
159
  end
@@ -153,6 +163,34 @@ module Concurrent
153
163
  class AtomicFixnum < JavaAtomicFixnum
154
164
  end
155
165
 
166
+ elsif defined? Concurrent::CAtomicFixnum
167
+
168
+ # @!macro atomic_fixnum
169
+ class CAtomicFixnum
170
+
171
+ # @!method initialize
172
+ # @!macro atomic_fixnum_method_initialize
173
+
174
+ # @!method value
175
+ # @!macro atomic_fixnum_method_value_get
176
+
177
+ # @!method value=
178
+ # @!macro atomic_fixnum_method_value_set
179
+
180
+ # @!method increment
181
+ # @!macro atomic_fixnum_method_increment
182
+
183
+ # @!method decrement
184
+ # @!macro atomic_fixnum_method_decrement
185
+
186
+ # @!method compare_and_set
187
+ # @!macro atomic_fixnum_method_compare_and_set
188
+ end
189
+
190
+ # @!macro atomic_fixnum
191
+ class AtomicFixnum < CAtomicFixnum
192
+ end
193
+
156
194
  else
157
195
 
158
196
  # @!macro atomic_fixnum
@@ -0,0 +1,51 @@
1
+ module Concurrent
2
+
3
+ # Safe synchronization under JRuby, prevents reading uninitialized @mutex variable.
4
+ # @note synchronized needs to be called in #initialize for this module to work properly
5
+ # @example usage
6
+ # class AClass
7
+ # include Synchronized
8
+ #
9
+ # def initialize
10
+ # synchronize do
11
+ # # body of the constructor ...
12
+ # end
13
+ # end
14
+ #
15
+ # def a_method
16
+ # synchronize do
17
+ # # body of a_method ...
18
+ # end
19
+ # end
20
+ # end
21
+ module Synchronization
22
+
23
+ engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
24
+
25
+ case engine
26
+ when 'jruby'
27
+ require 'jruby'
28
+
29
+ def synchronize
30
+ JRuby.reference0(self).synchronized { yield }
31
+ end
32
+
33
+ when 'rbx'
34
+
35
+ def synchronize
36
+ Rubinius.lock(self)
37
+ yield
38
+ ensure
39
+ Rubinius.unlock(self)
40
+ end
41
+
42
+ else
43
+
44
+ def synchronize
45
+ @mutex ||= Mutex.new
46
+ @mutex.synchronize { yield }
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -1,54 +1,19 @@
1
- module Concurrent
2
-
3
- module ThreadLocalSymbolAllocator
4
-
5
- COUNTER = AtomicFixnum.new
6
-
7
- protected
8
-
9
- def allocate_symbol
10
- # Warning: this symbol may never be deallocated
11
- @symbol = :"thread_local_symbol_#{COUNTER.increment}"
12
- end
13
-
14
- end
15
-
16
- module ThreadLocalOldStorage
17
-
18
- include ThreadLocalSymbolAllocator
19
-
20
- protected
21
-
22
- def allocate_storage
23
- allocate_symbol
24
- end
25
-
26
- def get
27
- Thread.current[@symbol]
28
- end
1
+ require 'concurrent/atomic'
29
2
 
30
- def set(value)
31
- Thread.current[@symbol] = value
32
- end
33
-
34
- end
35
-
36
- module ThreadLocalNewStorage
37
-
38
- include ThreadLocalSymbolAllocator
3
+ module Concurrent
39
4
 
40
- protected
5
+ module ThreadLocalRubyStorage
41
6
 
42
7
  def allocate_storage
43
- allocate_symbol
8
+ @storage = Atomic.new Hash.new
44
9
  end
45
10
 
46
11
  def get
47
- Thread.current.thread_variable_get(@symbol)
12
+ @storage.get[Thread.current]
48
13
  end
49
14
 
50
15
  def set(value)
51
- Thread.current.thread_variable_set(@symbol, value)
16
+ @storage.update { |s| s.merge Thread.current => value }
52
17
  end
53
18
 
54
19
  end
@@ -71,18 +36,10 @@ module Concurrent
71
36
 
72
37
  end
73
38
 
74
- class ThreadLocalVar
39
+ class AbstractThreadLocalVar
75
40
 
76
41
  NIL_SENTINEL = Object.new
77
42
 
78
- if RUBY_PLATFORM == 'java'
79
- include ThreadLocalJavaStorage
80
- elsif Thread.current.respond_to?(:thread_variable_set)
81
- include ThreadLocalNewStorage
82
- else
83
- include ThreadLocalOldStorage
84
- end
85
-
86
43
  def initialize(default = nil)
87
44
  @default = default
88
45
  allocate_storage
@@ -114,4 +71,12 @@ module Concurrent
114
71
 
115
72
  end
116
73
 
74
+ class ThreadLocalVar < AbstractThreadLocalVar
75
+ if RUBY_PLATFORM == 'java'
76
+ include ThreadLocalJavaStorage
77
+ else
78
+ include ThreadLocalRubyStorage
79
+ end
80
+ end
81
+
117
82
  end