farce 0.0.1.alpha2-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +26 -0
  3. data/CONTRIBUTING.md +71 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +1524 -0
  6. data/SECURITY.md +10 -0
  7. data/docs/benchmarks.md +185 -0
  8. data/docs/gems/dry-types.md +290 -0
  9. data/docs/gems/msgpack.md +70 -0
  10. data/docs/gems/ractor-shim.md +63 -0
  11. data/docs/modes.md +628 -0
  12. data/docs/scopes.md +649 -0
  13. data/docs/variants.md +346 -0
  14. data/ext/ext_helper.rb +20 -0
  15. data/ext/farce/README.md +21 -0
  16. data/ext/farce/atom.c +1023 -0
  17. data/ext/farce/bounded_map.c +1683 -0
  18. data/ext/farce/containers.h +77 -0
  19. data/ext/farce/counter.c +399 -0
  20. data/ext/farce/darwin.c +100 -0
  21. data/ext/farce/depend +12 -0
  22. data/ext/farce/dict.c +1523 -0
  23. data/ext/farce/dict.h +152 -0
  24. data/ext/farce/drivers.c +237 -0
  25. data/ext/farce/exchanger.c +299 -0
  26. data/ext/farce/extconf.rb +99 -0
  27. data/ext/farce/farce.c +359 -0
  28. data/ext/farce/flag.c +288 -0
  29. data/ext/farce/io.c +338 -0
  30. data/ext/farce/lock.c +510 -0
  31. data/ext/farce/map.c +2240 -0
  32. data/ext/farce/priority_queue.c +2056 -0
  33. data/ext/farce/queue.c +1059 -0
  34. data/ext/farce/reactor.c +820 -0
  35. data/ext/farce/reactor.h +103 -0
  36. data/ext/farce/shareable.h +31 -0
  37. data/ext/farce/signal.c +350 -0
  38. data/ext/farce/transaction.c +354 -0
  39. data/ext/farce/transaction.h +40 -0
  40. data/ext/farce/tree_map.c +1953 -0
  41. data/ext/farce/trie.c +2020 -0
  42. data/ext/farce/unshareable.c +155 -0
  43. data/ext/farce/unshared_io_pool.h +234 -0
  44. data/ext/farce/unshared_signal.c +263 -0
  45. data/ext/farce/unshared_wait.h +193 -0
  46. data/ext/farce/unsupported.c +6 -0
  47. data/ext/farce/vector.c +1195 -0
  48. data/ext/farce/weak_map.c +1714 -0
  49. data/ext/java/org/farce/BoundedMap.java +394 -0
  50. data/ext/java/org/farce/FiberScheduler.java +141 -0
  51. data/ext/java/org/farce/PriorityKey.java +45 -0
  52. data/ext/java/org/farce/PriorityQueue.java +373 -0
  53. data/ext/java/org/farce/QueueSignal.java +28 -0
  54. data/ext/rebind/README.md +14 -0
  55. data/ext/rebind/extconf.rb +10 -0
  56. data/ext/rebind/rebind.c +186 -0
  57. data/lib/farce/_yard/internal.rb +13 -0
  58. data/lib/farce/_yard/macros.rb +61 -0
  59. data/lib/farce/_yard/ractor.rb +46 -0
  60. data/lib/farce/abstract/atom.rb +186 -0
  61. data/lib/farce/abstract/bounded_map.rb +232 -0
  62. data/lib/farce/abstract/collection.rb +151 -0
  63. data/lib/farce/abstract/concurrent_map.rb +381 -0
  64. data/lib/farce/abstract/counter.rb +193 -0
  65. data/lib/farce/abstract/duplicable_map.rb +229 -0
  66. data/lib/farce/abstract/exchanger.rb +26 -0
  67. data/lib/farce/abstract/flag.rb +71 -0
  68. data/lib/farce/abstract/lazy.rb +115 -0
  69. data/lib/farce/abstract/lease.rb +104 -0
  70. data/lib/farce/abstract/lease_map.rb +261 -0
  71. data/lib/farce/abstract/lease_pool.rb +91 -0
  72. data/lib/farce/abstract/lfu_map.rb +20 -0
  73. data/lib/farce/abstract/lru_map.rb +25 -0
  74. data/lib/farce/abstract/map.rb +345 -0
  75. data/lib/farce/abstract/molecule.rb +245 -0
  76. data/lib/farce/abstract/port.rb +74 -0
  77. data/lib/farce/abstract/priority_queue.rb +117 -0
  78. data/lib/farce/abstract/queue.rb +269 -0
  79. data/lib/farce/abstract/scheduler.rb +111 -0
  80. data/lib/farce/abstract/set.rb +910 -0
  81. data/lib/farce/abstract/sorted_set.rb +172 -0
  82. data/lib/farce/abstract/timer_queue.rb +136 -0
  83. data/lib/farce/abstract/tree_map.rb +269 -0
  84. data/lib/farce/abstract/value.rb +68 -0
  85. data/lib/farce/abstract/vector.rb +1126 -0
  86. data/lib/farce/abstract/weak_atom.rb +32 -0
  87. data/lib/farce/abstract/weak_key_map.rb +12 -0
  88. data/lib/farce/abstract/weak_map.rb +13 -0
  89. data/lib/farce/abstract/weak_set.rb +12 -0
  90. data/lib/farce/abstract/weak_value_map.rb +12 -0
  91. data/lib/farce/abstract.rb +17 -0
  92. data/lib/farce/atom.rb +293 -0
  93. data/lib/farce/class_mirror.rb +87 -0
  94. data/lib/farce/clock.rb +121 -0
  95. data/lib/farce/config.rb +229 -0
  96. data/lib/farce/counter.rb +71 -0
  97. data/lib/farce/deduper.rb +122 -0
  98. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  99. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  101. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  102. data/lib/farce/engine/jruby/map.rb +505 -0
  103. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  104. data/lib/farce/engine/jruby/signal.rb +147 -0
  105. data/lib/farce/engine/jruby.rb +63 -0
  106. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  107. data/lib/farce/engine/jvm/counter.rb +102 -0
  108. data/lib/farce/engine/jvm/extension.rb +32 -0
  109. data/lib/farce/engine/jvm/farce.jar +0 -0
  110. data/lib/farce/engine/jvm/flag.rb +79 -0
  111. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  112. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  113. data/lib/farce/engine/jvm/types.rb +180 -0
  114. data/lib/farce/engine/jvm.rb +19 -0
  115. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  116. data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
  117. data/lib/farce/engine/ruby/3.4/port.rb +186 -0
  118. data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
  119. data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
  120. data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
  121. data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
  122. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  123. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  124. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  125. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  126. data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
  127. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  128. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  129. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  130. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  131. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  132. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  133. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  134. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  135. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  136. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  137. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  138. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  139. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  140. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  141. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  142. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  143. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  144. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  145. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  146. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  147. data/lib/farce/engine/ruby.rb +130 -0
  148. data/lib/farce/engine/shared/atom.rb +10 -0
  149. data/lib/farce/engine/shared/exchanger.rb +130 -0
  150. data/lib/farce/engine/shared/identity_key.rb +22 -0
  151. data/lib/farce/engine/shared/lease.rb +10 -0
  152. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  153. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  154. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  155. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  156. data/lib/farce/engine/shared/port.rb +44 -0
  157. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  158. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  159. data/lib/farce/engine/shared/queue.rb +233 -0
  160. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  161. data/lib/farce/engine/shared/rebindable.rb +15 -0
  162. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  163. data/lib/farce/engine/shared/strict_map.rb +117 -0
  164. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  165. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  166. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  167. data/lib/farce/engine/shared/trie.rb +317 -0
  168. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  169. data/lib/farce/engine/shared/unshareable.rb +14 -0
  170. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  171. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  172. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  173. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  174. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  175. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  176. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  177. data/lib/farce/engine/shared/vault.rb +31 -0
  178. data/lib/farce/engine/shared/vector.rb +83 -0
  179. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  180. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  181. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  182. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  183. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  184. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  185. data/lib/farce/engine/shared.rb +63 -0
  186. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  187. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  188. data/lib/farce/engine/truffleruby/map.rb +656 -0
  189. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  190. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  191. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  192. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  193. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  194. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  195. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  196. data/lib/farce/engine/truffleruby.rb +148 -0
  197. data/lib/farce/envelope.rb +335 -0
  198. data/lib/farce/error.rb +35 -0
  199. data/lib/farce/exchanger.rb +41 -0
  200. data/lib/farce/flag.rb +27 -0
  201. data/lib/farce/integrations/active_support/blank.rb +68 -0
  202. data/lib/farce/integrations/active_support/clock.rb +17 -0
  203. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  204. data/lib/farce/integrations/active_support/map.rb +175 -0
  205. data/lib/farce/integrations/active_support/set.rb +60 -0
  206. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  207. data/lib/farce/integrations/active_support/vector.rb +231 -0
  208. data/lib/farce/integrations/active_support.rb +9 -0
  209. data/lib/farce/integrations/activesupport.rb +5 -0
  210. data/lib/farce/integrations/bson.rb +108 -0
  211. data/lib/farce/integrations/cbor.rb +51 -0
  212. data/lib/farce/integrations/concurrent.rb +112 -0
  213. data/lib/farce/integrations/dry-types.rb +5 -0
  214. data/lib/farce/integrations/dry_types.rb +694 -0
  215. data/lib/farce/integrations/json.rb +7 -0
  216. data/lib/farce/integrations/msgpack.rb +112 -0
  217. data/lib/farce/integrations/oj.rb +69 -0
  218. data/lib/farce/integrations/psych.rb +191 -0
  219. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  220. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  221. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  222. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  223. data/lib/farce/integrations/shared/to_json.rb +42 -0
  224. data/lib/farce/integrations/sorted_set.rb +11 -0
  225. data/lib/farce/integrations/weakref.rb +38 -0
  226. data/lib/farce/integrations/yajl.rb +10 -0
  227. data/lib/farce/integrations.rb +156 -0
  228. data/lib/farce/internal/_frozen_config.rb +11 -0
  229. data/lib/farce/internal/autoloads.rb +47 -0
  230. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  231. data/lib/farce/internal/converter.rb +106 -0
  232. data/lib/farce/internal/copyable.rb +31 -0
  233. data/lib/farce/internal/delegation.rb +20 -0
  234. data/lib/farce/internal/external_transaction.rb +59 -0
  235. data/lib/farce/internal/fake_ractor.rb +179 -0
  236. data/lib/farce/internal/freeze.rb +118 -0
  237. data/lib/farce/internal/inspect.rb +157 -0
  238. data/lib/farce/internal/key_lock_map.rb +29 -0
  239. data/lib/farce/internal/key_normalizer.rb +289 -0
  240. data/lib/farce/internal/lease_initialization.rb +115 -0
  241. data/lib/farce/internal/lease_map.rb +441 -0
  242. data/lib/farce/internal/lease_pool_state.rb +228 -0
  243. data/lib/farce/internal/lease_state.rb +342 -0
  244. data/lib/farce/internal/lease_waiting.rb +13 -0
  245. data/lib/farce/internal/managed_queue.rb +26 -0
  246. data/lib/farce/internal/map_value_modes.rb +227 -0
  247. data/lib/farce/internal/marshal_support.rb +227 -0
  248. data/lib/farce/internal/mixin.rb +20 -0
  249. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  250. data/lib/farce/internal/noncopyable.rb +17 -0
  251. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  252. data/lib/farce/internal/pool_supervisor.rb +41 -0
  253. data/lib/farce/internal/pool_worker.rb +61 -0
  254. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  255. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  256. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  257. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  258. data/lib/farce/internal/portable_transaction.rb +164 -0
  259. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  260. data/lib/farce/internal/reservation_waiting.rb +24 -0
  261. data/lib/farce/internal/scheduled_task.rb +48 -0
  262. data/lib/farce/internal/scheduler_io.rb +171 -0
  263. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  264. data/lib/farce/internal/select_scheduler.rb +235 -0
  265. data/lib/farce/internal/storage.rb +162 -0
  266. data/lib/farce/internal/strict_lease.rb +30 -0
  267. data/lib/farce/internal/strict_lease_map.rb +20 -0
  268. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  269. data/lib/farce/internal/thread_pool.rb +166 -0
  270. data/lib/farce/internal/transaction_conflict.rb +9 -0
  271. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  272. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  273. data/lib/farce/internal/undefined.rb +22 -0
  274. data/lib/farce/internal/unshared_lease.rb +29 -0
  275. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  276. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  277. data/lib/farce/internal/value_serialization.rb +13 -0
  278. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  279. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  280. data/lib/farce/internal.rb +129 -0
  281. data/lib/farce/lazy.rb +100 -0
  282. data/lib/farce/lazy_ref.rb +40 -0
  283. data/lib/farce/lease.rb +28 -0
  284. data/lib/farce/lease_map.rb +34 -0
  285. data/lib/farce/lease_pool.rb +29 -0
  286. data/lib/farce/lfu_map.rb +53 -0
  287. data/lib/farce/local/atom.rb +23 -0
  288. data/lib/farce/local/counter.rb +88 -0
  289. data/lib/farce/local/flag.rb +65 -0
  290. data/lib/farce/local/lazy.rb +38 -0
  291. data/lib/farce/local/lazy_ref.rb +36 -0
  292. data/lib/farce/local/lease.rb +77 -0
  293. data/lib/farce/local/lease_map.rb +80 -0
  294. data/lib/farce/local/lease_pool.rb +38 -0
  295. data/lib/farce/local/lfu_map.rb +55 -0
  296. data/lib/farce/local/lru_map.rb +62 -0
  297. data/lib/farce/local/map.rb +47 -0
  298. data/lib/farce/local/molecule.rb +45 -0
  299. data/lib/farce/local/priority_queue.rb +53 -0
  300. data/lib/farce/local/queue.rb +32 -0
  301. data/lib/farce/local/scoped.rb +166 -0
  302. data/lib/farce/local/set.rb +18 -0
  303. data/lib/farce/local/sorted_set.rb +50 -0
  304. data/lib/farce/local/timer_queue.rb +41 -0
  305. data/lib/farce/local/tree_map.rb +47 -0
  306. data/lib/farce/local/vector.rb +30 -0
  307. data/lib/farce/local/weak_atom.rb +23 -0
  308. data/lib/farce/local/weak_key_map.rb +39 -0
  309. data/lib/farce/local/weak_map.rb +39 -0
  310. data/lib/farce/local/weak_set.rb +18 -0
  311. data/lib/farce/local/weak_value_map.rb +39 -0
  312. data/lib/farce/local.rb +35 -0
  313. data/lib/farce/lock.rb +54 -0
  314. data/lib/farce/lru_map.rb +63 -0
  315. data/lib/farce/map.rb +58 -0
  316. data/lib/farce/mode_manager.rb +142 -0
  317. data/lib/farce/molecule.rb +60 -0
  318. data/lib/farce/mutable.rb +171 -0
  319. data/lib/farce/pool.rb +332 -0
  320. data/lib/farce/port.rb +134 -0
  321. data/lib/farce/priority_queue.rb +70 -0
  322. data/lib/farce/proxy/register.rb +101 -0
  323. data/lib/farce/proxy/supervisor.rb +137 -0
  324. data/lib/farce/proxy/wrapper.rb +49 -0
  325. data/lib/farce/proxy.rb +291 -0
  326. data/lib/farce/queue.rb +60 -0
  327. data/lib/farce/ractor.rb +269 -0
  328. data/lib/farce/read_write_lock.rb +256 -0
  329. data/lib/farce/reference.rb +158 -0
  330. data/lib/farce/resolv/dns.rb +42 -0
  331. data/lib/farce/resolv.rb +85 -0
  332. data/lib/farce/scheduler.rb +536 -0
  333. data/lib/farce/set.rb +39 -0
  334. data/lib/farce/shareable.rb +150 -0
  335. data/lib/farce/signal.rb +97 -0
  336. data/lib/farce/sorted_set.rb +36 -0
  337. data/lib/farce/strict/atom.rb +34 -0
  338. data/lib/farce/strict/counter.rb +10 -0
  339. data/lib/farce/strict/exchanger.rb +25 -0
  340. data/lib/farce/strict/flag.rb +10 -0
  341. data/lib/farce/strict/lazy.rb +26 -0
  342. data/lib/farce/strict/lazy_ref.rb +26 -0
  343. data/lib/farce/strict/lease.rb +27 -0
  344. data/lib/farce/strict/lease_map.rb +33 -0
  345. data/lib/farce/strict/lease_pool.rb +28 -0
  346. data/lib/farce/strict/lfu_map.rb +27 -0
  347. data/lib/farce/strict/lru_map.rb +28 -0
  348. data/lib/farce/strict/map.rb +54 -0
  349. data/lib/farce/strict/molecule.rb +16 -0
  350. data/lib/farce/strict/port.rb +40 -0
  351. data/lib/farce/strict/priority_queue.rb +20 -0
  352. data/lib/farce/strict/queue.rb +17 -0
  353. data/lib/farce/strict/set.rb +14 -0
  354. data/lib/farce/strict/sorted_set.rb +19 -0
  355. data/lib/farce/strict/timer_queue.rb +13 -0
  356. data/lib/farce/strict/tree_map.rb +27 -0
  357. data/lib/farce/strict/vector.rb +24 -0
  358. data/lib/farce/strict/weak_atom.rb +31 -0
  359. data/lib/farce/strict/weak_key_map.rb +49 -0
  360. data/lib/farce/strict/weak_map.rb +49 -0
  361. data/lib/farce/strict/weak_set.rb +14 -0
  362. data/lib/farce/strict/weak_value_map.rb +48 -0
  363. data/lib/farce/strict.rb +28 -0
  364. data/lib/farce/system.rb +40 -0
  365. data/lib/farce/thread_scheduler.rb +70 -0
  366. data/lib/farce/timer_queue.rb +66 -0
  367. data/lib/farce/transaction/atom.rb +73 -0
  368. data/lib/farce/transaction/map.rb +25 -0
  369. data/lib/farce/transaction/map_operations.rb +143 -0
  370. data/lib/farce/transaction/molecule.rb +90 -0
  371. data/lib/farce/transaction/mutable.rb +61 -0
  372. data/lib/farce/transaction/set.rb +17 -0
  373. data/lib/farce/transaction/set_operations.rb +48 -0
  374. data/lib/farce/transaction/sorted_set.rb +18 -0
  375. data/lib/farce/transaction/tree_map.rb +78 -0
  376. data/lib/farce/transaction/vector.rb +139 -0
  377. data/lib/farce/transaction/wrapper.rb +202 -0
  378. data/lib/farce/transaction.rb +343 -0
  379. data/lib/farce/tree_map.rb +56 -0
  380. data/lib/farce/unsafe/lfu_map.rb +36 -0
  381. data/lib/farce/unsafe/lru_map.rb +36 -0
  382. data/lib/farce/unsafe/tree_map.rb +21 -0
  383. data/lib/farce/unsafe.rb +29 -0
  384. data/lib/farce/unshareable.rb +67 -0
  385. data/lib/farce/unshared/atom.rb +24 -0
  386. data/lib/farce/unshared/counter.rb +10 -0
  387. data/lib/farce/unshared/flag.rb +10 -0
  388. data/lib/farce/unshared/lazy.rb +38 -0
  389. data/lib/farce/unshared/lazy_ref.rb +26 -0
  390. data/lib/farce/unshared/lease.rb +27 -0
  391. data/lib/farce/unshared/lease_map.rb +26 -0
  392. data/lib/farce/unshared/lease_pool.rb +25 -0
  393. data/lib/farce/unshared/lfu_map.rb +20 -0
  394. data/lib/farce/unshared/lru_map.rb +20 -0
  395. data/lib/farce/unshared/map.rb +49 -0
  396. data/lib/farce/unshared/molecule.rb +16 -0
  397. data/lib/farce/unshared/priority_queue.rb +16 -0
  398. data/lib/farce/unshared/queue.rb +27 -0
  399. data/lib/farce/unshared/set.rb +14 -0
  400. data/lib/farce/unshared/sorted_set.rb +28 -0
  401. data/lib/farce/unshared/timer_queue.rb +16 -0
  402. data/lib/farce/unshared/tree_map.rb +21 -0
  403. data/lib/farce/unshared/vector.rb +19 -0
  404. data/lib/farce/unshared/weak_atom.rb +31 -0
  405. data/lib/farce/unshared/weak_key_map.rb +44 -0
  406. data/lib/farce/unshared/weak_map.rb +44 -0
  407. data/lib/farce/unshared/weak_set.rb +14 -0
  408. data/lib/farce/unshared/weak_value_map.rb +43 -0
  409. data/lib/farce/unshared.rb +28 -0
  410. data/lib/farce/vector.rb +228 -0
  411. data/lib/farce/version.rb +8 -0
  412. data/lib/farce/walker/definitions.rb +186 -0
  413. data/lib/farce/walker/modification.rb +355 -0
  414. data/lib/farce/walker.rb +319 -0
  415. data/lib/farce/weak_atom.rb +121 -0
  416. data/lib/farce/weak_key_map.rb +31 -0
  417. data/lib/farce/weak_map.rb +33 -0
  418. data/lib/farce/weak_ref.rb +67 -0
  419. data/lib/farce/weak_set.rb +82 -0
  420. data/lib/farce/weak_value.rb +195 -0
  421. data/lib/farce/weak_value_map.rb +32 -0
  422. data/lib/farce.rb +519 -0
  423. metadata +468 -0
@@ -0,0 +1,1683 @@
1
+ #include "containers.h"
2
+ #include <ruby/atomic.h>
3
+ #include <ruby/st.h>
4
+
5
+ #include <limits.h>
6
+ #include <stdbool.h>
7
+ #include <stddef.h>
8
+ #include <stdint.h>
9
+
10
+ #define LRU_INITIAL_CAPACITY ((size_t)8)
11
+
12
+ enum {
13
+ LRU_UNINITIALIZED = 0,
14
+ LRU_PUBLISHING = 1,
15
+ LRU_INITIALIZED = 2,
16
+ };
17
+
18
+ typedef struct lru_entry lru_entry_t;
19
+ typedef struct lfu_entry lfu_entry_t;
20
+ typedef struct lfu_bucket lfu_bucket_t;
21
+
22
+ struct lfu_bucket {
23
+ VALUE frequency;
24
+ lfu_bucket_t *previous;
25
+ lfu_bucket_t *following;
26
+ lru_entry_t *least_recent;
27
+ lru_entry_t *most_recent;
28
+ };
29
+
30
+ struct lru_entry {
31
+ VALUE key;
32
+ VALUE value;
33
+ st_index_t hash;
34
+ size_t slot;
35
+ lru_entry_t *previous;
36
+ lru_entry_t *following;
37
+ };
38
+
39
+ struct lfu_entry {
40
+ lru_entry_t base;
41
+ lfu_bucket_t *bucket;
42
+ };
43
+
44
+ static lfu_entry_t *
45
+ lfu_entry_from_base(lru_entry_t *entry)
46
+ {
47
+ return (lfu_entry_t *)entry;
48
+ }
49
+
50
+ #define LRU_TOMBSTONE ((lru_entry_t *)(uintptr_t)1)
51
+
52
+ typedef struct {
53
+ lru_entry_t **slots;
54
+ size_t capacity;
55
+ size_t size;
56
+ size_t tombstones;
57
+ size_t max_size;
58
+ lru_entry_t *least_recent;
59
+ lru_entry_t *most_recent;
60
+ lfu_bucket_t *least_frequency;
61
+ lfu_bucket_t *most_frequency;
62
+ size_t bucket_count;
63
+ VALUE lock;
64
+ bool compare_keys_by_identity;
65
+ bool compare_values_by_identity;
66
+ bool shareable_container;
67
+ bool lfu_policy;
68
+ rb_atomic_t state;
69
+ } lru_map_t;
70
+
71
+ typedef struct {
72
+ VALUE self;
73
+ lru_map_t *map;
74
+ VALUE key;
75
+ VALUE value;
76
+ VALUE result;
77
+ size_t limit;
78
+ bool found;
79
+ bool compare_keys_by_identity;
80
+ bool compare_values_by_identity;
81
+ } lru_arguments_t;
82
+
83
+ static const rb_data_type_t lru_map_type;
84
+ static const rb_data_type_t lru_shared_map_type;
85
+ static VALUE cLRUMap;
86
+ static VALUE cShareableLRUMap;
87
+ static VALUE cLFUMap;
88
+ static VALUE cShareableLFUMap;
89
+
90
+ static lru_entry_t **
91
+ lru_allocate_slots(size_t capacity)
92
+ {
93
+ if (capacity > SIZE_MAX / sizeof(lru_entry_t *)) {
94
+ rb_raise(rb_eRangeError, "bounded map index is too large");
95
+ }
96
+ return ruby_xcalloc(capacity, sizeof(lru_entry_t *));
97
+ }
98
+
99
+ static void
100
+ lru_free_entries(lru_entry_t *entry)
101
+ {
102
+ while (entry != NULL) {
103
+ lru_entry_t *following = entry->following;
104
+ xfree(entry);
105
+ entry = following;
106
+ }
107
+ }
108
+
109
+ static void
110
+ lfu_free_buckets(lfu_bucket_t *bucket)
111
+ {
112
+ while (bucket != NULL) {
113
+ lfu_bucket_t *following = bucket->following;
114
+ lru_free_entries(bucket->least_recent);
115
+ xfree(bucket);
116
+ bucket = following;
117
+ }
118
+ }
119
+
120
+ static void
121
+ lru_mark(void *opaque)
122
+ {
123
+ lru_map_t *map = opaque;
124
+ if (map == NULL) return;
125
+
126
+ rb_gc_mark_movable(map->lock);
127
+ if (map->lfu_policy) {
128
+ for (lfu_bucket_t *bucket = map->least_frequency;
129
+ bucket != NULL;
130
+ bucket = bucket->following) {
131
+ rb_gc_mark_movable(bucket->frequency);
132
+ for (lru_entry_t *entry = bucket->least_recent;
133
+ entry != NULL;
134
+ entry = entry->following) {
135
+ rb_gc_mark_movable(entry->key);
136
+ rb_gc_mark_movable(entry->value);
137
+ }
138
+ }
139
+ }
140
+ else {
141
+ for (lru_entry_t *entry = map->least_recent;
142
+ entry != NULL;
143
+ entry = entry->following) {
144
+ rb_gc_mark_movable(entry->key);
145
+ rb_gc_mark_movable(entry->value);
146
+ }
147
+ }
148
+ }
149
+
150
+ static void
151
+ lru_compact(void *opaque)
152
+ {
153
+ lru_map_t *map = opaque;
154
+ if (map == NULL) return;
155
+
156
+ map->lock = rb_gc_location(map->lock);
157
+ if (map->lfu_policy) {
158
+ for (lfu_bucket_t *bucket = map->least_frequency;
159
+ bucket != NULL;
160
+ bucket = bucket->following) {
161
+ bucket->frequency = rb_gc_location(bucket->frequency);
162
+ for (lru_entry_t *entry = bucket->least_recent;
163
+ entry != NULL;
164
+ entry = entry->following) {
165
+ entry->key = rb_gc_location(entry->key);
166
+ entry->value = rb_gc_location(entry->value);
167
+ }
168
+ }
169
+ }
170
+ else {
171
+ for (lru_entry_t *entry = map->least_recent;
172
+ entry != NULL;
173
+ entry = entry->following) {
174
+ entry->key = rb_gc_location(entry->key);
175
+ entry->value = rb_gc_location(entry->value);
176
+ }
177
+ }
178
+ }
179
+
180
+ static void
181
+ lru_free(void *opaque)
182
+ {
183
+ lru_map_t *map = opaque;
184
+ if (map == NULL) return;
185
+ if (map->lfu_policy) lfu_free_buckets(map->least_frequency);
186
+ else lru_free_entries(map->least_recent);
187
+ ruby_xfree(map->slots);
188
+ xfree(map);
189
+ }
190
+
191
+ static size_t
192
+ lru_memsize(const void *opaque)
193
+ {
194
+ const lru_map_t *map = opaque;
195
+ if (map == NULL) return 0;
196
+ return sizeof(*map) +
197
+ map->capacity * sizeof(lru_entry_t *) +
198
+ map->size * (map->lfu_policy ? sizeof(lfu_entry_t) : sizeof(lru_entry_t)) +
199
+ map->bucket_count * sizeof(lfu_bucket_t);
200
+ }
201
+
202
+ static const rb_data_type_t lru_map_type = {
203
+ .wrap_struct_name = "Farce::Internal::BoundedMap",
204
+ .function = {
205
+ .dmark = lru_mark,
206
+ .dfree = lru_free,
207
+ .dsize = lru_memsize,
208
+ .dcompact = lru_compact,
209
+ },
210
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
211
+ };
212
+
213
+ static const rb_data_type_t lru_shared_map_type = {
214
+ .wrap_struct_name = "Farce::Internal::ShareableBoundedMap",
215
+ .function = {
216
+ .dmark = lru_mark,
217
+ .dfree = lru_free,
218
+ .dsize = lru_memsize,
219
+ .dcompact = lru_compact,
220
+ },
221
+ .flags = RUBY_TYPED_FROZEN_SHAREABLE,
222
+ };
223
+
224
+ static VALUE
225
+ lru_allocate(
226
+ VALUE klass,
227
+ const rb_data_type_t *type,
228
+ bool shareable,
229
+ bool lfu_policy
230
+ )
231
+ {
232
+ lru_map_t *map;
233
+ VALUE self = TypedData_Make_Struct(klass, lru_map_t, type, map);
234
+
235
+ map->slots = NULL;
236
+ map->capacity = 0;
237
+ map->size = 0;
238
+ map->tombstones = 0;
239
+ map->max_size = 0;
240
+ map->least_recent = NULL;
241
+ map->most_recent = NULL;
242
+ map->least_frequency = NULL;
243
+ map->most_frequency = NULL;
244
+ map->bucket_count = 0;
245
+ map->lock = Qnil;
246
+ map->compare_keys_by_identity = false;
247
+ map->compare_values_by_identity = false;
248
+ map->shareable_container = shareable;
249
+ map->lfu_policy = lfu_policy;
250
+ RUBY_ATOMIC_SET(map->state, LRU_UNINITIALIZED);
251
+ RB_OBJ_WRITE(self, &map->lock, containers_lock_new());
252
+ return self;
253
+ }
254
+
255
+ static VALUE
256
+ lru_local_allocate(VALUE klass)
257
+ {
258
+ return lru_allocate(klass, &lru_map_type, false, false);
259
+ }
260
+
261
+ static VALUE
262
+ lru_shared_allocate(VALUE klass)
263
+ {
264
+ return lru_allocate(klass, &lru_shared_map_type, true, false);
265
+ }
266
+
267
+ static VALUE
268
+ lfu_local_allocate(VALUE klass)
269
+ {
270
+ return lru_allocate(klass, &lru_map_type, false, true);
271
+ }
272
+
273
+ static VALUE
274
+ lfu_shared_allocate(VALUE klass)
275
+ {
276
+ return lru_allocate(klass, &lru_shared_map_type, true, true);
277
+ }
278
+
279
+ static lru_map_t *
280
+ lru_get_raw(VALUE self)
281
+ {
282
+ lru_map_t *map;
283
+ if (rb_typeddata_is_kind_of(self, &lru_map_type)) {
284
+ TypedData_Get_Struct(self, lru_map_t, &lru_map_type, map);
285
+ return map;
286
+ }
287
+ if (rb_typeddata_is_kind_of(self, &lru_shared_map_type)) {
288
+ TypedData_Get_Struct(self, lru_map_t, &lru_shared_map_type, map);
289
+ return map;
290
+ }
291
+ rb_raise(rb_eTypeError, "wrong bounded map type");
292
+ }
293
+
294
+ static lru_map_t *
295
+ lru_get(VALUE self)
296
+ {
297
+ lru_map_t *map = lru_get_raw(self);
298
+ rb_atomic_t state = RUBY_ATOMIC_LOAD(map->state);
299
+ if (state == LRU_INITIALIZED) return map;
300
+ if (map->shareable_container &&
301
+ state == LRU_PUBLISHING &&
302
+ rb_ractor_shareable_p(self)) {
303
+ return map;
304
+ }
305
+ else {
306
+ rb_raise(rb_eRuntimeError, "uninitialized bounded map");
307
+ }
308
+ }
309
+
310
+ static void
311
+ lru_check_mutable(VALUE self, lru_map_t *map)
312
+ {
313
+ (void)map;
314
+ rb_check_frozen(self);
315
+ }
316
+
317
+ static void
318
+ lru_validate_references(VALUE self)
319
+ {
320
+ lru_map_t *map;
321
+ TypedData_Get_Struct(self, lru_map_t, &lru_shared_map_type, map);
322
+ containers_check_shareable(map->lock);
323
+ if (map->lfu_policy) {
324
+ for (lfu_bucket_t *bucket = map->least_frequency;
325
+ bucket != NULL;
326
+ bucket = bucket->following) {
327
+ containers_check_shareable(bucket->frequency);
328
+ for (lru_entry_t *entry = bucket->least_recent;
329
+ entry != NULL;
330
+ entry = entry->following) {
331
+ containers_check_shareable(entry->key);
332
+ containers_check_shareable(entry->value);
333
+ }
334
+ }
335
+ }
336
+ else {
337
+ for (lru_entry_t *entry = map->least_recent;
338
+ entry != NULL;
339
+ entry = entry->following) {
340
+ containers_check_shareable(entry->key);
341
+ containers_check_shareable(entry->value);
342
+ }
343
+ }
344
+ }
345
+
346
+ static size_t
347
+ lru_parse_limit(VALUE value, const char *name)
348
+ {
349
+ if (!RB_INTEGER_TYPE_P(value)) {
350
+ rb_raise(rb_eTypeError, "%s must be an Integer", name);
351
+ }
352
+ if ((FIXNUM_P(value) && FIX2LONG(value) < 0) ||
353
+ (RB_TYPE_P(value, T_BIGNUM) && RBIGNUM_NEGATIVE_P(value))) {
354
+ rb_raise(rb_eArgError, "%s must be non-negative", name);
355
+ }
356
+ return NUM2SIZET(value);
357
+ }
358
+
359
+ static VALUE
360
+ lru_prepare_key(lru_map_t *map, VALUE key)
361
+ {
362
+ if (!map->compare_keys_by_identity) {
363
+ key = containers_normalize_string_key(key);
364
+ }
365
+ if (map->shareable_container) containers_check_shareable(key);
366
+ return key;
367
+ }
368
+
369
+ static st_index_t
370
+ lru_key_hash(const lru_map_t *map, VALUE key)
371
+ {
372
+ if (map->compare_keys_by_identity) {
373
+ return (st_index_t)NUM2ULL(rb_obj_id(key));
374
+ }
375
+ return (st_index_t)NUM2LONG(rb_hash(key));
376
+ }
377
+
378
+ static bool
379
+ lru_keys_equal(const lru_map_t *map, VALUE stored, VALUE probe)
380
+ {
381
+ if (stored == probe) return true;
382
+ return !map->compare_keys_by_identity && rb_eql(stored, probe);
383
+ }
384
+
385
+ static size_t
386
+ lru_find_slot(
387
+ lru_map_t *map,
388
+ VALUE key,
389
+ st_index_t hash,
390
+ bool *found
391
+ )
392
+ {
393
+ size_t mask = map->capacity - 1;
394
+ size_t first_tombstone = SIZE_MAX;
395
+
396
+ for (size_t offset = 0; offset < map->capacity; offset++) {
397
+ size_t index = ((size_t)hash + offset) & mask;
398
+ lru_entry_t *entry = map->slots[index];
399
+
400
+ if (entry == NULL) {
401
+ *found = false;
402
+ return first_tombstone == SIZE_MAX ? index : first_tombstone;
403
+ }
404
+ if (entry == LRU_TOMBSTONE) {
405
+ if (first_tombstone == SIZE_MAX) first_tombstone = index;
406
+ continue;
407
+ }
408
+ if (entry->hash == hash && lru_keys_equal(map, entry->key, key)) {
409
+ *found = true;
410
+ return index;
411
+ }
412
+ }
413
+
414
+ *found = false;
415
+ return first_tombstone;
416
+ }
417
+
418
+ static size_t
419
+ lru_find_empty_slot(const lru_map_t *map, st_index_t hash)
420
+ {
421
+ size_t mask = map->capacity - 1;
422
+ size_t first_tombstone = SIZE_MAX;
423
+
424
+ for (size_t offset = 0; offset < map->capacity; offset++) {
425
+ size_t index = ((size_t)hash + offset) & mask;
426
+ lru_entry_t *entry = map->slots[index];
427
+
428
+ if (entry == NULL) {
429
+ return first_tombstone == SIZE_MAX ? index : first_tombstone;
430
+ }
431
+ if (entry == LRU_TOMBSTONE && first_tombstone == SIZE_MAX) {
432
+ first_tombstone = index;
433
+ }
434
+ }
435
+
436
+ if (first_tombstone != SIZE_MAX) return first_tombstone;
437
+ rb_raise(rb_eRuntimeError, "bounded map index has no insertion slot");
438
+ }
439
+
440
+ static void
441
+ lru_place_resized_entry(
442
+ lru_entry_t **slots,
443
+ size_t mask,
444
+ lru_entry_t *entry
445
+ )
446
+ {
447
+ size_t index = (size_t)entry->hash & mask;
448
+ while (slots[index] != NULL) index = (index + 1) & mask;
449
+ slots[index] = entry;
450
+ entry->slot = index;
451
+ }
452
+
453
+ static void
454
+ lru_resize(lru_map_t *map, size_t capacity)
455
+ {
456
+ lru_entry_t **slots = lru_allocate_slots(capacity);
457
+ size_t mask = capacity - 1;
458
+
459
+ if (map->lfu_policy) {
460
+ for (lfu_bucket_t *bucket = map->least_frequency;
461
+ bucket != NULL;
462
+ bucket = bucket->following) {
463
+ for (lru_entry_t *entry = bucket->least_recent;
464
+ entry != NULL;
465
+ entry = entry->following) {
466
+ lru_place_resized_entry(slots, mask, entry);
467
+ }
468
+ }
469
+ }
470
+ else {
471
+ for (lru_entry_t *entry = map->least_recent;
472
+ entry != NULL;
473
+ entry = entry->following) {
474
+ lru_place_resized_entry(slots, mask, entry);
475
+ }
476
+ }
477
+
478
+ ruby_xfree(map->slots);
479
+ map->slots = slots;
480
+ map->capacity = capacity;
481
+ map->tombstones = 0;
482
+ }
483
+
484
+ static void
485
+ lru_prepare_insert(lru_map_t *map)
486
+ {
487
+ size_t used = map->size + map->tombstones;
488
+ size_t load_limit = (map->capacity / 10) * 7 +
489
+ (((map->capacity % 10) * 7 + 9) / 10);
490
+ if (used + 1 < load_limit) return;
491
+
492
+ if (map->size + 1 < load_limit) {
493
+ lru_resize(map, map->capacity);
494
+ return;
495
+ }
496
+ if (map->capacity > SIZE_MAX / 2) {
497
+ rb_raise(rb_eRangeError, "bounded map index cannot grow further");
498
+ }
499
+ lru_resize(map, map->capacity * 2);
500
+ }
501
+
502
+ static void
503
+ lru_unlink(lru_map_t *map, lru_entry_t *entry)
504
+ {
505
+ if (entry->previous != NULL) {
506
+ entry->previous->following = entry->following;
507
+ }
508
+ else {
509
+ map->least_recent = entry->following;
510
+ }
511
+ if (entry->following != NULL) {
512
+ entry->following->previous = entry->previous;
513
+ }
514
+ else {
515
+ map->most_recent = entry->previous;
516
+ }
517
+ entry->previous = NULL;
518
+ entry->following = NULL;
519
+ }
520
+
521
+ static void
522
+ lru_append(lru_map_t *map, lru_entry_t *entry)
523
+ {
524
+ entry->previous = map->most_recent;
525
+ entry->following = NULL;
526
+ if (map->most_recent != NULL) map->most_recent->following = entry;
527
+ else map->least_recent = entry;
528
+ map->most_recent = entry;
529
+ }
530
+
531
+ static void
532
+ lru_promote(lru_map_t *map, lru_entry_t *entry)
533
+ {
534
+ if (entry == map->most_recent) return;
535
+ lru_unlink(map, entry);
536
+ lru_append(map, entry);
537
+ }
538
+
539
+ static void
540
+ lru_remove_entry(lru_map_t *map, lru_entry_t *entry)
541
+ {
542
+ lru_unlink(map, entry);
543
+ map->slots[entry->slot] = LRU_TOMBSTONE;
544
+ map->tombstones++;
545
+ map->size--;
546
+ xfree(entry);
547
+ }
548
+
549
+ static void
550
+ lru_insert_entry(lru_map_t *map, lru_entry_t *entry, size_t slot)
551
+ {
552
+ if (map->slots[slot] == LRU_TOMBSTONE) map->tombstones--;
553
+ map->slots[slot] = entry;
554
+ entry->slot = slot;
555
+ lru_append(map, entry);
556
+ map->size++;
557
+ }
558
+
559
+ static VALUE
560
+ lfu_increment_frequency(VALUE frequency)
561
+ {
562
+ if (FIXNUM_P(frequency)) {
563
+ long value = FIX2LONG(frequency);
564
+ return value < FIXNUM_MAX ? LONG2FIX(value + 1) : LONG2NUM(value + 1);
565
+ }
566
+ return rb_big_plus(frequency, INT2FIX(1));
567
+ }
568
+
569
+ static bool
570
+ lfu_frequencies_equal(VALUE left, VALUE right)
571
+ {
572
+ if (left == right) return true;
573
+ if (!RB_TYPE_P(left, T_BIGNUM) || !RB_TYPE_P(right, T_BIGNUM)) {
574
+ return false;
575
+ }
576
+ return rb_big_cmp(left, right) == INT2FIX(0);
577
+ }
578
+
579
+ static lfu_bucket_t *
580
+ lfu_allocate_bucket(VALUE self, VALUE frequency)
581
+ {
582
+ lfu_bucket_t *bucket = ALLOC(lfu_bucket_t);
583
+ bucket->frequency = Qnil;
584
+ bucket->previous = NULL;
585
+ bucket->following = NULL;
586
+ bucket->least_recent = NULL;
587
+ bucket->most_recent = NULL;
588
+ RB_OBJ_WRITE(self, &bucket->frequency, frequency);
589
+ return bucket;
590
+ }
591
+
592
+ static void
593
+ lfu_link_bucket_after(
594
+ lru_map_t *map,
595
+ lfu_bucket_t *previous,
596
+ lfu_bucket_t *bucket
597
+ )
598
+ {
599
+ bucket->previous = previous;
600
+ if (previous == NULL) {
601
+ bucket->following = map->least_frequency;
602
+ map->least_frequency = bucket;
603
+ }
604
+ else {
605
+ bucket->following = previous->following;
606
+ previous->following = bucket;
607
+ }
608
+ if (bucket->following != NULL) bucket->following->previous = bucket;
609
+ else map->most_frequency = bucket;
610
+ map->bucket_count++;
611
+ }
612
+
613
+ static void
614
+ lfu_remove_bucket(lru_map_t *map, lfu_bucket_t *bucket)
615
+ {
616
+ if (bucket->previous != NULL) {
617
+ bucket->previous->following = bucket->following;
618
+ }
619
+ else {
620
+ map->least_frequency = bucket->following;
621
+ }
622
+ if (bucket->following != NULL) {
623
+ bucket->following->previous = bucket->previous;
624
+ }
625
+ else {
626
+ map->most_frequency = bucket->previous;
627
+ }
628
+ map->bucket_count--;
629
+ xfree(bucket);
630
+ }
631
+
632
+ static void
633
+ lfu_unlink_entry(lfu_bucket_t *bucket, lru_entry_t *entry)
634
+ {
635
+ if (entry->previous != NULL) {
636
+ entry->previous->following = entry->following;
637
+ }
638
+ else {
639
+ bucket->least_recent = entry->following;
640
+ }
641
+ if (entry->following != NULL) {
642
+ entry->following->previous = entry->previous;
643
+ }
644
+ else {
645
+ bucket->most_recent = entry->previous;
646
+ }
647
+ entry->previous = NULL;
648
+ entry->following = NULL;
649
+ }
650
+
651
+ static void
652
+ lfu_append_entry(lfu_bucket_t *bucket, lru_entry_t *entry)
653
+ {
654
+ lfu_entry_from_base(entry)->bucket = bucket;
655
+ entry->previous = bucket->most_recent;
656
+ entry->following = NULL;
657
+ if (bucket->most_recent != NULL) bucket->most_recent->following = entry;
658
+ else bucket->least_recent = entry;
659
+ bucket->most_recent = entry;
660
+ }
661
+
662
+ static void
663
+ lfu_promote(VALUE self, lru_map_t *map, lru_entry_t *entry)
664
+ {
665
+ lfu_bucket_t *current = lfu_entry_from_base(entry)->bucket;
666
+ lfu_bucket_t *following = current->following;
667
+ VALUE frequency = lfu_increment_frequency(current->frequency);
668
+
669
+ if (following != NULL &&
670
+ lfu_frequencies_equal(following->frequency, frequency)) {
671
+ lfu_unlink_entry(current, entry);
672
+ lfu_append_entry(following, entry);
673
+ if (current->least_recent == NULL) lfu_remove_bucket(map, current);
674
+ return;
675
+ }
676
+
677
+ if (current->least_recent == current->most_recent) {
678
+ RB_OBJ_WRITE(self, &current->frequency, frequency);
679
+ return;
680
+ }
681
+
682
+ lfu_bucket_t *destination = lfu_allocate_bucket(self, frequency);
683
+ lfu_link_bucket_after(map, current, destination);
684
+ lfu_unlink_entry(current, entry);
685
+ lfu_append_entry(destination, entry);
686
+ }
687
+
688
+ static void
689
+ lfu_remove_entry(lru_map_t *map, lru_entry_t *entry)
690
+ {
691
+ lfu_bucket_t *bucket = lfu_entry_from_base(entry)->bucket;
692
+ lfu_unlink_entry(bucket, entry);
693
+ map->slots[entry->slot] = LRU_TOMBSTONE;
694
+ map->tombstones++;
695
+ map->size--;
696
+ xfree(entry);
697
+ if (bucket->least_recent == NULL) lfu_remove_bucket(map, bucket);
698
+ }
699
+
700
+ static void
701
+ bounded_remove_entry(lru_map_t *map, lru_entry_t *entry)
702
+ {
703
+ if (map->lfu_policy) lfu_remove_entry(map, entry);
704
+ else lru_remove_entry(map, entry);
705
+ }
706
+
707
+ static lru_entry_t *
708
+ bounded_victim(lru_map_t *map)
709
+ {
710
+ return map->lfu_policy
711
+ ? map->least_frequency->least_recent
712
+ : map->least_recent;
713
+ }
714
+
715
+ static VALUE
716
+ lru_call_locked(
717
+ lru_map_t *map,
718
+ containers_lock_operation_t operation,
719
+ lru_arguments_t *arguments
720
+ )
721
+ {
722
+ return containers_lock_synchronize_call(map->lock, operation, (VALUE)arguments);
723
+ }
724
+
725
+ static VALUE
726
+ lru_store_body(VALUE opaque)
727
+ {
728
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
729
+ lru_map_t *map = arguments->map;
730
+ VALUE key;
731
+ st_index_t hash;
732
+ size_t slot;
733
+ bool found;
734
+
735
+ lru_check_mutable(arguments->self, map);
736
+ key = lru_prepare_key(map, arguments->key);
737
+ if (map->shareable_container) containers_check_shareable(arguments->value);
738
+ hash = lru_key_hash(map, key);
739
+ if (map->max_size == 0) {
740
+ lru_check_mutable(arguments->self, map);
741
+ return arguments->value;
742
+ }
743
+ slot = lru_find_slot(map, key, hash, &found);
744
+ lru_check_mutable(arguments->self, map);
745
+
746
+ if (found) {
747
+ lru_entry_t *entry = map->slots[slot];
748
+ RB_OBJ_WRITE(arguments->self, &entry->value, arguments->value);
749
+ lru_promote(map, entry);
750
+ return arguments->value;
751
+ }
752
+
753
+ lru_prepare_insert(map);
754
+ slot = lru_find_empty_slot(map, hash);
755
+
756
+ lru_entry_t *entry = ALLOC(lru_entry_t);
757
+ entry->key = Qnil;
758
+ entry->value = Qnil;
759
+ entry->hash = hash;
760
+ entry->slot = 0;
761
+ entry->previous = NULL;
762
+ entry->following = NULL;
763
+ RB_OBJ_WRITE(arguments->self, &entry->key, key);
764
+ RB_OBJ_WRITE(arguments->self, &entry->value, arguments->value);
765
+
766
+ lru_entry_t *victim = map->size >= map->max_size
767
+ ? map->least_recent
768
+ : NULL;
769
+ lru_insert_entry(map, entry, slot);
770
+ if (victim != NULL) bounded_remove_entry(map, victim);
771
+ return arguments->value;
772
+ }
773
+
774
+ static VALUE
775
+ lru_store(VALUE self, VALUE key, VALUE value)
776
+ {
777
+ lru_map_t *map = lru_get(self);
778
+ lru_arguments_t arguments = {
779
+ .self = self,
780
+ .map = map,
781
+ .key = key,
782
+ .value = value,
783
+ };
784
+ return lru_call_locked(map, lru_store_body, &arguments);
785
+ }
786
+
787
+ typedef struct {
788
+ VALUE self;
789
+ VALUE frequency;
790
+ lfu_bucket_t *bucket;
791
+ } lfu_bucket_allocation_t;
792
+
793
+ static VALUE
794
+ lfu_allocate_bucket_body(VALUE opaque)
795
+ {
796
+ lfu_bucket_allocation_t *allocation =
797
+ (lfu_bucket_allocation_t *)opaque;
798
+ allocation->bucket = lfu_allocate_bucket(
799
+ allocation->self,
800
+ allocation->frequency
801
+ );
802
+ return Qnil;
803
+ }
804
+
805
+ static lfu_bucket_t *
806
+ lfu_allocate_bucket_after_entry(
807
+ VALUE self,
808
+ VALUE frequency,
809
+ lru_entry_t *entry
810
+ )
811
+ {
812
+ lfu_bucket_allocation_t allocation = {
813
+ .self = self,
814
+ .frequency = frequency,
815
+ .bucket = NULL,
816
+ };
817
+ int state = 0;
818
+
819
+ (void)rb_protect(lfu_allocate_bucket_body, (VALUE)&allocation, &state);
820
+ if (state != 0) {
821
+ xfree(entry);
822
+ rb_jump_tag(state);
823
+ }
824
+ return allocation.bucket;
825
+ }
826
+
827
+ static VALUE
828
+ lfu_initial_frequency(void)
829
+ {
830
+ #ifdef FARCE_TEST_LFU_OVERFLOW
831
+ return LONG2NUM(FIXNUM_MAX - 1);
832
+ #else
833
+ return INT2FIX(1);
834
+ #endif
835
+ }
836
+
837
+ static VALUE
838
+ lfu_store_body(VALUE opaque)
839
+ {
840
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
841
+ lru_map_t *map = arguments->map;
842
+ VALUE key;
843
+ st_index_t hash;
844
+ size_t slot;
845
+ bool found;
846
+
847
+ lru_check_mutable(arguments->self, map);
848
+ key = lru_prepare_key(map, arguments->key);
849
+ if (map->shareable_container) containers_check_shareable(arguments->value);
850
+ hash = lru_key_hash(map, key);
851
+ if (map->max_size == 0) {
852
+ lru_check_mutable(arguments->self, map);
853
+ return arguments->value;
854
+ }
855
+ slot = lru_find_slot(map, key, hash, &found);
856
+ lru_check_mutable(arguments->self, map);
857
+
858
+ if (found) {
859
+ lru_entry_t *entry = map->slots[slot];
860
+ lfu_promote(arguments->self, map, entry);
861
+ RB_OBJ_WRITE(arguments->self, &entry->value, arguments->value);
862
+ return arguments->value;
863
+ }
864
+
865
+ lru_prepare_insert(map);
866
+ slot = lru_find_empty_slot(map, hash);
867
+ VALUE frequency = lfu_initial_frequency();
868
+ lfu_bucket_t *bucket = map->least_frequency;
869
+ bool needs_bucket = bucket == NULL ||
870
+ !lfu_frequencies_equal(bucket->frequency, frequency);
871
+ lfu_entry_t *allocated_entry = ALLOC(lfu_entry_t);
872
+ lru_entry_t *entry = &allocated_entry->base;
873
+ lfu_bucket_t *new_bucket = needs_bucket
874
+ ? lfu_allocate_bucket_after_entry(
875
+ arguments->self,
876
+ frequency,
877
+ entry
878
+ )
879
+ : NULL;
880
+
881
+ entry->key = Qnil;
882
+ entry->value = Qnil;
883
+ entry->hash = hash;
884
+ entry->slot = slot;
885
+ entry->previous = NULL;
886
+ entry->following = NULL;
887
+ allocated_entry->bucket = NULL;
888
+ RB_OBJ_WRITE(arguments->self, &entry->key, key);
889
+ RB_OBJ_WRITE(arguments->self, &entry->value, arguments->value);
890
+
891
+ lru_entry_t *victim = map->size >= map->max_size
892
+ ? bounded_victim(map)
893
+ : NULL;
894
+ if (new_bucket != NULL) {
895
+ lfu_link_bucket_after(map, NULL, new_bucket);
896
+ bucket = new_bucket;
897
+ }
898
+ if (map->slots[slot] == LRU_TOMBSTONE) map->tombstones--;
899
+ map->slots[slot] = entry;
900
+ map->size++;
901
+ lfu_append_entry(bucket, entry);
902
+ if (victim != NULL) lfu_remove_entry(map, victim);
903
+ return arguments->value;
904
+ }
905
+
906
+ static VALUE
907
+ lfu_store(VALUE self, VALUE key, VALUE value)
908
+ {
909
+ lru_map_t *map = lru_get(self);
910
+ lru_arguments_t arguments = {
911
+ .self = self,
912
+ .map = map,
913
+ .key = key,
914
+ .value = value,
915
+ };
916
+ return lru_call_locked(map, lfu_store_body, &arguments);
917
+ }
918
+
919
+ static VALUE
920
+ lru_lookup_body(VALUE opaque)
921
+ {
922
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
923
+ lru_map_t *map = arguments->map;
924
+ VALUE key = lru_prepare_key(map, arguments->key);
925
+ st_index_t hash = lru_key_hash(map, key);
926
+ bool found;
927
+ size_t slot = lru_find_slot(map, key, hash, &found);
928
+
929
+ arguments->found = found;
930
+ if (!found) return Qnil;
931
+ lru_entry_t *entry = map->slots[slot];
932
+ if (!RB_OBJ_FROZEN(arguments->self)) lru_promote(map, entry);
933
+ return entry->value;
934
+ }
935
+
936
+ static VALUE
937
+ lru_aref(VALUE self, VALUE key)
938
+ {
939
+ lru_map_t *map = lru_get(self);
940
+ lru_arguments_t arguments = {.self = self, .map = map, .key = key};
941
+ return lru_call_locked(map, lru_lookup_body, &arguments);
942
+ }
943
+
944
+ static VALUE
945
+ lfu_lookup_body(VALUE opaque)
946
+ {
947
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
948
+ lru_map_t *map = arguments->map;
949
+ VALUE key = lru_prepare_key(map, arguments->key);
950
+ st_index_t hash = lru_key_hash(map, key);
951
+ bool found;
952
+ size_t slot = lru_find_slot(map, key, hash, &found);
953
+
954
+ arguments->found = found;
955
+ if (!found) return Qnil;
956
+ lru_entry_t *entry = map->slots[slot];
957
+ if (!RB_OBJ_FROZEN(arguments->self)) {
958
+ lfu_promote(arguments->self, map, entry);
959
+ }
960
+ return entry->value;
961
+ }
962
+
963
+ static VALUE
964
+ lfu_aref(VALUE self, VALUE key)
965
+ {
966
+ lru_map_t *map = lru_get(self);
967
+ lru_arguments_t arguments = {.self = self, .map = map, .key = key};
968
+ return lru_call_locked(map, lfu_lookup_body, &arguments);
969
+ }
970
+
971
+ static VALUE
972
+ lru_fetch(int argc, VALUE *argv, VALUE self)
973
+ {
974
+ VALUE key;
975
+ VALUE default_value = Qnil;
976
+ bool default_given;
977
+ bool block_given;
978
+ lru_map_t *map;
979
+ lru_arguments_t arguments;
980
+ VALUE result;
981
+
982
+ rb_scan_args(argc, argv, "11", &key, &default_value);
983
+ default_given = argc == 2;
984
+ block_given = rb_block_given_p();
985
+ if (default_given && block_given) rb_warn("block supersedes default value argument");
986
+
987
+ map = lru_get(self);
988
+ arguments = (lru_arguments_t){.self = self, .map = map, .key = key};
989
+ result = lru_call_locked(map, lru_lookup_body, &arguments);
990
+ if (arguments.found) return result;
991
+ if (block_given) return rb_yield(key);
992
+ if (default_given) return default_value;
993
+ containers_raise_key_error(self, key);
994
+ return Qnil;
995
+ }
996
+
997
+ static VALUE
998
+ lfu_fetch(int argc, VALUE *argv, VALUE self)
999
+ {
1000
+ VALUE key;
1001
+ VALUE default_value = Qnil;
1002
+ bool default_given;
1003
+ bool block_given;
1004
+ lru_map_t *map;
1005
+ lru_arguments_t arguments;
1006
+ VALUE result;
1007
+
1008
+ rb_scan_args(argc, argv, "11", &key, &default_value);
1009
+ default_given = argc == 2;
1010
+ block_given = rb_block_given_p();
1011
+ if (default_given && block_given) {
1012
+ rb_warn("block supersedes default value argument");
1013
+ }
1014
+
1015
+ map = lru_get(self);
1016
+ arguments = (lru_arguments_t){.self = self, .map = map, .key = key};
1017
+ result = lru_call_locked(map, lfu_lookup_body, &arguments);
1018
+ if (arguments.found) return result;
1019
+ if (block_given) return rb_yield(key);
1020
+ if (default_given) return default_value;
1021
+ containers_raise_key_error(self, key);
1022
+ return Qnil;
1023
+ }
1024
+
1025
+ static VALUE
1026
+ lru_observe_body(VALUE opaque)
1027
+ {
1028
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1029
+ lru_map_t *map = arguments->map;
1030
+ VALUE key = lru_prepare_key(map, arguments->key);
1031
+ st_index_t hash = lru_key_hash(map, key);
1032
+ bool found;
1033
+ size_t slot = lru_find_slot(map, key, hash, &found);
1034
+
1035
+ arguments->found = found;
1036
+ arguments->result = found ? map->slots[slot]->key : Qnil;
1037
+ return arguments->result;
1038
+ }
1039
+
1040
+ static VALUE
1041
+ lru_preflight_key_body(VALUE opaque)
1042
+ {
1043
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1044
+ lru_map_t *map = arguments->map;
1045
+ VALUE key = lru_prepare_key(map, arguments->key);
1046
+ st_index_t hash = lru_key_hash(map, key);
1047
+ bool found;
1048
+
1049
+ (void)lru_find_slot(map, key, hash, &found);
1050
+ return key;
1051
+ }
1052
+
1053
+ static VALUE
1054
+ lru_preflight_key(VALUE self, VALUE key)
1055
+ {
1056
+ lru_map_t *map = lru_get(self);
1057
+ lru_arguments_t arguments = {.self = self, .map = map, .key = key};
1058
+ return lru_call_locked(map, lru_preflight_key_body, &arguments);
1059
+ }
1060
+
1061
+ static VALUE
1062
+ lru_key_p(VALUE self, VALUE key)
1063
+ {
1064
+ lru_map_t *map = lru_get(self);
1065
+ lru_arguments_t arguments = {.self = self, .map = map, .key = key};
1066
+ (void)lru_call_locked(map, lru_observe_body, &arguments);
1067
+ return arguments.found ? Qtrue : Qfalse;
1068
+ }
1069
+
1070
+ static VALUE
1071
+ lru_getkey(VALUE self, VALUE key)
1072
+ {
1073
+ lru_map_t *map = lru_get(self);
1074
+ lru_arguments_t arguments = {.self = self, .map = map, .key = key};
1075
+ return lru_call_locked(map, lru_observe_body, &arguments);
1076
+ }
1077
+
1078
+ static VALUE
1079
+ lru_delete_body(VALUE opaque)
1080
+ {
1081
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1082
+ lru_map_t *map = arguments->map;
1083
+ VALUE key;
1084
+ st_index_t hash;
1085
+ bool found;
1086
+ size_t slot;
1087
+
1088
+ lru_check_mutable(arguments->self, map);
1089
+ key = lru_prepare_key(map, arguments->key);
1090
+ hash = lru_key_hash(map, key);
1091
+ slot = lru_find_slot(map, key, hash, &found);
1092
+ lru_check_mutable(arguments->self, map);
1093
+ if (!found) return Qnil;
1094
+
1095
+ lru_entry_t *entry = map->slots[slot];
1096
+ VALUE result = entry->value;
1097
+ bounded_remove_entry(map, entry);
1098
+ return result;
1099
+ }
1100
+
1101
+ static VALUE
1102
+ lru_delete(VALUE self, VALUE key)
1103
+ {
1104
+ lru_map_t *map = lru_get(self);
1105
+ lru_arguments_t arguments = {.self = self, .map = map, .key = key};
1106
+ return lru_call_locked(map, lru_delete_body, &arguments);
1107
+ }
1108
+
1109
+ static VALUE
1110
+ lru_clear_body(VALUE opaque)
1111
+ {
1112
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1113
+ lru_map_t *map = arguments->map;
1114
+ lru_entry_t **slots;
1115
+
1116
+ lru_check_mutable(arguments->self, map);
1117
+ slots = lru_allocate_slots(LRU_INITIAL_CAPACITY);
1118
+ if (map->lfu_policy) lfu_free_buckets(map->least_frequency);
1119
+ else lru_free_entries(map->least_recent);
1120
+ ruby_xfree(map->slots);
1121
+ map->slots = slots;
1122
+ map->capacity = LRU_INITIAL_CAPACITY;
1123
+ map->size = 0;
1124
+ map->tombstones = 0;
1125
+ map->least_recent = NULL;
1126
+ map->most_recent = NULL;
1127
+ map->least_frequency = NULL;
1128
+ map->most_frequency = NULL;
1129
+ map->bucket_count = 0;
1130
+ return arguments->self;
1131
+ }
1132
+
1133
+ static VALUE
1134
+ lru_clear(VALUE self)
1135
+ {
1136
+ lru_map_t *map = lru_get(self);
1137
+ lru_arguments_t arguments = {.self = self, .map = map};
1138
+ return lru_call_locked(map, lru_clear_body, &arguments);
1139
+ }
1140
+
1141
+ static VALUE
1142
+ lru_size_body(VALUE opaque)
1143
+ {
1144
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1145
+ return SIZET2NUM(arguments->map->size);
1146
+ }
1147
+
1148
+ static VALUE
1149
+ lru_size(VALUE self)
1150
+ {
1151
+ lru_map_t *map = lru_get(self);
1152
+ lru_arguments_t arguments = {.self = self, .map = map};
1153
+ return lru_call_locked(map, lru_size_body, &arguments);
1154
+ }
1155
+
1156
+ static VALUE
1157
+ lru_empty_body(VALUE opaque)
1158
+ {
1159
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1160
+ return arguments->map->size == 0 ? Qtrue : Qfalse;
1161
+ }
1162
+
1163
+ static VALUE
1164
+ lru_empty_p(VALUE self)
1165
+ {
1166
+ lru_map_t *map = lru_get(self);
1167
+ lru_arguments_t arguments = {.self = self, .map = map};
1168
+ return lru_call_locked(map, lru_empty_body, &arguments);
1169
+ }
1170
+
1171
+ static VALUE
1172
+ lru_max_size_body(VALUE opaque)
1173
+ {
1174
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1175
+ return SIZET2NUM(arguments->map->max_size);
1176
+ }
1177
+
1178
+ static VALUE
1179
+ lru_max_size(VALUE self)
1180
+ {
1181
+ lru_map_t *map = lru_get(self);
1182
+ lru_arguments_t arguments = {.self = self, .map = map};
1183
+ return lru_call_locked(map, lru_max_size_body, &arguments);
1184
+ }
1185
+
1186
+ static void
1187
+ lru_prune_to(lru_map_t *map, size_t target)
1188
+ {
1189
+ while (map->size > target) {
1190
+ bounded_remove_entry(map, bounded_victim(map));
1191
+ }
1192
+ }
1193
+
1194
+ static VALUE
1195
+ lru_resize_limit_body(VALUE opaque)
1196
+ {
1197
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1198
+ lru_check_mutable(arguments->self, arguments->map);
1199
+ lru_prune_to(arguments->map, arguments->limit);
1200
+ arguments->map->max_size = arguments->limit;
1201
+ return arguments->result;
1202
+ }
1203
+
1204
+ static VALUE
1205
+ lru_set_max_size(VALUE self, VALUE limit)
1206
+ {
1207
+ lru_map_t *map = lru_get(self);
1208
+ lru_arguments_t arguments = {
1209
+ .self = self,
1210
+ .map = map,
1211
+ .limit = lru_parse_limit(limit, "max_size"),
1212
+ };
1213
+ arguments.result = SIZET2NUM(arguments.limit);
1214
+ return lru_call_locked(map, lru_resize_limit_body, &arguments);
1215
+ }
1216
+
1217
+ static VALUE
1218
+ lru_prune_body(VALUE opaque)
1219
+ {
1220
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1221
+ lru_check_mutable(arguments->self, arguments->map);
1222
+ size_t original = arguments->map->size;
1223
+ size_t removed = original > arguments->limit
1224
+ ? original - arguments->limit
1225
+ : 0;
1226
+ VALUE result = SIZET2NUM(removed);
1227
+ lru_prune_to(arguments->map, arguments->limit);
1228
+ return result;
1229
+ }
1230
+
1231
+ static VALUE
1232
+ lru_prune(int argc, VALUE *argv, VALUE self)
1233
+ {
1234
+ VALUE keywords = Qnil;
1235
+ VALUE keyword_values[1];
1236
+ ID keyword_ids[] = {rb_intern("to")};
1237
+
1238
+ rb_scan_args(argc, argv, "0:", &keywords);
1239
+ rb_get_kwargs(keywords, keyword_ids, 1, 0, keyword_values);
1240
+ lru_map_t *map = lru_get(self);
1241
+ lru_arguments_t arguments = {
1242
+ .self = self,
1243
+ .map = map,
1244
+ .limit = lru_parse_limit(keyword_values[0], "to"),
1245
+ };
1246
+ return lru_call_locked(map, lru_prune_body, &arguments);
1247
+ }
1248
+
1249
+ static VALUE
1250
+ lru_shift_body(VALUE opaque)
1251
+ {
1252
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1253
+ lru_map_t *map = arguments->map;
1254
+ lru_check_mutable(arguments->self, map);
1255
+ if (map->size == 0) return Qnil;
1256
+
1257
+ lru_entry_t *entry = bounded_victim(map);
1258
+ VALUE pair = rb_assoc_new(entry->key, entry->value);
1259
+ bounded_remove_entry(map, entry);
1260
+ return pair;
1261
+ }
1262
+
1263
+ static VALUE
1264
+ lru_shift(VALUE self)
1265
+ {
1266
+ lru_map_t *map = lru_get(self);
1267
+ lru_arguments_t arguments = {.self = self, .map = map};
1268
+ return lru_call_locked(map, lru_shift_body, &arguments);
1269
+ }
1270
+
1271
+ static VALUE
1272
+ lru_snapshot_body(VALUE opaque)
1273
+ {
1274
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1275
+ lru_map_t *map = arguments->map;
1276
+ if (map->size > (size_t)LONG_MAX / 2) {
1277
+ rb_raise(rb_eRangeError, "bounded map is too large to enumerate");
1278
+ }
1279
+
1280
+ VALUE entries = rb_ary_new_capa((long)(map->size * 2));
1281
+ if (map->lfu_policy) {
1282
+ for (lfu_bucket_t *bucket = map->least_frequency;
1283
+ bucket != NULL;
1284
+ bucket = bucket->following) {
1285
+ for (lru_entry_t *entry = bucket->least_recent;
1286
+ entry != NULL;
1287
+ entry = entry->following) {
1288
+ rb_ary_push(entries, entry->key);
1289
+ rb_ary_push(entries, entry->value);
1290
+ }
1291
+ }
1292
+ }
1293
+ else {
1294
+ for (lru_entry_t *entry = map->least_recent;
1295
+ entry != NULL;
1296
+ entry = entry->following) {
1297
+ rb_ary_push(entries, entry->key);
1298
+ rb_ary_push(entries, entry->value);
1299
+ }
1300
+ }
1301
+ return entries;
1302
+ }
1303
+
1304
+ static VALUE
1305
+ lru_enumerator_size(VALUE self, VALUE arguments, VALUE enumerator)
1306
+ {
1307
+ (void)arguments;
1308
+ (void)enumerator;
1309
+ return lru_size(self);
1310
+ }
1311
+
1312
+ static VALUE
1313
+ lru_each(VALUE self)
1314
+ {
1315
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, lru_enumerator_size);
1316
+ lru_map_t *map = lru_get(self);
1317
+ lru_arguments_t arguments = {.self = self, .map = map};
1318
+ VALUE entries = lru_call_locked(map, lru_snapshot_body, &arguments);
1319
+ long length = RARRAY_LEN(entries);
1320
+
1321
+ for (long index = 0; index < length; index += 2) {
1322
+ rb_yield(rb_assoc_new(
1323
+ RARRAY_AREF(entries, index),
1324
+ RARRAY_AREF(entries, index + 1)
1325
+ ));
1326
+ }
1327
+ RB_GC_GUARD(entries);
1328
+ return self;
1329
+ }
1330
+
1331
+ static VALUE
1332
+ lru_each_key(VALUE self)
1333
+ {
1334
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, lru_enumerator_size);
1335
+ lru_map_t *map = lru_get(self);
1336
+ lru_arguments_t arguments = {.self = self, .map = map};
1337
+ VALUE entries = lru_call_locked(map, lru_snapshot_body, &arguments);
1338
+ long length = RARRAY_LEN(entries);
1339
+
1340
+ for (long index = 0; index < length; index += 2) {
1341
+ rb_yield(RARRAY_AREF(entries, index));
1342
+ }
1343
+ RB_GC_GUARD(entries);
1344
+ return self;
1345
+ }
1346
+
1347
+ static VALUE
1348
+ lru_each_value(VALUE self)
1349
+ {
1350
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, lru_enumerator_size);
1351
+ lru_map_t *map = lru_get(self);
1352
+ lru_arguments_t arguments = {.self = self, .map = map};
1353
+ VALUE entries = lru_call_locked(map, lru_snapshot_body, &arguments);
1354
+ long length = RARRAY_LEN(entries);
1355
+
1356
+ for (long index = 1; index < length; index += 2) {
1357
+ rb_yield(RARRAY_AREF(entries, index));
1358
+ }
1359
+ RB_GC_GUARD(entries);
1360
+ return self;
1361
+ }
1362
+
1363
+ static VALUE
1364
+ lru_keys(VALUE self)
1365
+ {
1366
+ lru_map_t *map = lru_get(self);
1367
+ lru_arguments_t arguments = {.self = self, .map = map};
1368
+ VALUE entries = lru_call_locked(map, lru_snapshot_body, &arguments);
1369
+ long length = RARRAY_LEN(entries);
1370
+ VALUE keys = rb_ary_new_capa(length / 2);
1371
+
1372
+ for (long index = 0; index < length; index += 2) {
1373
+ rb_ary_push(keys, RARRAY_AREF(entries, index));
1374
+ }
1375
+ RB_GC_GUARD(entries);
1376
+ rb_obj_freeze(keys);
1377
+ return keys;
1378
+ }
1379
+
1380
+ static VALUE
1381
+ lru_compare_keys_by_identity_p(VALUE self)
1382
+ {
1383
+ return lru_get(self)->compare_keys_by_identity ? Qtrue : Qfalse;
1384
+ }
1385
+
1386
+ static VALUE
1387
+ lru_compare_values_by_identity_p(VALUE self)
1388
+ {
1389
+ return lru_get(self)->compare_values_by_identity ? Qtrue : Qfalse;
1390
+ }
1391
+
1392
+ static int
1393
+ lru_initialize_entry(VALUE key, VALUE value, VALUE opaque)
1394
+ {
1395
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1396
+ arguments->key = key;
1397
+ arguments->value = value;
1398
+ if (arguments->map->lfu_policy) {
1399
+ (void)lfu_store_body((VALUE)arguments);
1400
+ }
1401
+ else {
1402
+ (void)lru_store_body((VALUE)arguments);
1403
+ }
1404
+ return ST_CONTINUE;
1405
+ }
1406
+
1407
+ static void
1408
+ lru_check_initializable(VALUE self, lru_map_t *map)
1409
+ {
1410
+ if (RUBY_ATOMIC_LOAD(map->state) == LRU_INITIALIZED) {
1411
+ rb_raise(rb_eRuntimeError, "bounded map is already initialized");
1412
+ }
1413
+ rb_check_frozen(self);
1414
+ if (RUBY_ATOMIC_LOAD(map->state) != LRU_UNINITIALIZED) {
1415
+ rb_raise(rb_eRuntimeError, "bounded map is already initialized");
1416
+ }
1417
+ }
1418
+
1419
+ static VALUE
1420
+ lru_make_shareable(VALUE opaque)
1421
+ {
1422
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1423
+ return containers_publish_native_with_references(
1424
+ arguments->self,
1425
+ lru_validate_references
1426
+ );
1427
+ }
1428
+
1429
+ static VALUE
1430
+ lru_finish_publication(VALUE opaque)
1431
+ {
1432
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1433
+ rb_atomic_t state = rb_ractor_shareable_p(arguments->self)
1434
+ ? LRU_INITIALIZED
1435
+ : LRU_UNINITIALIZED;
1436
+
1437
+ RUBY_ATOMIC_SET(arguments->map->state, state);
1438
+ return Qnil;
1439
+ }
1440
+
1441
+ static VALUE
1442
+ lru_initialize_body(VALUE opaque)
1443
+ {
1444
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1445
+ lru_map_t *map = arguments->map;
1446
+ lru_entry_t **slots;
1447
+
1448
+ lru_check_initializable(arguments->self, map);
1449
+ slots = lru_allocate_slots(LRU_INITIAL_CAPACITY);
1450
+ if (map->lfu_policy) lfu_free_buckets(map->least_frequency);
1451
+ else lru_free_entries(map->least_recent);
1452
+ ruby_xfree(map->slots);
1453
+ map->slots = slots;
1454
+ map->capacity = LRU_INITIAL_CAPACITY;
1455
+ map->size = 0;
1456
+ map->tombstones = 0;
1457
+ map->max_size = arguments->limit;
1458
+ map->least_recent = NULL;
1459
+ map->most_recent = NULL;
1460
+ map->least_frequency = NULL;
1461
+ map->most_frequency = NULL;
1462
+ map->bucket_count = 0;
1463
+ map->compare_keys_by_identity = arguments->compare_keys_by_identity;
1464
+ map->compare_values_by_identity = arguments->compare_values_by_identity;
1465
+
1466
+ if (!NIL_P(arguments->value)) {
1467
+ rb_hash_foreach(arguments->value, lru_initialize_entry, opaque);
1468
+ }
1469
+
1470
+ if (!map->shareable_container) {
1471
+ RUBY_ATOMIC_SET(map->state, LRU_INITIALIZED);
1472
+ return arguments->self;
1473
+ }
1474
+
1475
+ RUBY_ATOMIC_SET(map->state, LRU_PUBLISHING);
1476
+ return rb_ensure(
1477
+ lru_make_shareable,
1478
+ opaque,
1479
+ lru_finish_publication,
1480
+ opaque
1481
+ );
1482
+ }
1483
+
1484
+ static VALUE
1485
+ lru_initialize(int argc, VALUE *argv, VALUE self)
1486
+ {
1487
+ VALUE entries = Qnil;
1488
+ VALUE keywords = Qnil;
1489
+ VALUE keyword_values[4] = {Qundef, Qundef, Qundef, Qundef};
1490
+ ID keyword_ids[] = {
1491
+ rb_intern("max_size"),
1492
+ rb_intern("compare_by_identity"),
1493
+ rb_intern("compare_keys_by_identity"),
1494
+ rb_intern("compare_values_by_identity"),
1495
+ };
1496
+ lru_map_t *map = lru_get_raw(self);
1497
+
1498
+ rb_scan_args(argc, argv, "01:", &entries, &keywords);
1499
+ lru_check_initializable(self, map);
1500
+ rb_get_kwargs(keywords, keyword_ids, 1, 3, keyword_values);
1501
+
1502
+ bool common = keyword_values[1] == Qundef
1503
+ ? false
1504
+ : containers_strict_bool(keyword_values[1], "compare_by_identity");
1505
+ bool compare_keys_by_identity = keyword_values[2] == Qundef
1506
+ ? common
1507
+ : containers_strict_bool(keyword_values[2], "compare_keys_by_identity");
1508
+ bool compare_values_by_identity = keyword_values[3] == Qundef
1509
+ ? common
1510
+ : containers_strict_bool(keyword_values[3], "compare_values_by_identity");
1511
+ size_t max_size = lru_parse_limit(keyword_values[0], "max_size");
1512
+
1513
+ if (!NIL_P(entries)) {
1514
+ entries = rb_check_hash_type(entries);
1515
+ if (NIL_P(entries)) {
1516
+ rb_raise(rb_eTypeError, "entries must be a Hash or respond to #to_hash");
1517
+ }
1518
+ }
1519
+ lru_check_initializable(self, map);
1520
+
1521
+ lru_arguments_t arguments = {
1522
+ .self = self,
1523
+ .map = map,
1524
+ .value = entries,
1525
+ .limit = max_size,
1526
+ .compare_keys_by_identity = compare_keys_by_identity,
1527
+ .compare_values_by_identity = compare_values_by_identity,
1528
+ };
1529
+ return lru_call_locked(map, lru_initialize_body, &arguments);
1530
+ }
1531
+
1532
+ static VALUE
1533
+ lru_copy_body(VALUE opaque)
1534
+ {
1535
+ lru_arguments_t *arguments = (lru_arguments_t *)opaque;
1536
+ lru_map_t *source = lru_get(arguments->value);
1537
+ lru_map_t *copy = arguments->map;
1538
+ VALUE self = arguments->self;
1539
+
1540
+ lru_check_initializable(self, copy);
1541
+ lru_entry_t **slots = lru_allocate_slots(source->capacity);
1542
+ if (copy->lfu_policy) lfu_free_buckets(copy->least_frequency);
1543
+ else lru_free_entries(copy->least_recent);
1544
+ ruby_xfree(copy->slots);
1545
+ copy->slots = slots;
1546
+ copy->capacity = source->capacity;
1547
+ copy->size = 0;
1548
+ copy->tombstones = 0;
1549
+ copy->max_size = source->max_size;
1550
+ copy->least_recent = NULL;
1551
+ copy->most_recent = NULL;
1552
+ copy->least_frequency = NULL;
1553
+ copy->most_frequency = NULL;
1554
+ copy->bucket_count = 0;
1555
+ copy->compare_keys_by_identity = source->compare_keys_by_identity;
1556
+ copy->compare_values_by_identity = source->compare_values_by_identity;
1557
+
1558
+ if (source->lfu_policy) {
1559
+ for (lfu_bucket_t *source_bucket = source->least_frequency;
1560
+ source_bucket != NULL;
1561
+ source_bucket = source_bucket->following) {
1562
+ lfu_bucket_t *bucket = lfu_allocate_bucket(self, source_bucket->frequency);
1563
+ lfu_link_bucket_after(copy, copy->most_frequency, bucket);
1564
+ for (lru_entry_t *source_entry = source_bucket->least_recent;
1565
+ source_entry != NULL;
1566
+ source_entry = source_entry->following) {
1567
+ lfu_entry_t *entry = ALLOC(lfu_entry_t);
1568
+ entry->base.key = Qnil;
1569
+ entry->base.value = Qnil;
1570
+ entry->base.hash = source_entry->hash;
1571
+ entry->base.previous = NULL;
1572
+ entry->base.following = NULL;
1573
+ entry->bucket = NULL;
1574
+ RB_OBJ_WRITE(self, &entry->base.key, source_entry->key);
1575
+ RB_OBJ_WRITE(self, &entry->base.value, source_entry->value);
1576
+ lru_place_resized_entry(copy->slots, copy->capacity - 1, &entry->base);
1577
+ lfu_append_entry(bucket, &entry->base);
1578
+ copy->size++;
1579
+ }
1580
+ }
1581
+ }
1582
+ else {
1583
+ for (lru_entry_t *source_entry = source->least_recent;
1584
+ source_entry != NULL;
1585
+ source_entry = source_entry->following) {
1586
+ lru_entry_t *entry = ALLOC(lru_entry_t);
1587
+ entry->key = Qnil;
1588
+ entry->value = Qnil;
1589
+ entry->hash = source_entry->hash;
1590
+ entry->previous = NULL;
1591
+ entry->following = NULL;
1592
+ RB_OBJ_WRITE(self, &entry->key, source_entry->key);
1593
+ RB_OBJ_WRITE(self, &entry->value, source_entry->value);
1594
+ lru_place_resized_entry(copy->slots, copy->capacity - 1, entry);
1595
+ lru_append(copy, entry);
1596
+ copy->size++;
1597
+ }
1598
+ }
1599
+
1600
+ if (!copy->shareable_container) {
1601
+ RUBY_ATOMIC_SET(copy->state, LRU_INITIALIZED);
1602
+ return self;
1603
+ }
1604
+
1605
+ RUBY_ATOMIC_SET(copy->state, LRU_PUBLISHING);
1606
+ return rb_ensure(lru_make_shareable, opaque, lru_finish_publication, opaque);
1607
+ }
1608
+
1609
+ static VALUE
1610
+ lru_initialize_copy(VALUE self, VALUE other)
1611
+ {
1612
+ lru_map_t *source = lru_get(other);
1613
+ lru_map_t *copy = lru_get_raw(self);
1614
+ lru_arguments_t arguments = {
1615
+ .self = self,
1616
+ .map = copy,
1617
+ .value = other,
1618
+ };
1619
+
1620
+ lru_check_initializable(self, copy);
1621
+ if (source->lfu_policy != copy->lfu_policy ||
1622
+ source->shareable_container != copy->shareable_container) {
1623
+ rb_raise(rb_eTypeError, "incompatible bounded map copy");
1624
+ }
1625
+ return lru_call_locked(source, lru_copy_body, &arguments);
1626
+ }
1627
+
1628
+ static void
1629
+ lru_define_methods(VALUE klass, bool lfu_policy)
1630
+ {
1631
+ rb_define_method(klass, "initialize", lru_initialize, -1);
1632
+ rb_define_method(klass, "initialize_copy", lru_initialize_copy, 1);
1633
+ rb_define_method(klass, "[]", lfu_policy ? lfu_aref : lru_aref, 1);
1634
+ rb_define_method(klass, "[]=", lfu_policy ? lfu_store : lru_store, 2);
1635
+ rb_define_method(klass, "fetch", lfu_policy ? lfu_fetch : lru_fetch, -1);
1636
+ rb_define_method(klass, "prepare_key", lru_preflight_key, 1);
1637
+ rb_define_method(klass, "key?", lru_key_p, 1);
1638
+ rb_define_method(klass, "getkey", lru_getkey, 1);
1639
+ rb_define_method(klass, "delete", lru_delete, 1);
1640
+ rb_define_method(klass, "clear", lru_clear, 0);
1641
+ rb_define_method(klass, "max_size", lru_max_size, 0);
1642
+ rb_define_method(klass, "max_size=", lru_set_max_size, 1);
1643
+ rb_define_method(klass, "prune", lru_prune, -1);
1644
+ rb_define_method(klass, "shift", lru_shift, 0);
1645
+ rb_define_method(klass, "size", lru_size, 0);
1646
+ rb_define_method(klass, "length", lru_size, 0);
1647
+ rb_define_method(klass, "empty?", lru_empty_p, 0);
1648
+ rb_define_method(klass, "compare_keys_by_identity?", lru_compare_keys_by_identity_p, 0);
1649
+ rb_define_method(klass, "compare_values_by_identity?", lru_compare_values_by_identity_p, 0);
1650
+ rb_define_method(klass, "each", lru_each, 0);
1651
+ rb_define_method(klass, "each_pair", lru_each, 0);
1652
+ rb_define_method(klass, "each_key", lru_each_key, 0);
1653
+ rb_define_method(klass, "each_value", lru_each_value, 0);
1654
+ rb_define_method(klass, "keys", lru_keys, 0);
1655
+ }
1656
+
1657
+ void
1658
+ containers_init_lru_maps(VALUE internal)
1659
+ {
1660
+ cLRUMap = rb_define_class_under(internal, "LRUMap", rb_cObject);
1661
+ rb_define_alloc_func(cLRUMap, lru_local_allocate);
1662
+ lru_define_methods(cLRUMap, false);
1663
+
1664
+ cShareableLRUMap = rb_define_class_under(
1665
+ internal,
1666
+ "ShareableLRUMap",
1667
+ rb_cObject
1668
+ );
1669
+ rb_define_alloc_func(cShareableLRUMap, lru_shared_allocate);
1670
+ lru_define_methods(cShareableLRUMap, false);
1671
+
1672
+ cLFUMap = rb_define_class_under(internal, "LFUMap", rb_cObject);
1673
+ rb_define_alloc_func(cLFUMap, lfu_local_allocate);
1674
+ lru_define_methods(cLFUMap, true);
1675
+
1676
+ cShareableLFUMap = rb_define_class_under(
1677
+ internal,
1678
+ "ShareableLFUMap",
1679
+ rb_cObject
1680
+ );
1681
+ rb_define_alloc_func(cShareableLFUMap, lfu_shared_allocate);
1682
+ lru_define_methods(cShareableLFUMap, true);
1683
+ }