Class: Concurrent::Map

Inherits:
Collection::MapImplementation
  • Object
show all
Defined in:
lib/concurrent/map.rb

Overview

`Concurrent::Map` is a hash-like object and should have much better performance characteristics, especially under high concurrency, than `Concurrent::Hash`. However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash` – for instance, it does not necessarily retain ordering by insertion time as `Hash` does. For most uses it should do fine though, and we recommend you consider `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.

> require 'concurrent' > > map = Concurrent::Map.new

Instance Method Summary collapse

Constructor Details

#initialize(options = nil, &block) ⇒ Map

Returns a new instance of Map.


81
82
83
84
85
86
87
88
89
90
# File 'lib/concurrent/map.rb', line 81

def initialize(options = nil, &block)
  if options.kind_of?(::Hash)
    validate_options_hash!(options)
  else
    options = nil
  end

  super(options)
  @default_proc = block
end

Instance Method Details

#[](key) ⇒ Object Also known as: get


92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/concurrent/map.rb', line 92

def [](key)
  if value = super # non-falsy value is an existing mapping, return it right away
    value
    # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
    # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
    # would be returned)
    # note: nil == value check is not technically necessary
  elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
    @default_proc.call(self, key)
  else
    value
  end
end

#computeObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 60

#compute_if_absentObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 54

#compute_if_presentObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 57

#deleteObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 75

#delete_pairObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


81
82
83
84
85
86
87
88
89
90
# File 'lib/concurrent/map.rb', line 81

def initialize(options = nil, &block)
  if options.kind_of?(::Hash)
    validate_options_hash!(options)
  else
    options = nil
  end

  super(options)
  @default_proc = block
end

#each_keyObject


166
167
168
# File 'lib/concurrent/map.rb', line 166

def each_key
  each_pair {|k, v| yield k}
end

#each_valueObject


170
171
172
# File 'lib/concurrent/map.rb', line 170

def each_value
  each_pair {|k, v| yield v}
end

#empty?Boolean

Returns:

  • (Boolean)

180
181
182
183
# File 'lib/concurrent/map.rb', line 180

def empty?
  each_pair {|k, v| return false}
  true
end

#fetch(key, default_value = NULL) ⇒ Object

The “fetch-then-act” methods of `Map` are not atomic. `Map` is intended to be use as a concurrency primitive with strong happens-before guarantees. It is not intended to be used as a high-level abstraction supporting complex operations. All read and write operations are thread safe, but no guarantees are made regarding race conditions between the fetch operation and yielding to the block. Additionally, this method does not support recursion. This is due to internal constraints that are very unlikely to change in the near future.


118
119
120
121
122
123
124
125
126
127
128
# File 'lib/concurrent/map.rb', line 118

def fetch(key, default_value = NULL)
  if NULL != (value = get_or_default(key, NULL))
    value
  elsif block_given?
    yield key
  elsif NULL != default_value
    default_value
  else
    raise_fetch_no_key
  end
end

#fetch_or_store(key, default_value = NULL) ⇒ Object

The “fetch-then-act” methods of `Map` are not atomic. `Map` is intended to be use as a concurrency primitive with strong happens-before guarantees. It is not intended to be used as a high-level abstraction supporting complex operations. All read and write operations are thread safe, but no guarantees are made regarding race conditions between the fetch operation and yielding to the block. Additionally, this method does not support recursion. This is due to internal constraints that are very unlikely to change in the near future.


131
132
133
134
135
# File 'lib/concurrent/map.rb', line 131

def fetch_or_store(key, default_value = NULL)
  fetch(key) do
    put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
  end
end

#get_and_setObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 72

#key(value) ⇒ Object Also known as: index


174
175
176
177
# File 'lib/concurrent/map.rb', line 174

def key(value)
  each_pair {|k, v| return k if v == value}
  nil
end

#keysObject


154
155
156
157
158
# File 'lib/concurrent/map.rb', line 154

def keys
  arr = []
  each_pair {|k, v| arr << k}
  arr
end

#marshal_dumpObject

Raises:

  • (TypeError)

191
192
193
194
195
196
# File 'lib/concurrent/map.rb', line 191

def marshal_dump
  raise TypeError, "can't dump hash with default proc" if @default_proc
  h = {}
  each_pair {|k, v| h[k] = v}
  h
end

#marshal_load(hash) ⇒ Object


198
199
200
201
# File 'lib/concurrent/map.rb', line 198

def marshal_load(hash)
  initialize
  populate_from(hash)
end

#merge_pairObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 63

#put_if_absent(key, value) ⇒ Object

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 51

#replace_if_existsObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 69

#replace_pairObject

This method is atomic. Atomic methods of `Map` which accept a block do not allow the `self` instance to be used within the block. Doing so will cause a deadlock.


# File 'lib/concurrent/map.rb', line 66

#sizeObject


185
186
187
188
189
# File 'lib/concurrent/map.rb', line 185

def size
  count = 0
  each_pair {|k, v| count += 1}
  count
end

#value?(value) ⇒ Boolean

Returns:

  • (Boolean)

147
148
149
150
151
152
# File 'lib/concurrent/map.rb', line 147

def value?(value)
  each_value do |v|
    return true if value.equal?(v)
  end
  false
end

#valuesObject


160
161
162
163
164
# File 'lib/concurrent/map.rb', line 160

def values
  arr = []
  each_pair {|k, v| arr << v}
  arr
end