concurrent-ruby 1.1.10 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -1
  3. data/Gemfile +1 -2
  4. data/README.md +23 -20
  5. data/Rakefile +75 -65
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  7. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  8. data/lib/concurrent-ruby/concurrent/array.rb +3 -13
  9. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  10. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  11. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  12. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +81 -151
  14. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  15. data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
  16. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  17. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  18. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
  19. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  21. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  22. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
  24. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  25. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +5 -3
  26. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
  27. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  28. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  29. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  30. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  31. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  32. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  33. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
  34. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
  35. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
  36. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  37. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  38. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  39. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  40. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  41. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  42. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
  43. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +4 -0
  44. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +6 -9
  45. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +5 -0
  46. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +7 -0
  47. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
  48. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  49. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  50. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +6 -2
  51. data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
  52. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  53. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  54. data/lib/concurrent-ruby/concurrent/map.rb +43 -39
  55. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
  60. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  61. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +1 -1
  62. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  63. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  64. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  65. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  66. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  67. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  68. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  69. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
  71. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  72. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  73. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  74. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  75. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  76. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  77. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  78. data/lib/concurrent-ruby/concurrent/timer_task.rb +59 -9
  79. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  80. data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
  81. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  82. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
  83. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  84. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  85. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +118 -58
  86. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  87. metadata +13 -17
  88. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  89. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  90. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  91. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
  92. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  93. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  94. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  95. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  96. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
  97. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -118
@@ -1,7 +1,8 @@
1
1
  require 'thread'
2
2
  require 'concurrent/atomic/atomic_fixnum'
3
3
  require 'concurrent/errors'
4
- require 'concurrent/synchronization'
4
+ require 'concurrent/synchronization/object'
5
+ require 'concurrent/synchronization/lock'
5
6
 
6
7
  module Concurrent
7
8
 
@@ -1,8 +1,10 @@
1
1
  require 'thread'
2
2
  require 'concurrent/atomic/atomic_reference'
3
+ require 'concurrent/atomic/atomic_fixnum'
3
4
  require 'concurrent/errors'
4
- require 'concurrent/synchronization'
5
- require 'concurrent/atomic/thread_local_var'
5
+ require 'concurrent/synchronization/object'
6
+ require 'concurrent/synchronization/lock'
7
+ require 'concurrent/atomic/lock_local_var'
6
8
 
7
9
  module Concurrent
8
10
 
@@ -109,7 +111,7 @@ module Concurrent
109
111
  @Counter = AtomicFixnum.new(0) # single integer which represents lock state
110
112
  @ReadQueue = Synchronization::Lock.new # used to queue waiting readers
111
113
  @WriteQueue = Synchronization::Lock.new # used to queue waiting writers
112
- @HeldCount = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread
114
+ @HeldCount = LockLocalVar.new(0) # indicates # of R & W locks held by this thread
113
115
  end
114
116
 
115
117
  # Execute a block operation within a read lock.
@@ -1,5 +1,4 @@
1
1
  require 'concurrent/atomic/mutex_semaphore'
2
- require 'concurrent/synchronization'
3
2
 
4
3
  module Concurrent
5
4
 
@@ -11,7 +10,7 @@ module Concurrent
11
10
  #
12
11
  # @param [Fixnum] count the initial count
13
12
  #
14
- # @raise [ArgumentError] if `count` is not an integer or is less than zero
13
+ # @raise [ArgumentError] if `count` is not an integer
15
14
 
16
15
  # @!macro semaphore_method_acquire
17
16
  #
@@ -21,8 +20,7 @@ module Concurrent
21
20
  #
22
21
  # @param [Fixnum] permits Number of permits to acquire
23
22
  #
24
- # @raise [ArgumentError] if `permits` is not an integer or is less than
25
- # one
23
+ # @raise [ArgumentError] if `permits` is not an integer or is less than zero
26
24
  #
27
25
  # @return [nil, BasicObject] Without a block, `nil` is returned. If a block
28
26
  # is given, its return value is returned.
@@ -52,8 +50,7 @@ module Concurrent
52
50
  # @param [Fixnum] timeout the number of seconds to wait for the counter
53
51
  # or `nil` to return immediately
54
52
  #
55
- # @raise [ArgumentError] if `permits` is not an integer or is less than
56
- # one
53
+ # @raise [ArgumentError] if `permits` is not an integer or is less than zero
57
54
  #
58
55
  # @return [true, false, nil, BasicObject] `false` if no permits are
59
56
  # available, `true` when acquired a permit. If a block is given, the
@@ -66,7 +63,7 @@ module Concurrent
66
63
  #
67
64
  # @param [Fixnum] permits Number of permits to return to the semaphore.
68
65
  #
69
- # @raise [ArgumentError] if `permits` is not a number or is less than one
66
+ # @raise [ArgumentError] if `permits` is not a number or is less than zero
70
67
  #
71
68
  # @return [nil]
72
69
 
@@ -96,8 +93,8 @@ module Concurrent
96
93
 
97
94
  # @!visibility private
98
95
  # @!macro internal_implementation_note
99
- SemaphoreImplementation = case
100
- when defined?(JavaSemaphore)
96
+ SemaphoreImplementation = if Concurrent.on_jruby?
97
+ require 'concurrent/utility/native_extension_loader'
101
98
  JavaSemaphore
102
99
  else
103
100
  MutexSemaphore
@@ -1,104 +1,111 @@
1
- require 'concurrent/utility/engine'
2
- require 'concurrent/atomic/ruby_thread_local_var'
3
- require 'concurrent/atomic/java_thread_local_var'
1
+ require 'concurrent/constants'
2
+ require_relative 'locals'
4
3
 
5
4
  module Concurrent
6
5
 
7
- ###################################################################
8
-
9
- # @!macro thread_local_var_method_initialize
6
+ # A `ThreadLocalVar` is a variable where the value is different for each thread.
7
+ # Each variable may have a default value, but when you modify the variable only
8
+ # the current thread will ever see that change.
9
+ #
10
+ # This is similar to Ruby's built-in thread-local variables (`Thread#thread_variable_get`),
11
+ # but with these major advantages:
12
+ # * `ThreadLocalVar` has its own identity, it doesn't need a Symbol.
13
+ # * Each Ruby's built-in thread-local variable leaks some memory forever (it's a Symbol held forever on the thread),
14
+ # so it's only OK to create a small amount of them.
15
+ # `ThreadLocalVar` has no such issue and it is fine to create many of them.
16
+ # * Ruby's built-in thread-local variables leak forever the value set on each thread (unless set to nil explicitly).
17
+ # `ThreadLocalVar` automatically removes the mapping for each thread once the `ThreadLocalVar` instance is GC'd.
18
+ #
19
+ # @!macro thread_safe_variable_comparison
20
+ #
21
+ # @example
22
+ # v = ThreadLocalVar.new(14)
23
+ # v.value #=> 14
24
+ # v.value = 2
25
+ # v.value #=> 2
26
+ #
27
+ # @example
28
+ # v = ThreadLocalVar.new(14)
29
+ #
30
+ # t1 = Thread.new do
31
+ # v.value #=> 14
32
+ # v.value = 1
33
+ # v.value #=> 1
34
+ # end
10
35
  #
11
- # Creates a thread local variable.
36
+ # t2 = Thread.new do
37
+ # v.value #=> 14
38
+ # v.value = 2
39
+ # v.value #=> 2
40
+ # end
12
41
  #
13
- # @param [Object] default the default value when otherwise unset
14
- # @param [Proc] default_block Optional block that gets called to obtain the
15
- # default value for each thread
42
+ # v.value #=> 14
43
+ class ThreadLocalVar
44
+ LOCALS = ThreadLocals.new
16
45
 
17
- # @!macro thread_local_var_method_get
18
- #
19
- # Returns the value in the current thread's copy of this thread-local variable.
20
- #
21
- # @return [Object] the current value
46
+ # Creates a thread local variable.
47
+ #
48
+ # @param [Object] default the default value when otherwise unset
49
+ # @param [Proc] default_block Optional block that gets called to obtain the
50
+ # default value for each thread
51
+ def initialize(default = nil, &default_block)
52
+ if default && block_given?
53
+ raise ArgumentError, "Cannot use both value and block as default value"
54
+ end
22
55
 
23
- # @!macro thread_local_var_method_set
24
- #
25
- # Sets the current thread's copy of this thread-local variable to the specified value.
26
- #
27
- # @param [Object] value the value to set
28
- # @return [Object] the new value
56
+ if block_given?
57
+ @default_block = default_block
58
+ @default = nil
59
+ else
60
+ @default_block = nil
61
+ @default = default
62
+ end
29
63
 
30
- # @!macro thread_local_var_method_bind
31
- #
32
- # Bind the given value to thread local storage during
33
- # execution of the given block.
34
- #
35
- # @param [Object] value the value to bind
36
- # @yield the operation to be performed with the bound variable
37
- # @return [Object] the value
64
+ @index = LOCALS.next_index(self)
65
+ end
38
66
 
67
+ # Returns the value in the current thread's copy of this thread-local variable.
68
+ #
69
+ # @return [Object] the current value
70
+ def value
71
+ LOCALS.fetch(@index) { default }
72
+ end
39
73
 
40
- ###################################################################
74
+ # Sets the current thread's copy of this thread-local variable to the specified value.
75
+ #
76
+ # @param [Object] value the value to set
77
+ # @return [Object] the new value
78
+ def value=(value)
79
+ LOCALS.set(@index, value)
80
+ end
41
81
 
42
- # @!macro thread_local_var_public_api
43
- #
44
- # @!method initialize(default = nil, &default_block)
45
- # @!macro thread_local_var_method_initialize
46
- #
47
- # @!method value
48
- # @!macro thread_local_var_method_get
49
- #
50
- # @!method value=(value)
51
- # @!macro thread_local_var_method_set
52
- #
53
- # @!method bind(value, &block)
54
- # @!macro thread_local_var_method_bind
82
+ # Bind the given value to thread local storage during
83
+ # execution of the given block.
84
+ #
85
+ # @param [Object] value the value to bind
86
+ # @yield the operation to be performed with the bound variable
87
+ # @return [Object] the value
88
+ def bind(value)
89
+ if block_given?
90
+ old_value = self.value
91
+ self.value = value
92
+ begin
93
+ yield
94
+ ensure
95
+ self.value = old_value
96
+ end
97
+ end
98
+ end
55
99
 
56
- ###################################################################
100
+ protected
57
101
 
58
- # @!visibility private
59
- # @!macro internal_implementation_note
60
- ThreadLocalVarImplementation = case
61
- when Concurrent.on_jruby?
62
- JavaThreadLocalVar
63
- else
64
- RubyThreadLocalVar
65
- end
66
- private_constant :ThreadLocalVarImplementation
67
-
68
- # @!macro thread_local_var
69
- #
70
- # A `ThreadLocalVar` is a variable where the value is different for each thread.
71
- # Each variable may have a default value, but when you modify the variable only
72
- # the current thread will ever see that change.
73
- #
74
- # @!macro thread_safe_variable_comparison
75
- #
76
- # @example
77
- # v = ThreadLocalVar.new(14)
78
- # v.value #=> 14
79
- # v.value = 2
80
- # v.value #=> 2
81
- #
82
- # @example
83
- # v = ThreadLocalVar.new(14)
84
- #
85
- # t1 = Thread.new do
86
- # v.value #=> 14
87
- # v.value = 1
88
- # v.value #=> 1
89
- # end
90
- #
91
- # t2 = Thread.new do
92
- # v.value #=> 14
93
- # v.value = 2
94
- # v.value #=> 2
95
- # end
96
- #
97
- # v.value #=> 14
98
- #
99
- # @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal
100
- #
101
- # @!macro thread_local_var_public_api
102
- class ThreadLocalVar < ThreadLocalVarImplementation
102
+ # @!visibility private
103
+ def default
104
+ if @default_block
105
+ self.value = @default_block.call
106
+ else
107
+ @default
108
+ end
109
+ end
103
110
  end
104
111
  end
@@ -0,0 +1,37 @@
1
+ require 'concurrent/errors'
2
+
3
+ module Concurrent
4
+
5
+ # Define update methods that use direct paths
6
+ #
7
+ # @!visibility private
8
+ # @!macro internal_implementation_note
9
+ module AtomicDirectUpdate
10
+ def update
11
+ true until compare_and_set(old_value = get, new_value = yield(old_value))
12
+ new_value
13
+ end
14
+
15
+ def try_update
16
+ old_value = get
17
+ new_value = yield old_value
18
+
19
+ return unless compare_and_set old_value, new_value
20
+
21
+ new_value
22
+ end
23
+
24
+ def try_update!
25
+ old_value = get
26
+ new_value = yield old_value
27
+ unless compare_and_set(old_value, new_value)
28
+ if $VERBOSE
29
+ raise ConcurrentUpdateError, "Update failed"
30
+ else
31
+ raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
32
+ end
33
+ end
34
+ new_value
35
+ end
36
+ end
37
+ end
@@ -1,8 +1,13 @@
1
+ require 'concurrent/atomic_reference/atomic_direct_update'
2
+ require 'concurrent/atomic_reference/numeric_cas_wrapper'
3
+ require 'concurrent/synchronization/safe_initialization'
4
+
1
5
  module Concurrent
2
6
 
3
7
  # @!visibility private
4
8
  # @!macro internal_implementation_note
5
- class MutexAtomicReference < Synchronization::LockableObject
9
+ class MutexAtomicReference
10
+ extend Concurrent::Synchronization::SafeInitialization
6
11
  include AtomicDirectUpdate
7
12
  include AtomicNumericCompareAndSetWrapper
8
13
  alias_method :compare_and_swap, :compare_and_set
@@ -10,7 +15,8 @@ module Concurrent
10
15
  # @!macro atomic_reference_method_initialize
11
16
  def initialize(value = nil)
12
17
  super()
13
- synchronize { ns_initialize(value) }
18
+ @Lock = ::Mutex.new
19
+ @value = value
14
20
  end
15
21
 
16
22
  # @!macro atomic_reference_method_get
@@ -49,8 +55,13 @@ module Concurrent
49
55
 
50
56
  protected
51
57
 
52
- def ns_initialize(value)
53
- @value = value
58
+ # @!visibility private
59
+ def synchronize
60
+ if @Lock.owned?
61
+ yield
62
+ else
63
+ @Lock.synchronize { yield }
64
+ end
54
65
  end
55
66
  end
56
67
  end
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
 
3
3
  module Concurrent
4
4
  module Collection
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
 
3
3
  module Concurrent
4
4
  module Collection
@@ -1,3 +1,5 @@
1
+ require 'concurrent/synchronization/object'
2
+
1
3
  module Concurrent
2
4
 
3
5
  # @!macro warn.edge
@@ -9,8 +9,8 @@ module Concurrent
9
9
  # @!visibility private
10
10
  class MriMapBackend < NonConcurrentMapBackend
11
11
 
12
- def initialize(options = nil)
13
- super(options)
12
+ def initialize(options = nil, &default_proc)
13
+ super(options, &default_proc)
14
14
  @write_lock = Mutex.new
15
15
  end
16
16
 
@@ -12,8 +12,10 @@ module Concurrent
12
12
  # directly without calling each other. This is important because of the
13
13
  # SynchronizedMapBackend which uses a non-reentrant mutex for performance
14
14
  # reasons.
15
- def initialize(options = nil)
16
- @backend = {}
15
+ def initialize(options = nil, &default_proc)
16
+ validate_options_hash!(options) if options.kind_of?(::Hash)
17
+ set_backend(default_proc)
18
+ @default_proc = default_proc
17
19
  end
18
20
 
19
21
  def [](key)
@@ -55,7 +57,7 @@ module Concurrent
55
57
  end
56
58
 
57
59
  def compute(key)
58
- store_computed_value(key, yield(@backend[key]))
60
+ store_computed_value(key, yield(get_or_default(key, nil)))
59
61
  end
60
62
 
61
63
  def merge_pair(key, value)
@@ -67,7 +69,7 @@ module Concurrent
67
69
  end
68
70
 
69
71
  def get_and_set(key, value)
70
- stored_value = @backend[key]
72
+ stored_value = get_or_default(key, nil)
71
73
  @backend[key] = value
72
74
  stored_value
73
75
  end
@@ -109,13 +111,19 @@ module Concurrent
109
111
  @backend.fetch(key, default_value)
110
112
  end
111
113
 
112
- alias_method :_get, :[]
113
- alias_method :_set, :[]=
114
- private :_get, :_set
115
114
  private
115
+
116
+ def set_backend(default_proc)
117
+ if default_proc
118
+ @backend = ::Hash.new { |_h, key| default_proc.call(self, key) }
119
+ else
120
+ @backend = {}
121
+ end
122
+ end
123
+
116
124
  def initialize_copy(other)
117
125
  super
118
- @backend = {}
126
+ set_backend(@default_proc)
119
127
  self
120
128
  end
121
129
 
@@ -8,74 +8,77 @@ module Concurrent
8
8
  # @!visibility private
9
9
  class SynchronizedMapBackend < NonConcurrentMapBackend
10
10
 
11
- require 'mutex_m'
12
- include Mutex_m
13
- # WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are
14
- # not allowed to call each other.
11
+ def initialize(*args, &block)
12
+ super
13
+
14
+ # WARNING: Mutex is a non-reentrant lock, so the synchronized methods are
15
+ # not allowed to call each other.
16
+ @mutex = Mutex.new
17
+ end
15
18
 
16
19
  def [](key)
17
- synchronize { super }
20
+ @mutex.synchronize { super }
18
21
  end
19
22
 
20
23
  def []=(key, value)
21
- synchronize { super }
24
+ @mutex.synchronize { super }
22
25
  end
23
26
 
24
27
  def compute_if_absent(key)
25
- synchronize { super }
28
+ @mutex.synchronize { super }
26
29
  end
27
30
 
28
31
  def compute_if_present(key)
29
- synchronize { super }
32
+ @mutex.synchronize { super }
30
33
  end
31
34
 
32
35
  def compute(key)
33
- synchronize { super }
36
+ @mutex.synchronize { super }
34
37
  end
35
38
 
36
39
  def merge_pair(key, value)
37
- synchronize { super }
40
+ @mutex.synchronize { super }
38
41
  end
39
42
 
40
43
  def replace_pair(key, old_value, new_value)
41
- synchronize { super }
44
+ @mutex.synchronize { super }
42
45
  end
43
46
 
44
47
  def replace_if_exists(key, new_value)
45
- synchronize { super }
48
+ @mutex.synchronize { super }
46
49
  end
47
50
 
48
51
  def get_and_set(key, value)
49
- synchronize { super }
52
+ @mutex.synchronize { super }
50
53
  end
51
54
 
52
55
  def key?(key)
53
- synchronize { super }
56
+ @mutex.synchronize { super }
54
57
  end
55
58
 
56
59
  def delete(key)
57
- synchronize { super }
60
+ @mutex.synchronize { super }
58
61
  end
59
62
 
60
63
  def delete_pair(key, value)
61
- synchronize { super }
64
+ @mutex.synchronize { super }
62
65
  end
63
66
 
64
67
  def clear
65
- synchronize { super }
68
+ @mutex.synchronize { super }
66
69
  end
67
70
 
68
71
  def size
69
- synchronize { super }
72
+ @mutex.synchronize { super }
70
73
  end
71
74
 
72
75
  def get_or_default(key, default_value)
73
- synchronize { super }
76
+ @mutex.synchronize { super }
74
77
  end
75
78
 
76
79
  private
77
80
  def dupped_backend
78
- synchronize { super }
81
+ @mutex.synchronize { super }
79
82
  end
80
83
  end
81
84
  end
@@ -1,4 +1,5 @@
1
1
  require 'logger'
2
+ require 'concurrent/atomic/atomic_reference'
2
3
 
3
4
  module Concurrent
4
5
  module Concern
@@ -15,8 +16,6 @@ module Concurrent
15
16
  # @param [String, nil] message when nil block is used to generate the message
16
17
  # @yieldreturn [String] a message
17
18
  def log(level, progname, message = nil, &block)
18
- #NOTE: Cannot require 'concurrent/configuration' above due to circular references.
19
- # Assume that the gem has been initialized if we've gotten this far.
20
19
  logger = if defined?(@logger) && @logger
21
20
  @logger
22
21
  else
@@ -30,3 +29,88 @@ module Concurrent
30
29
  end
31
30
  end
32
31
  end
32
+
33
+ module Concurrent
34
+ extend Concern::Logging
35
+
36
+ # @return [Logger] Logger with provided level and output.
37
+ def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
38
+ # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
39
+ lambda do |severity, progname, message = nil, &block|
40
+ return false if severity < level
41
+
42
+ message = block ? block.call : message
43
+ formatted_message = case message
44
+ when String
45
+ message
46
+ when Exception
47
+ format "%s (%s)\n%s",
48
+ message.message, message.class, (message.backtrace || []).join("\n")
49
+ else
50
+ message.inspect
51
+ end
52
+
53
+ output.print format "[%s] %5s -- %s: %s\n",
54
+ Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
55
+ Logger::SEV_LABEL[severity],
56
+ progname,
57
+ formatted_message
58
+ true
59
+ end
60
+ end
61
+
62
+ # Use logger created by #create_simple_logger to log concurrent-ruby messages.
63
+ def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
64
+ Concurrent.global_logger = create_simple_logger level, output
65
+ end
66
+
67
+ # @return [Logger] Logger with provided level and output.
68
+ # @deprecated
69
+ def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
70
+ logger = Logger.new(output)
71
+ logger.level = level
72
+ logger.formatter = lambda do |severity, datetime, progname, msg|
73
+ formatted_message = case msg
74
+ when String
75
+ msg
76
+ when Exception
77
+ format "%s (%s)\n%s",
78
+ msg.message, msg.class, (msg.backtrace || []).join("\n")
79
+ else
80
+ msg.inspect
81
+ end
82
+ format "[%s] %5s -- %s: %s\n",
83
+ datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
84
+ severity,
85
+ progname,
86
+ formatted_message
87
+ end
88
+
89
+ lambda do |loglevel, progname, message = nil, &block|
90
+ logger.add loglevel, message, progname, &block
91
+ end
92
+ end
93
+
94
+ # Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
95
+ # @deprecated
96
+ def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
97
+ Concurrent.global_logger = create_stdlib_logger level, output
98
+ end
99
+
100
+ # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
101
+
102
+ # Suppresses all output when used for logging.
103
+ NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
104
+
105
+ # @!visibility private
106
+ GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
107
+ private_constant :GLOBAL_LOGGER
108
+
109
+ def self.global_logger
110
+ GLOBAL_LOGGER.value
111
+ end
112
+
113
+ def self.global_logger=(value)
114
+ GLOBAL_LOGGER.value = value
115
+ end
116
+ end