class Concurrent::Map
`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 = ::new
Constants
- DEFAULT_OBJ_ID_STR_WIDTH
@!visibility private
Public Class Methods
@!method delete_pair
@!macro map_method_is_atomic
# 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
Public Instance Methods
# 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
# File lib/concurrent/map.rb, line 166 def each_key each_pair {|k, v| yield k} end
# File lib/concurrent/map.rb, line 170 def each_value each_pair {|k, v| yield v} end
# File lib/concurrent/map.rb, line 182 def empty? each_pair {|k, v| return false} true end
@!macro [attach] map_method_not_atomic
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.
# 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
@!macro map_method_not_atomic
# 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
override default inspect() method: firstly, we don't want to be spilling our guts (i-vars), secondly, MRI backend's inspect() call on its @backend i-var will bump @backend's iter level while possibly yielding GVL
# File lib/concurrent/map.rb, line 211 def inspect id_str = (object_id << 1).to_s(16).rjust(DEFAULT_OBJ_ID_STR_WIDTH, '0') "#<#{self.class.name}:0x#{id_str} entries=#{size} default_proc=#{@default_proc.inspect}>" end
# File lib/concurrent/map.rb, line 176 def key(value) each_pair {|k, v| return k if v == value} nil end
# File lib/concurrent/map.rb, line 154 def keys arr = [] each_pair {|k, v| arr << k} arr end
# File lib/concurrent/map.rb, line 193 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
# File lib/concurrent/map.rb, line 200 def marshal_load(hash) initialize populate_from(hash) end
@!macro map_method_is_atomic
# File lib/concurrent/map.rb, line 138 def put_if_absent(key, value) computed = false result = compute_if_absent(key) do computed = true value end computed ? nil : result end
# File lib/concurrent/map.rb, line 187 def size count = 0 each_pair {|k, v| count += 1} count end
# File lib/concurrent/map.rb, line 147 def value?(value) each_value do |v| return true if value.equal?(v) end false end
# File lib/concurrent/map.rb, line 160 def values arr = [] each_pair {|k, v| arr << v} arr end
Private Instance Methods
# File lib/concurrent/map.rb, line 221 def initialize_copy(other) super populate_from(other) end
# File lib/concurrent/map.rb, line 226 def populate_from(hash) hash.each_pair {|k, v| self[k] = v} self end
# File lib/concurrent/map.rb, line 217 def raise_fetch_no_key raise KeyError, 'key not found' end
# File lib/concurrent/map.rb, line 231 def validate_options_hash!(options) if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0) raise ArgumentError, ":initial_capacity must be a positive Integer" end if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1) raise ArgumentError, ":load_factor must be a number between 0 and 1" end end