PageRenderTime 25ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb

https://gitlab.com/prodigasistemas/gsan-api
Ruby | 337 lines | 153 code | 33 blank | 151 comment | 15 complexity | c6b6a07a9343427f8d305d261bf428a7 MD5 | raw file
  1. require 'thread'
  2. require 'concurrent/constants'
  3. require 'concurrent/synchronization'
  4. require 'concurrent/utility/engine'
  5. module Concurrent
  6. # @!visibility private
  7. module Collection
  8. # @!visibility private
  9. MapImplementation = case
  10. when Concurrent.on_jruby?
  11. # noinspection RubyResolve
  12. JRubyMapBackend
  13. when Concurrent.on_cruby?
  14. require 'concurrent/collection/map/mri_map_backend'
  15. MriMapBackend
  16. when Concurrent.on_rbx? || Concurrent.on_truffleruby?
  17. require 'concurrent/collection/map/atomic_reference_map_backend'
  18. AtomicReferenceMapBackend
  19. else
  20. warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
  21. require 'concurrent/collection/map/synchronized_map_backend'
  22. SynchronizedMapBackend
  23. end
  24. end
  25. # `Concurrent::Map` is a hash-like object and should have much better performance
  26. # characteristics, especially under high concurrency, than `Concurrent::Hash`.
  27. # However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash`
  28. # -- for instance, it does not necessarily retain ordering by insertion time as `Hash`
  29. # does. For most uses it should do fine though, and we recommend you consider
  30. # `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.
  31. class Map < Collection::MapImplementation
  32. # @!macro map.atomic_method
  33. # This method is atomic.
  34. # @!macro map.atomic_method_with_block
  35. # This method is atomic.
  36. # @note Atomic methods taking a block do not allow the `self` instance
  37. # to be used within the block. Doing so will cause a deadlock.
  38. # @!method compute_if_absent(key)
  39. # Compute and store new value for key if the key is absent.
  40. # @param [Object] key
  41. # @yield new value
  42. # @yieldreturn [Object] new value
  43. # @return [Object] new value or current value
  44. # @!macro map.atomic_method_with_block
  45. # @!method compute_if_present(key)
  46. # Compute and store new value for key if the key is present.
  47. # @param [Object] key
  48. # @yield new value
  49. # @yieldparam old_value [Object]
  50. # @yieldreturn [Object, nil] new value, when nil the key is removed
  51. # @return [Object, nil] new value or nil
  52. # @!macro map.atomic_method_with_block
  53. # @!method compute(key)
  54. # Compute and store new value for key.
  55. # @param [Object] key
  56. # @yield compute new value from old one
  57. # @yieldparam old_value [Object, nil] old_value, or nil when key is absent
  58. # @yieldreturn [Object, nil] new value, when nil the key is removed
  59. # @return [Object, nil] new value or nil
  60. # @!macro map.atomic_method_with_block
  61. # @!method merge_pair(key, value)
  62. # If the key is absent, the value is stored, otherwise new value is
  63. # computed with a block.
  64. # @param [Object] key
  65. # @param [Object] value
  66. # @yield compute new value from old one
  67. # @yieldparam old_value [Object] old value
  68. # @yieldreturn [Object, nil] new value, when nil the key is removed
  69. # @return [Object, nil] new value or nil
  70. # @!macro map.atomic_method_with_block
  71. # @!method replace_pair(key, old_value, new_value)
  72. # Replaces old_value with new_value if key exists and current value
  73. # matches old_value
  74. # @param [Object] key
  75. # @param [Object] old_value
  76. # @param [Object] new_value
  77. # @return [true, false] true if replaced
  78. # @!macro map.atomic_method
  79. # @!method replace_if_exists(key, new_value)
  80. # Replaces current value with new_value if key exists
  81. # @param [Object] key
  82. # @param [Object] new_value
  83. # @return [Object, nil] old value or nil
  84. # @!macro map.atomic_method
  85. # @!method get_and_set(key, value)
  86. # Get the current value under key and set new value.
  87. # @param [Object] key
  88. # @param [Object] value
  89. # @return [Object, nil] old value or nil when the key was absent
  90. # @!macro map.atomic_method
  91. # @!method delete(key)
  92. # Delete key and its value.
  93. # @param [Object] key
  94. # @return [Object, nil] old value or nil when the key was absent
  95. # @!macro map.atomic_method
  96. # @!method delete_pair(key, value)
  97. # Delete pair and its value if current value equals the provided value.
  98. # @param [Object] key
  99. # @param [Object] value
  100. # @return [true, false] true if deleted
  101. # @!macro map.atomic_method
  102. def initialize(options = nil, &block)
  103. if options.kind_of?(::Hash)
  104. validate_options_hash!(options)
  105. else
  106. options = nil
  107. end
  108. super(options)
  109. @default_proc = block
  110. end
  111. # Get a value with key
  112. # @param [Object] key
  113. # @return [Object] the value
  114. def [](key)
  115. if value = super # non-falsy value is an existing mapping, return it right away
  116. value
  117. # 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
  118. # 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
  119. # would be returned)
  120. # note: nil == value check is not technically necessary
  121. elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
  122. @default_proc.call(self, key)
  123. else
  124. value
  125. end
  126. end
  127. alias_method :get, :[]
  128. # TODO (pitr-ch 30-Oct-2018): doc
  129. alias_method :put, :[]=
  130. # Get a value with key, or default_value when key is absent,
  131. # or fail when no default_value is given.
  132. # @param [Object] key
  133. # @param [Object] default_value
  134. # @yield default value for a key
  135. # @yieldparam key [Object]
  136. # @yieldreturn [Object] default value
  137. # @return [Object] the value or default value
  138. # @raise [KeyError] when key is missing and no default_value is provided
  139. # @!macro map_method_not_atomic
  140. # @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended
  141. # to be use as a concurrency primitive with strong happens-before
  142. # guarantees. It is not intended to be used as a high-level abstraction
  143. # supporting complex operations. All read and write operations are
  144. # thread safe, but no guarantees are made regarding race conditions
  145. # between the fetch operation and yielding to the block. Additionally,
  146. # this method does not support recursion. This is due to internal
  147. # constraints that are very unlikely to change in the near future.
  148. def fetch(key, default_value = NULL)
  149. if NULL != (value = get_or_default(key, NULL))
  150. value
  151. elsif block_given?
  152. yield key
  153. elsif NULL != default_value
  154. default_value
  155. else
  156. raise_fetch_no_key
  157. end
  158. end
  159. # Fetch value with key, or store default value when key is absent,
  160. # or fail when no default_value is given. This is a two step operation,
  161. # therefore not atomic. The store can overwrite other concurrently
  162. # stored value.
  163. # @param [Object] key
  164. # @param [Object] default_value
  165. # @yield default value for a key
  166. # @yieldparam key [Object]
  167. # @yieldreturn [Object] default value
  168. # @return [Object] the value or default value
  169. # @!macro map.atomic_method_with_block
  170. def fetch_or_store(key, default_value = NULL)
  171. fetch(key) do
  172. put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
  173. end
  174. end
  175. # Insert value into map with key if key is absent in one atomic step.
  176. # @param [Object] key
  177. # @param [Object] value
  178. # @return [Object, nil] the value or nil when key was present
  179. def put_if_absent(key, value)
  180. computed = false
  181. result = compute_if_absent(key) do
  182. computed = true
  183. value
  184. end
  185. computed ? nil : result
  186. end unless method_defined?(:put_if_absent)
  187. # Is the value stored in the map. Iterates over all values.
  188. # @param [Object] value
  189. # @return [true, false]
  190. def value?(value)
  191. each_value do |v|
  192. return true if value.equal?(v)
  193. end
  194. false
  195. end
  196. # All keys
  197. # @return [::Array<Object>] keys
  198. def keys
  199. arr = []
  200. each_pair { |k, v| arr << k }
  201. arr
  202. end unless method_defined?(:keys)
  203. # All values
  204. # @return [::Array<Object>] values
  205. def values
  206. arr = []
  207. each_pair { |k, v| arr << v }
  208. arr
  209. end unless method_defined?(:values)
  210. # Iterates over each key.
  211. # @yield for each key in the map
  212. # @yieldparam key [Object]
  213. # @return [self]
  214. # @!macro map.atomic_method_with_block
  215. def each_key
  216. each_pair { |k, v| yield k }
  217. end unless method_defined?(:each_key)
  218. # Iterates over each value.
  219. # @yield for each value in the map
  220. # @yieldparam value [Object]
  221. # @return [self]
  222. # @!macro map.atomic_method_with_block
  223. def each_value
  224. each_pair { |k, v| yield v }
  225. end unless method_defined?(:each_value)
  226. # Iterates over each key value pair.
  227. # @yield for each key value pair in the map
  228. # @yieldparam key [Object]
  229. # @yieldparam value [Object]
  230. # @return [self]
  231. # @!macro map.atomic_method_with_block
  232. def each_pair
  233. return enum_for :each_pair unless block_given?
  234. super
  235. end
  236. alias_method :each, :each_pair unless method_defined?(:each)
  237. # Find key of a value.
  238. # @param [Object] value
  239. # @return [Object, nil] key or nil when not found
  240. def key(value)
  241. each_pair { |k, v| return k if v == value }
  242. nil
  243. end unless method_defined?(:key)
  244. alias_method :index, :key if RUBY_VERSION < '1.9'
  245. # Is map empty?
  246. # @return [true, false]
  247. def empty?
  248. each_pair { |k, v| return false }
  249. true
  250. end unless method_defined?(:empty?)
  251. # The size of map.
  252. # @return [Integer] size
  253. def size
  254. count = 0
  255. each_pair { |k, v| count += 1 }
  256. count
  257. end unless method_defined?(:size)
  258. # @!visibility private
  259. def marshal_dump
  260. raise TypeError, "can't dump hash with default proc" if @default_proc
  261. h = {}
  262. each_pair { |k, v| h[k] = v }
  263. h
  264. end
  265. # @!visibility private
  266. def marshal_load(hash)
  267. initialize
  268. populate_from(hash)
  269. end
  270. undef :freeze
  271. # @!visibility private
  272. def inspect
  273. format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect
  274. end
  275. private
  276. def raise_fetch_no_key
  277. raise KeyError, 'key not found'
  278. end
  279. def initialize_copy(other)
  280. super
  281. populate_from(other)
  282. end
  283. def populate_from(hash)
  284. hash.each_pair { |k, v| self[k] = v }
  285. self
  286. end
  287. def validate_options_hash!(options)
  288. if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0)
  289. raise ArgumentError, ":initial_capacity must be a positive Integer"
  290. end
  291. if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
  292. raise ArgumentError, ":load_factor must be a number between 0 and 1"
  293. end
  294. end
  295. end
  296. end