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,1714 @@
1
+ #include "containers.h"
2
+
3
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
4
+
5
+ #include "ruby/fiber/scheduler.h"
6
+ #include "ruby/io.h"
7
+
8
+ #include <errno.h>
9
+ #include <fcntl.h>
10
+ #include <math.h>
11
+ #include <stdint.h>
12
+ #include <stdlib.h>
13
+ #include <time.h>
14
+ #include <unistd.h>
15
+
16
+ #define WEAK_MAP_INITIAL_CAPACITY 16
17
+
18
+ /* These functions are exported by Rubies with the probed typed-data weak
19
+ * reference callback, but their declarations intentionally remain internal. */
20
+ void rb_gc_declare_weak_references(VALUE object);
21
+ bool rb_gc_handle_weak_references_alive_p(VALUE object);
22
+
23
+ static VALUE cWeakKeyMap;
24
+ static VALUE weak_map_keep;
25
+ static VALUE weak_map_delete_token;
26
+ static VALUE cWeakValueMap;
27
+ static VALUE cWeakMap;
28
+ static VALUE eIsolationError;
29
+
30
+ typedef enum {
31
+ WEAK_MAP_EMPTY = 0,
32
+ WEAK_MAP_OCCUPIED = 1,
33
+ WEAK_MAP_TOMBSTONE = 2
34
+ } weak_map_slot_state_t;
35
+
36
+ typedef struct {
37
+ VALUE key;
38
+ VALUE value;
39
+ st_index_t hash;
40
+ weak_map_slot_state_t state;
41
+ } weak_map_slot_t;
42
+
43
+ typedef struct weak_map_waiter {
44
+ int read_fd;
45
+ int write_fd;
46
+ bool notified;
47
+ struct weak_map_waiter *next;
48
+ } weak_map_waiter_t;
49
+
50
+ /* Owned by the active operation's C stack, including while its Fiber sleeps. */
51
+ typedef struct weak_map_reservation {
52
+ VALUE key;
53
+ VALUE current;
54
+ st_index_t hash;
55
+ VALUE fiber;
56
+ VALUE thread;
57
+ bool invalidated;
58
+ struct weak_map_reservation *next;
59
+ } weak_map_reservation_t;
60
+
61
+ typedef struct {
62
+ pthread_mutex_t lock;
63
+ weak_map_slot_t *slots;
64
+ size_t capacity;
65
+ size_t size;
66
+ size_t tombstones;
67
+ uint64_t generation;
68
+ uint64_t iteration_epoch;
69
+ weak_map_waiter_t *waiters;
70
+ bool compare_keys_by_identity;
71
+ bool compare_values_by_identity;
72
+ bool weak_keys;
73
+ bool weak_values;
74
+ bool comparing;
75
+ VALUE comparing_owner;
76
+ VALUE comparing_thread;
77
+ weak_map_reservation_t *reservations;
78
+ bool initialized;
79
+ } weak_map_t;
80
+
81
+ typedef struct {
82
+ bool finite;
83
+ double deadline;
84
+ } weak_map_timeout_t;
85
+
86
+ typedef struct {
87
+ VALUE fiber;
88
+ VALUE thread;
89
+ VALUE scheduler;
90
+ } weak_map_execution_context_t;
91
+
92
+ static double
93
+ weak_map_monotonic_now(void)
94
+ {
95
+ struct timespec now;
96
+ #ifdef CLOCK_MONOTONIC
97
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
98
+ return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
99
+ }
100
+ #endif
101
+ struct timeval fallback;
102
+ gettimeofday(&fallback, NULL);
103
+ return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
104
+ }
105
+
106
+ static weak_map_timeout_t
107
+ weak_map_parse_timeout(VALUE timeout)
108
+ {
109
+ weak_map_timeout_t parsed = {.finite = false, .deadline = 0};
110
+ if (NIL_P(timeout)) return parsed;
111
+ double seconds = NUM2DBL(timeout);
112
+ if (!isfinite(seconds) || seconds < 0) {
113
+ rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
114
+ }
115
+ parsed.finite = true;
116
+ parsed.deadline = weak_map_monotonic_now() + seconds;
117
+ return parsed;
118
+ }
119
+
120
+ static void
121
+ weak_map_set_fd_flags(int fd)
122
+ {
123
+ #ifdef _WIN32
124
+ (void)fd;
125
+ #else
126
+ int flags = fcntl(fd, F_GETFL, 0);
127
+ if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
128
+ flags = fcntl(fd, F_GETFD, 0);
129
+ if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
130
+ #endif
131
+ }
132
+
133
+ static void
134
+ weak_map_waiter_initialize(weak_map_waiter_t *waiter)
135
+ {
136
+ int descriptors[2];
137
+ if (pipe(descriptors) != 0) rb_sys_fail("pipe");
138
+ waiter->read_fd = descriptors[0];
139
+ waiter->write_fd = descriptors[1];
140
+ waiter->notified = false;
141
+ waiter->next = NULL;
142
+ weak_map_set_fd_flags(waiter->read_fd);
143
+ weak_map_set_fd_flags(waiter->write_fd);
144
+ }
145
+
146
+ static void
147
+ weak_map_notify_waiters_locked(weak_map_t *map)
148
+ {
149
+ unsigned char byte = 1;
150
+ map->generation++;
151
+ for (weak_map_waiter_t *waiter = map->waiters; waiter; waiter = waiter->next) {
152
+ if (waiter->notified) continue;
153
+ ssize_t result;
154
+ do {
155
+ result = write(waiter->write_fd, &byte, 1);
156
+ } while (result < 0 && errno == EINTR);
157
+ if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
158
+ waiter->notified = true;
159
+ }
160
+ }
161
+ }
162
+
163
+ static void
164
+ weak_map_mark(void *pointer)
165
+ {
166
+ weak_map_t *map = pointer;
167
+ rb_gc_mark_movable(map->comparing_owner);
168
+ rb_gc_mark_movable(map->comparing_thread);
169
+ for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
170
+ rb_gc_mark_movable(entry->key);
171
+ rb_gc_mark_movable(entry->current);
172
+ rb_gc_mark_movable(entry->fiber);
173
+ rb_gc_mark_movable(entry->thread);
174
+ }
175
+ if (!map->slots) return;
176
+
177
+ for (size_t index = 0; index < map->capacity; index++) {
178
+ weak_map_slot_t *slot = &map->slots[index];
179
+ if (slot->state != WEAK_MAP_OCCUPIED) continue;
180
+ if (!map->weak_keys) rb_gc_mark_movable(slot->key);
181
+ if (!map->weak_values) rb_gc_mark_movable(slot->value);
182
+ }
183
+ }
184
+
185
+ static void
186
+ weak_map_compact(void *pointer)
187
+ {
188
+ weak_map_t *map = pointer;
189
+ map->comparing_owner = rb_gc_location(map->comparing_owner);
190
+ map->comparing_thread = rb_gc_location(map->comparing_thread);
191
+ for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
192
+ entry->key = rb_gc_location(entry->key);
193
+ entry->current = rb_gc_location(entry->current);
194
+ entry->fiber = rb_gc_location(entry->fiber);
195
+ entry->thread = rb_gc_location(entry->thread);
196
+ }
197
+ if (!map->slots) return;
198
+
199
+ for (size_t index = 0; index < map->capacity; index++) {
200
+ weak_map_slot_t *slot = &map->slots[index];
201
+ if (slot->state != WEAK_MAP_OCCUPIED) continue;
202
+ slot->key = rb_gc_location(slot->key);
203
+ slot->value = rb_gc_location(slot->value);
204
+ }
205
+ }
206
+
207
+ /* The VM invokes this after marking while mutators are stopped. Taking the map
208
+ * mutex here could deadlock if GC stopped a mutator at a safepoint while it
209
+ * owned the mutex, so cleanup deliberately runs lock-free. */
210
+ static void
211
+ weak_map_handle_weak_references(void *pointer)
212
+ {
213
+ weak_map_t *map = pointer;
214
+ if (!map->slots) return;
215
+ bool changed = false;
216
+
217
+ for (size_t index = 0; index < map->capacity; index++) {
218
+ weak_map_slot_t *slot = &map->slots[index];
219
+ if (slot->state != WEAK_MAP_OCCUPIED) continue;
220
+
221
+ bool key_alive = !map->weak_keys || rb_gc_handle_weak_references_alive_p(slot->key);
222
+ bool value_alive = !map->weak_values || rb_gc_handle_weak_references_alive_p(slot->value);
223
+ if (key_alive && value_alive) continue;
224
+
225
+ slot->key = Qnil;
226
+ slot->value = Qnil;
227
+ slot->state = WEAK_MAP_TOMBSTONE;
228
+ map->size--;
229
+ map->tombstones++;
230
+ changed = true;
231
+ }
232
+ if (changed) weak_map_notify_waiters_locked(map);
233
+ }
234
+
235
+ static void
236
+ weak_map_free(void *pointer)
237
+ {
238
+ weak_map_t *map = pointer;
239
+ pthread_mutex_destroy(&map->lock);
240
+ free(map->slots);
241
+ ruby_xfree(map);
242
+ }
243
+
244
+ static size_t
245
+ weak_map_memsize(const void *pointer)
246
+ {
247
+ const weak_map_t *map = pointer;
248
+ return map ? sizeof(weak_map_t) + map->capacity * sizeof(weak_map_slot_t) : 0;
249
+ }
250
+
251
+ static const rb_data_type_t weak_map_type = {
252
+ .wrap_struct_name = "Ractor::Containers::WeakMap",
253
+ .function = {
254
+ .dmark = weak_map_mark,
255
+ .dfree = weak_map_free,
256
+ .dsize = weak_map_memsize,
257
+ .dcompact = weak_map_compact,
258
+ .handle_weak_references = weak_map_handle_weak_references,
259
+ },
260
+ .flags = RUBY_TYPED_FROZEN_SHAREABLE,
261
+ };
262
+
263
+ static void
264
+ weak_map_validate_references(VALUE self)
265
+ {
266
+ weak_map_t *map;
267
+ TypedData_Get_Struct(self, weak_map_t, &weak_map_type, map);
268
+ for (size_t index = 0; index < map->capacity; index++) {
269
+ weak_map_slot_t *slot = &map->slots[index];
270
+ if (slot->state != WEAK_MAP_OCCUPIED) continue;
271
+ containers_check_shareable(slot->key);
272
+ containers_check_shareable(slot->value);
273
+ }
274
+ }
275
+
276
+ static weak_map_slot_t *
277
+ weak_map_allocate_slots(size_t capacity)
278
+ {
279
+ weak_map_slot_t *slots = calloc(capacity, sizeof(weak_map_slot_t));
280
+ if (!slots) rb_memerror();
281
+ return slots;
282
+ }
283
+
284
+ static weak_map_slot_t *
285
+ weak_map_allocate_slots_locked(weak_map_t *map, size_t capacity)
286
+ {
287
+ weak_map_slot_t *slots = calloc(capacity, sizeof(weak_map_slot_t));
288
+ if (!slots) {
289
+ pthread_mutex_unlock(&map->lock);
290
+ rb_memerror();
291
+ }
292
+ return slots;
293
+ }
294
+
295
+ static VALUE
296
+ weak_map_allocate(VALUE klass)
297
+ {
298
+ weak_map_t *map;
299
+ VALUE object = TypedData_Make_Struct(klass, weak_map_t, &weak_map_type, map);
300
+ pthread_mutex_init(&map->lock, NULL);
301
+ map->slots = NULL;
302
+ map->capacity = 0;
303
+ map->size = 0;
304
+ map->tombstones = 0;
305
+ map->generation = 0;
306
+ map->iteration_epoch = 0;
307
+ map->waiters = NULL;
308
+ map->compare_keys_by_identity = false;
309
+ map->compare_values_by_identity = false;
310
+ map->weak_keys = klass == cWeakKeyMap || klass == cWeakMap;
311
+ map->weak_values = klass == cWeakValueMap || klass == cWeakMap;
312
+ map->comparing = false;
313
+ map->comparing_owner = Qnil;
314
+ map->comparing_thread = Qnil;
315
+ map->reservations = NULL;
316
+ map->initialized = false;
317
+ return object;
318
+ }
319
+
320
+ static weak_map_t *
321
+ get_weak_map(VALUE self)
322
+ {
323
+ weak_map_t *map;
324
+ TypedData_Get_Struct(self, weak_map_t, &weak_map_type, map);
325
+ if (!map->initialized) rb_raise(rb_eRuntimeError, "uninitialized weak map");
326
+ return map;
327
+ }
328
+
329
+ typedef struct {
330
+ weak_map_t *map;
331
+ weak_map_waiter_t *waiter;
332
+ weak_map_timeout_t *timeout;
333
+ bool registered;
334
+ } weak_map_wait_context_t;
335
+
336
+ static VALUE
337
+ weak_map_wait_body(VALUE opaque)
338
+ {
339
+ weak_map_wait_context_t *context = (weak_map_wait_context_t *)opaque;
340
+ VALUE wait_timeout = Qnil;
341
+ if (context->timeout->finite) {
342
+ double remaining = context->timeout->deadline - weak_map_monotonic_now();
343
+ if (remaining <= 0) return Qfalse;
344
+ wait_timeout = DBL2NUM(remaining);
345
+ }
346
+ return containers_wait_for_readable(context->waiter->read_fd, wait_timeout) ? Qtrue : Qfalse;
347
+ }
348
+
349
+ static VALUE
350
+ weak_map_wait_cleanup(VALUE opaque)
351
+ {
352
+ weak_map_wait_context_t *context = (weak_map_wait_context_t *)opaque;
353
+ if (context->registered) {
354
+ pthread_mutex_lock(&context->map->lock);
355
+ weak_map_waiter_t **cursor = &context->map->waiters;
356
+ while (*cursor && *cursor != context->waiter) cursor = &(*cursor)->next;
357
+ if (*cursor) *cursor = context->waiter->next;
358
+ pthread_mutex_unlock(&context->map->lock);
359
+ context->registered = false;
360
+ }
361
+ close(context->waiter->read_fd);
362
+ close(context->waiter->write_fd);
363
+ return Qnil;
364
+ }
365
+
366
+ static bool
367
+ weak_map_wait_once(weak_map_t *map, uint64_t generation, weak_map_timeout_t *timeout)
368
+ {
369
+ weak_map_waiter_t waiter;
370
+ weak_map_waiter_initialize(&waiter);
371
+
372
+ pthread_mutex_lock(&map->lock);
373
+ if (map->generation != generation) {
374
+ pthread_mutex_unlock(&map->lock);
375
+ close(waiter.read_fd);
376
+ close(waiter.write_fd);
377
+ return true;
378
+ }
379
+ waiter.next = map->waiters;
380
+ map->waiters = &waiter;
381
+ pthread_mutex_unlock(&map->lock);
382
+
383
+ weak_map_wait_context_t context = {
384
+ .map = map,
385
+ .waiter = &waiter,
386
+ .timeout = timeout,
387
+ .registered = true,
388
+ };
389
+ return RTEST(rb_ensure(
390
+ weak_map_wait_body,
391
+ (VALUE)&context,
392
+ weak_map_wait_cleanup,
393
+ (VALUE)&context
394
+ ));
395
+ }
396
+
397
+ static void
398
+ weak_map_lock_state(weak_map_t *map, weak_map_execution_context_t *execution)
399
+ {
400
+ weak_map_timeout_t timeout = {.finite = false, .deadline = 0};
401
+ execution->fiber = rb_fiber_current();
402
+ execution->thread = rb_thread_current();
403
+ execution->scheduler = rb_fiber_scheduler_current();
404
+
405
+ for (;;) {
406
+ pthread_mutex_lock(&map->lock);
407
+ if (!map->comparing) return;
408
+
409
+ if (map->comparing_owner == execution->fiber) {
410
+ pthread_mutex_unlock(&map->lock);
411
+ rb_raise(rb_eThreadError, "recursive weak-map access from key equality");
412
+ }
413
+ if (map->comparing_thread == execution->thread &&
414
+ NIL_P(execution->scheduler)) {
415
+ pthread_mutex_unlock(&map->lock);
416
+ rb_raise(
417
+ rb_eThreadError,
418
+ "deadlock; weak-map key equality is owned by another unscheduled fiber"
419
+ );
420
+ }
421
+
422
+ uint64_t generation = map->generation;
423
+ pthread_mutex_unlock(&map->lock);
424
+ (void)weak_map_wait_once(map, generation, &timeout);
425
+ }
426
+ }
427
+
428
+ static VALUE
429
+ weak_map_timeout_result(void)
430
+ {
431
+ return rb_block_given_p() ? rb_yield_values(0) : Qnil;
432
+ }
433
+
434
+ static st_index_t
435
+ weak_map_key_hash(weak_map_t *map, VALUE key)
436
+ {
437
+ if (map->compare_keys_by_identity) {
438
+ return (st_index_t)NUM2ULL(rb_obj_id(key));
439
+ }
440
+ return (st_index_t)NUM2LONG(rb_hash(key));
441
+ }
442
+
443
+ typedef struct {
444
+ VALUE left;
445
+ VALUE right;
446
+ } weak_map_eql_arguments_t;
447
+
448
+ static VALUE
449
+ weak_map_eql_protected(VALUE opaque)
450
+ {
451
+ weak_map_eql_arguments_t *arguments = (weak_map_eql_arguments_t *)opaque;
452
+ return rb_eql(arguments->left, arguments->right) ? Qtrue : Qfalse;
453
+ }
454
+
455
+ static bool
456
+ weak_map_keys_equal(
457
+ weak_map_t *map,
458
+ VALUE left,
459
+ VALUE right,
460
+ bool *stale,
461
+ const weak_map_execution_context_t *execution
462
+ )
463
+ {
464
+ *stale = false;
465
+ if (map->compare_keys_by_identity) return left == right;
466
+
467
+ weak_map_eql_arguments_t arguments = {.left = left, .right = right};
468
+ uint64_t generation = map->generation;
469
+ int state = 0;
470
+
471
+ map->comparing = true;
472
+ map->comparing_owner = execution->fiber;
473
+ map->comparing_thread = execution->thread;
474
+ pthread_mutex_unlock(&map->lock);
475
+
476
+ VALUE result = rb_protect(weak_map_eql_protected, (VALUE)&arguments, &state);
477
+
478
+ pthread_mutex_lock(&map->lock);
479
+ *stale = map->generation != generation;
480
+ map->comparing = false;
481
+ map->comparing_owner = Qnil;
482
+ map->comparing_thread = Qnil;
483
+ weak_map_notify_waiters_locked(map);
484
+ if (state) {
485
+ pthread_mutex_unlock(&map->lock);
486
+ rb_jump_tag(state);
487
+ }
488
+ return RTEST(result);
489
+ }
490
+
491
+ static weak_map_reservation_t *
492
+ weak_map_find_reservation_locked(
493
+ weak_map_t *map, VALUE key, st_index_t hash,
494
+ const weak_map_execution_context_t *execution
495
+ )
496
+ {
497
+ for (;;) {
498
+ bool restart = false;
499
+ for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
500
+ if (entry->invalidated || entry->hash != hash) continue;
501
+ bool stale;
502
+ bool equal = weak_map_keys_equal(map, entry->key, key, &stale, execution);
503
+ if (stale) {
504
+ restart = true;
505
+ break;
506
+ }
507
+ if (equal) return entry;
508
+ }
509
+ if (!restart) return NULL;
510
+ }
511
+ }
512
+
513
+ static void
514
+ weak_map_check_reservation_wait_locked(
515
+ weak_map_t *map, weak_map_reservation_t *entry,
516
+ const weak_map_execution_context_t *execution
517
+ )
518
+ {
519
+ if (entry->fiber == execution->fiber) {
520
+ pthread_mutex_unlock(&map->lock);
521
+ rb_raise(rb_eThreadError, "deadlock; recursive weak-map access during an update");
522
+ }
523
+ if (entry->thread == execution->thread && NIL_P(execution->scheduler)) {
524
+ pthread_mutex_unlock(&map->lock);
525
+ rb_raise(rb_eThreadError, "deadlock; weak-map update is owned by another unscheduled fiber");
526
+ }
527
+ }
528
+
529
+ static bool
530
+ weak_map_lock_for_key_with_timeout(
531
+ weak_map_t *map, VALUE key, st_index_t hash,
532
+ weak_map_timeout_t *timeout, weak_map_execution_context_t *execution
533
+ )
534
+ {
535
+ for (;;) {
536
+ weak_map_lock_state(map, execution);
537
+ weak_map_reservation_t *entry = weak_map_find_reservation_locked(map, key, hash, execution);
538
+ if (!entry) return true;
539
+ weak_map_check_reservation_wait_locked(map, entry, execution);
540
+ uint64_t generation = map->generation;
541
+ pthread_mutex_unlock(&map->lock);
542
+ if (!weak_map_wait_once(map, generation, timeout)) return false;
543
+ }
544
+ }
545
+
546
+ static void
547
+ weak_map_lock_for_key(
548
+ weak_map_t *map, VALUE key, st_index_t hash, weak_map_execution_context_t *execution
549
+ )
550
+ {
551
+ weak_map_timeout_t timeout = {.finite = false, .deadline = 0};
552
+ (void)weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, execution);
553
+ }
554
+
555
+ static void
556
+ weak_map_begin_reservation_locked(
557
+ weak_map_t *map, weak_map_reservation_t *entry, VALUE key, VALUE current,
558
+ st_index_t hash, const weak_map_execution_context_t *execution
559
+ )
560
+ {
561
+ entry->key = key;
562
+ entry->current = current;
563
+ entry->hash = hash;
564
+ entry->fiber = execution->fiber;
565
+ entry->thread = execution->thread;
566
+ entry->invalidated = false;
567
+ entry->next = map->reservations;
568
+ map->reservations = entry;
569
+ }
570
+
571
+ static void
572
+ weak_map_finish_reservation_locked(weak_map_t *map, weak_map_reservation_t *entry)
573
+ {
574
+ weak_map_reservation_t **cursor = &map->reservations;
575
+ while (*cursor && *cursor != entry) cursor = &(*cursor)->next;
576
+ if (*cursor) *cursor = entry->next;
577
+ weak_map_notify_waiters_locked(map);
578
+ }
579
+
580
+ static size_t
581
+ weak_map_find_slot(
582
+ weak_map_t *map,
583
+ VALUE key,
584
+ st_index_t hash,
585
+ bool *found,
586
+ const weak_map_execution_context_t *execution
587
+ )
588
+ {
589
+ for (;;) {
590
+ size_t mask = map->capacity - 1;
591
+ size_t first_tombstone = SIZE_MAX;
592
+ bool restart = false;
593
+
594
+ for (size_t offset = 0; offset < map->capacity; offset++) {
595
+ size_t index = ((size_t)hash + offset) & mask;
596
+ weak_map_slot_t *slot = &map->slots[index];
597
+
598
+ if (slot->state == WEAK_MAP_EMPTY) {
599
+ *found = false;
600
+ return first_tombstone == SIZE_MAX ? index : first_tombstone;
601
+ }
602
+ if (slot->state == WEAK_MAP_TOMBSTONE) {
603
+ if (first_tombstone == SIZE_MAX) first_tombstone = index;
604
+ continue;
605
+ }
606
+ if (slot->hash == hash) {
607
+ bool stale;
608
+ bool equal = weak_map_keys_equal(
609
+ map,
610
+ slot->key,
611
+ key,
612
+ &stale,
613
+ execution
614
+ );
615
+
616
+ if (stale) {
617
+ restart = true;
618
+ break;
619
+ }
620
+ if (equal) {
621
+ *found = true;
622
+ return index;
623
+ }
624
+ }
625
+ }
626
+
627
+ if (restart) continue;
628
+ *found = false;
629
+ return first_tombstone;
630
+ }
631
+ }
632
+
633
+ static void
634
+ weak_map_resize(weak_map_t *map, size_t new_capacity)
635
+ {
636
+ map->iteration_epoch++;
637
+ weak_map_slot_t *old_slots = map->slots;
638
+ size_t old_capacity = map->capacity;
639
+ map->slots = weak_map_allocate_slots_locked(map, new_capacity);
640
+ map->capacity = new_capacity;
641
+ map->size = 0;
642
+ map->tombstones = 0;
643
+
644
+ for (size_t index = 0; index < old_capacity; index++) {
645
+ weak_map_slot_t *old = &old_slots[index];
646
+ if (old->state != WEAK_MAP_OCCUPIED) continue;
647
+
648
+ size_t mask = map->capacity - 1;
649
+ size_t target = (size_t)old->hash & mask;
650
+ while (map->slots[target].state == WEAK_MAP_OCCUPIED) {
651
+ target = (target + 1) & mask;
652
+ }
653
+ map->slots[target] = *old;
654
+ map->size++;
655
+ }
656
+ free(old_slots);
657
+ }
658
+
659
+ static void
660
+ weak_map_prepare_insert(weak_map_t *map)
661
+ {
662
+ if ((map->size + map->tombstones + 1) * 10 >= map->capacity * 7) {
663
+ weak_map_resize(map, map->capacity * 2);
664
+ }
665
+ }
666
+
667
+ static VALUE
668
+ weak_map_store_locked(
669
+ VALUE self,
670
+ weak_map_t *map,
671
+ VALUE key,
672
+ VALUE value,
673
+ st_index_t hash,
674
+ const weak_map_execution_context_t *execution
675
+ )
676
+ {
677
+ bool found;
678
+ weak_map_prepare_insert(map);
679
+ size_t index = weak_map_find_slot(map, key, hash, &found, execution);
680
+ if (RB_OBJ_FROZEN(self)) {
681
+ pthread_mutex_unlock(&map->lock);
682
+ rb_check_frozen(self);
683
+ }
684
+ weak_map_slot_t *slot = &map->slots[index];
685
+ if (!found) {
686
+ if (slot->state == WEAK_MAP_TOMBSTONE) map->tombstones--;
687
+ slot->key = key;
688
+ slot->hash = hash;
689
+ slot->state = WEAK_MAP_OCCUPIED;
690
+ map->size++;
691
+ map->iteration_epoch++;
692
+ }
693
+ slot->value = value;
694
+ RB_OBJ_WRITTEN(self, Qundef, key);
695
+ RB_OBJ_WRITTEN(self, Qundef, value);
696
+ weak_map_notify_waiters_locked(map);
697
+ return value;
698
+ }
699
+
700
+ static VALUE
701
+ weak_map_normalize_key(weak_map_t *map, VALUE key)
702
+ {
703
+ if (!map->compare_keys_by_identity && RB_TYPE_P(key, T_STRING) && !RB_OBJ_FROZEN(key)) {
704
+ key = containers_normalize_string_key(key);
705
+ }
706
+ return key;
707
+ }
708
+
709
+ static VALUE
710
+ weak_map_prepare_key(weak_map_t *map, VALUE key)
711
+ {
712
+ key = weak_map_normalize_key(map, key);
713
+ containers_check_shareable(key);
714
+ return key;
715
+ }
716
+
717
+ typedef struct {
718
+ VALUE self;
719
+ weak_map_t *map;
720
+ } weak_map_init_context_t;
721
+
722
+ static int
723
+ weak_map_initialize_entry(VALUE key, VALUE value, VALUE opaque)
724
+ {
725
+ weak_map_init_context_t *context = (weak_map_init_context_t *)opaque;
726
+ key = weak_map_prepare_key(context->map, key);
727
+ containers_check_shareable(value);
728
+ st_index_t hash = weak_map_key_hash(context->map, key);
729
+ weak_map_execution_context_t execution;
730
+ weak_map_lock_state(context->map, &execution);
731
+ weak_map_store_locked(context->self, context->map, key, value, hash, &execution);
732
+ pthread_mutex_unlock(&context->map->lock);
733
+ return ST_CONTINUE;
734
+ }
735
+
736
+ static VALUE
737
+ weak_map_initialize(int argc, VALUE *argv, VALUE self)
738
+ {
739
+ VALUE mapping = Qnil;
740
+ VALUE keywords = Qnil;
741
+ ID keyword_ids[] = {
742
+ rb_intern("compare_by_identity"),
743
+ rb_intern("compare_keys_by_identity"),
744
+ rb_intern("compare_values_by_identity")
745
+ };
746
+ VALUE keyword_values[3] = {Qundef, Qundef, Qundef};
747
+ weak_map_t *map;
748
+
749
+ rb_scan_args(argc, argv, "01:", &mapping, &keywords);
750
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 3, keyword_values);
751
+ TypedData_Get_Struct(self, weak_map_t, &weak_map_type, map);
752
+ if (map->initialized) rb_raise(rb_eRuntimeError, "weak map is already initialized");
753
+
754
+ bool common = keyword_values[0] == Qundef
755
+ ? false
756
+ : containers_strict_bool(keyword_values[0], "compare_by_identity");
757
+ map->compare_keys_by_identity = keyword_values[1] == Qundef
758
+ ? common
759
+ : containers_strict_bool(keyword_values[1], "compare_keys_by_identity");
760
+ map->compare_values_by_identity = keyword_values[2] == Qundef
761
+ ? common
762
+ : containers_strict_bool(keyword_values[2], "compare_values_by_identity");
763
+ map->capacity = WEAK_MAP_INITIAL_CAPACITY;
764
+ map->slots = weak_map_allocate_slots(map->capacity);
765
+
766
+ rb_gc_declare_weak_references(self);
767
+ if (!NIL_P(mapping)) {
768
+ Check_Type(mapping, T_HASH);
769
+ weak_map_init_context_t context = {.self = self, .map = map};
770
+ rb_hash_foreach(mapping, weak_map_initialize_entry, (VALUE)&context);
771
+ }
772
+ map->initialized = true;
773
+ containers_publish_native_with_references(self, weak_map_validate_references);
774
+ return self;
775
+ }
776
+
777
+ static VALUE
778
+ weak_map_get(VALUE self, VALUE key)
779
+ {
780
+ weak_map_t *map = get_weak_map(self);
781
+ key = weak_map_prepare_key(map, key);
782
+ st_index_t hash = weak_map_key_hash(map, key);
783
+ bool found;
784
+ VALUE result = Qnil;
785
+ weak_map_execution_context_t execution;
786
+ weak_map_lock_state(map, &execution);
787
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
788
+ if (found) result = map->slots[index].value;
789
+ pthread_mutex_unlock(&map->lock);
790
+ return result;
791
+ }
792
+
793
+ static VALUE
794
+ weak_map_check_mutation(VALUE self)
795
+ {
796
+ rb_check_frozen(self);
797
+ return self;
798
+ }
799
+
800
+ static VALUE
801
+ weak_map_prepare_mutation_key(VALUE self, VALUE key)
802
+ {
803
+ rb_check_frozen(self);
804
+ key = weak_map_normalize_key(get_weak_map(self), key);
805
+ if (!rb_ractor_shareable_p(key)) {
806
+ rb_raise(eIsolationError, "key must be Ractor-shareable");
807
+ }
808
+ return key;
809
+ }
810
+
811
+ static VALUE
812
+ weak_map_fetch(int argc, VALUE *argv, VALUE self)
813
+ {
814
+ VALUE key;
815
+ VALUE default_value;
816
+ rb_scan_args(argc, argv, "11", &key, &default_value);
817
+
818
+ bool default_given = argc == 2;
819
+ bool block_given = rb_block_given_p();
820
+ if (block_given && default_given) {
821
+ rb_warn("block supersedes default value argument");
822
+ }
823
+
824
+ VALUE original_key = key;
825
+ weak_map_t *map = get_weak_map(self);
826
+ key = weak_map_prepare_key(map, key);
827
+ st_index_t hash = weak_map_key_hash(map, key);
828
+ bool found;
829
+ VALUE result = Qnil;
830
+ weak_map_execution_context_t execution;
831
+ weak_map_lock_state(map, &execution);
832
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
833
+ if (found) result = map->slots[index].value;
834
+ pthread_mutex_unlock(&map->lock);
835
+
836
+ if (found) return result;
837
+ if (block_given) return rb_yield(original_key);
838
+ if (default_given) return default_value;
839
+ containers_raise_key_error(self, original_key);
840
+ return Qnil;
841
+ }
842
+
843
+ static VALUE
844
+ weak_map_set(VALUE self, VALUE key, VALUE value)
845
+ {
846
+ rb_check_frozen(self);
847
+ weak_map_t *map = get_weak_map(self);
848
+ key = weak_map_prepare_key(map, key);
849
+ containers_check_shareable(value);
850
+ st_index_t hash = weak_map_key_hash(map, key);
851
+ rb_check_frozen(self);
852
+ weak_map_execution_context_t execution;
853
+ weak_map_lock_for_key(map, key, hash, &execution);
854
+ weak_map_store_locked(self, map, key, value, hash, &execution);
855
+ pthread_mutex_unlock(&map->lock);
856
+ return value;
857
+ }
858
+
859
+ static VALUE
860
+ weak_map_key_p(VALUE self, VALUE key)
861
+ {
862
+ weak_map_t *map = get_weak_map(self);
863
+ key = weak_map_prepare_key(map, key);
864
+ st_index_t hash = weak_map_key_hash(map, key);
865
+ bool found;
866
+ weak_map_execution_context_t execution;
867
+ weak_map_lock_state(map, &execution);
868
+ (void)weak_map_find_slot(map, key, hash, &found, &execution);
869
+ pthread_mutex_unlock(&map->lock);
870
+ return found ? Qtrue : Qfalse;
871
+ }
872
+
873
+ static VALUE
874
+ weak_map_getkey(VALUE self, VALUE key)
875
+ {
876
+ weak_map_t *map = get_weak_map(self);
877
+ key = weak_map_prepare_key(map, key);
878
+ st_index_t hash = weak_map_key_hash(map, key);
879
+ bool found;
880
+ VALUE result = Qnil;
881
+ weak_map_execution_context_t execution;
882
+ weak_map_lock_state(map, &execution);
883
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
884
+ if (found) result = map->compare_keys_by_identity ? key : map->slots[index].key;
885
+ pthread_mutex_unlock(&map->lock);
886
+ return result;
887
+ }
888
+
889
+ static VALUE
890
+ weak_map_delete(VALUE self, VALUE key)
891
+ {
892
+ rb_check_frozen(self);
893
+ weak_map_t *map = get_weak_map(self);
894
+ key = weak_map_prepare_key(map, key);
895
+ st_index_t hash = weak_map_key_hash(map, key);
896
+ rb_check_frozen(self);
897
+ bool found;
898
+ VALUE result = Qnil;
899
+ weak_map_execution_context_t execution;
900
+ weak_map_lock_for_key(map, key, hash, &execution);
901
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
902
+ if (RB_OBJ_FROZEN(self)) {
903
+ pthread_mutex_unlock(&map->lock);
904
+ rb_check_frozen(self);
905
+ }
906
+ if (found) {
907
+ weak_map_slot_t *slot = &map->slots[index];
908
+ result = slot->value;
909
+ slot->key = Qnil;
910
+ slot->value = Qnil;
911
+ slot->state = WEAK_MAP_TOMBSTONE;
912
+ map->size--;
913
+ map->tombstones++;
914
+ weak_map_notify_waiters_locked(map);
915
+ }
916
+ pthread_mutex_unlock(&map->lock);
917
+ return result;
918
+ }
919
+
920
+ static VALUE
921
+ weak_map_clear(VALUE self)
922
+ {
923
+ rb_check_frozen(self);
924
+ weak_map_t *map = get_weak_map(self);
925
+ weak_map_execution_context_t execution;
926
+ weak_map_lock_state(map, &execution);
927
+ for (weak_map_reservation_t *entry = map->reservations; entry; entry = entry->next) {
928
+ entry->invalidated = true;
929
+ }
930
+ for (size_t index = 0; index < map->capacity; index++) {
931
+ weak_map_slot_t *slot = &map->slots[index];
932
+ slot->key = Qnil;
933
+ slot->value = Qnil;
934
+ slot->hash = 0;
935
+ slot->state = WEAK_MAP_EMPTY;
936
+ }
937
+ map->iteration_epoch++;
938
+ map->size = 0;
939
+ map->tombstones = 0;
940
+ weak_map_notify_waiters_locked(map);
941
+ pthread_mutex_unlock(&map->lock);
942
+ return self;
943
+ }
944
+
945
+ typedef struct {
946
+ VALUE self;
947
+ weak_map_t *map;
948
+ VALUE key;
949
+ VALUE current;
950
+ VALUE argument;
951
+ VALUE replacement;
952
+ st_index_t hash;
953
+ bool identity;
954
+ bool complete;
955
+ bool present;
956
+ weak_map_reservation_t reservation;
957
+ } weak_map_operation_t;
958
+
959
+ static VALUE
960
+ weak_map_operation_cleanup(VALUE opaque)
961
+ {
962
+ weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
963
+ if (!operation->complete) {
964
+ weak_map_execution_context_t execution;
965
+ weak_map_lock_state(operation->map, &execution);
966
+ weak_map_finish_reservation_locked(operation->map, &operation->reservation);
967
+ weak_map_notify_waiters_locked(operation->map);
968
+ pthread_mutex_unlock(&operation->map->lock);
969
+ }
970
+ return Qnil;
971
+ }
972
+
973
+ static VALUE
974
+ weak_map_store_body(VALUE opaque)
975
+ {
976
+ weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
977
+ VALUE result = rb_yield_values(0);
978
+ containers_check_shareable(result);
979
+ rb_check_frozen(operation->self);
980
+ weak_map_execution_context_t execution;
981
+ weak_map_lock_state(operation->map, &execution);
982
+ bool stored = !operation->reservation.invalidated;
983
+ if (stored) {
984
+ weak_map_store_locked(
985
+ operation->self,
986
+ operation->map,
987
+ operation->key,
988
+ result,
989
+ operation->hash,
990
+ &execution
991
+ );
992
+ }
993
+ weak_map_finish_reservation_locked(operation->map, &operation->reservation);
994
+ pthread_mutex_unlock(&operation->map->lock);
995
+ operation->complete = true;
996
+ return stored ? result : Qnil;
997
+ }
998
+
999
+ static VALUE
1000
+ weak_map_store_if_absent(int argc, VALUE *argv, VALUE self)
1001
+ {
1002
+ rb_check_frozen(self);
1003
+ VALUE key;
1004
+ VALUE keywords = Qnil;
1005
+ VALUE keyword_values[1] = {Qundef};
1006
+ ID keyword_ids[] = {rb_intern("timeout")};
1007
+ rb_scan_args(argc, argv, "1:", &key, &keywords);
1008
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1009
+
1010
+ weak_map_t *map = get_weak_map(self);
1011
+ key = weak_map_prepare_key(map, key);
1012
+ weak_map_operation_t operation = {
1013
+ .self = self,
1014
+ .map = map,
1015
+ .key = key,
1016
+ .complete = false,
1017
+ };
1018
+ rb_need_block();
1019
+ operation.hash = weak_map_key_hash(map, key);
1020
+ rb_check_frozen(self);
1021
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1022
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1023
+ );
1024
+
1025
+ weak_map_execution_context_t execution;
1026
+ if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
1027
+ bool found;
1028
+ size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
1029
+ if (RB_OBJ_FROZEN(self)) {
1030
+ pthread_mutex_unlock(&map->lock);
1031
+ rb_check_frozen(self);
1032
+ }
1033
+ if (found) {
1034
+ VALUE value = map->slots[index].value;
1035
+ pthread_mutex_unlock(&map->lock);
1036
+ return value;
1037
+ }
1038
+ operation.key = found ? map->slots[index].key : key;
1039
+ weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
1040
+ operation.current, operation.hash, &execution);
1041
+ pthread_mutex_unlock(&map->lock);
1042
+ return rb_ensure(weak_map_store_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
1043
+ }
1044
+
1045
+ static VALUE
1046
+ weak_map_cas_body(VALUE opaque)
1047
+ {
1048
+ weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
1049
+ bool matches = operation->identity
1050
+ ? operation->current == operation->argument
1051
+ : RTEST(rb_equal(operation->current, operation->argument));
1052
+ rb_check_frozen(operation->self);
1053
+
1054
+ weak_map_execution_context_t execution;
1055
+ weak_map_lock_state(operation->map, &execution);
1056
+ matches = matches && !operation->reservation.invalidated;
1057
+ if (matches) {
1058
+ weak_map_store_locked(
1059
+ operation->self,
1060
+ operation->map,
1061
+ operation->key,
1062
+ operation->replacement,
1063
+ operation->hash,
1064
+ &execution
1065
+ );
1066
+ }
1067
+ weak_map_finish_reservation_locked(operation->map, &operation->reservation);
1068
+ if (!matches) weak_map_notify_waiters_locked(operation->map);
1069
+ pthread_mutex_unlock(&operation->map->lock);
1070
+ operation->complete = true;
1071
+ return matches ? Qtrue : Qfalse;
1072
+ }
1073
+
1074
+ static VALUE
1075
+ weak_map_compare_and_set(int argc, VALUE *argv, VALUE self)
1076
+ {
1077
+ rb_check_frozen(self);
1078
+ VALUE key;
1079
+ VALUE expected;
1080
+ VALUE replacement;
1081
+ VALUE keywords = Qnil;
1082
+ VALUE keyword_values[1] = {Qundef};
1083
+ ID keyword_ids[] = {rb_intern("timeout")};
1084
+ rb_scan_args(argc, argv, "3:", &key, &expected, &replacement, &keywords);
1085
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1086
+
1087
+ weak_map_t *map = get_weak_map(self);
1088
+ key = weak_map_prepare_key(map, key);
1089
+ weak_map_operation_t operation = {
1090
+ .self = self,
1091
+ .map = map,
1092
+ .key = key,
1093
+ .argument = expected,
1094
+ .replacement = replacement,
1095
+ .identity = map->compare_values_by_identity,
1096
+ .complete = false,
1097
+ };
1098
+ containers_check_shareable(expected);
1099
+ containers_check_shareable(replacement);
1100
+ operation.hash = weak_map_key_hash(map, key);
1101
+ rb_check_frozen(self);
1102
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1103
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1104
+ );
1105
+
1106
+ weak_map_execution_context_t execution;
1107
+ if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qfalse;
1108
+ bool found;
1109
+ size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
1110
+ if (RB_OBJ_FROZEN(self)) {
1111
+ pthread_mutex_unlock(&map->lock);
1112
+ rb_check_frozen(self);
1113
+ }
1114
+ if (!found) {
1115
+ pthread_mutex_unlock(&map->lock);
1116
+ return Qfalse;
1117
+ }
1118
+ operation.current = map->slots[index].value;
1119
+ operation.key = found ? map->slots[index].key : key;
1120
+ weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
1121
+ operation.current, operation.hash, &execution);
1122
+ pthread_mutex_unlock(&map->lock);
1123
+ return rb_ensure(weak_map_cas_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
1124
+ }
1125
+
1126
+ /* Decisions are interpreted before value validation. KEEP never writes storage. */
1127
+ static VALUE
1128
+ weak_map_modify_body(VALUE opaque)
1129
+ {
1130
+ weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
1131
+ VALUE value = rb_yield_values(2, operation->present ? Qtrue : Qfalse, operation->current);
1132
+ bool remove = value == weak_map_delete_token;
1133
+ bool store = !remove && value != weak_map_keep;
1134
+ if (store) containers_check_shareable(value);
1135
+ rb_check_frozen(operation->self);
1136
+ weak_map_execution_context_t execution;
1137
+ weak_map_lock_state(operation->map, &execution);
1138
+ bool changed = false;
1139
+ if (store && !operation->reservation.invalidated) {
1140
+ weak_map_store_locked(operation->self, operation->map, operation->key,
1141
+ value, operation->hash, &execution);
1142
+ changed = true;
1143
+ } else if (remove && !operation->reservation.invalidated) {
1144
+ bool found;
1145
+ size_t index = weak_map_find_slot(operation->map, operation->key,
1146
+ operation->hash, &found, &execution);
1147
+ if (RB_OBJ_FROZEN(operation->self)) {
1148
+ pthread_mutex_unlock(&operation->map->lock);
1149
+ rb_check_frozen(operation->self);
1150
+ }
1151
+ if (found) {
1152
+ weak_map_slot_t *slot = &operation->map->slots[index];
1153
+ slot->key = Qnil;
1154
+ slot->value = Qnil;
1155
+ slot->state = WEAK_MAP_TOMBSTONE;
1156
+ operation->map->size--;
1157
+ operation->map->tombstones++;
1158
+ weak_map_notify_waiters_locked(operation->map);
1159
+ changed = true;
1160
+ }
1161
+ }
1162
+ operation->complete = true;
1163
+ weak_map_finish_reservation_locked(operation->map, &operation->reservation);
1164
+ pthread_mutex_unlock(&operation->map->lock);
1165
+ return changed ? Qtrue : Qfalse;
1166
+ }
1167
+
1168
+ static VALUE
1169
+ weak_map_modify(VALUE self, VALUE key)
1170
+ {
1171
+ rb_check_frozen(self);
1172
+ rb_need_block();
1173
+ weak_map_t *map = get_weak_map(self);
1174
+ key = weak_map_prepare_key(map, key);
1175
+ weak_map_operation_t operation = {
1176
+ .self = self,
1177
+ .map = map,
1178
+ .key = key,
1179
+ .complete = false,
1180
+ };
1181
+ operation.hash = weak_map_key_hash(map, key);
1182
+ rb_check_frozen(self);
1183
+ weak_map_execution_context_t execution;
1184
+ weak_map_lock_for_key(map, key, operation.hash, &execution);
1185
+ bool found;
1186
+ size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
1187
+ if (RB_OBJ_FROZEN(self)) {
1188
+ pthread_mutex_unlock(&map->lock);
1189
+ rb_check_frozen(self);
1190
+ }
1191
+ operation.present = found;
1192
+ operation.current = found ? map->slots[index].value : Qnil;
1193
+ operation.key = found ? map->slots[index].key : key;
1194
+ weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
1195
+ operation.current, operation.hash, &execution);
1196
+ pthread_mutex_unlock(&map->lock);
1197
+ return rb_ensure(weak_map_modify_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
1198
+ }
1199
+
1200
+ static VALUE
1201
+ weak_map_upsert_body(VALUE opaque)
1202
+ {
1203
+ weak_map_operation_t *operation = (weak_map_operation_t *)opaque;
1204
+ VALUE result = rb_yield(operation->current);
1205
+ containers_check_shareable(result);
1206
+ rb_check_frozen(operation->self);
1207
+ weak_map_execution_context_t execution;
1208
+ weak_map_lock_state(operation->map, &execution);
1209
+ bool stored = !operation->reservation.invalidated;
1210
+ if (stored) {
1211
+ weak_map_store_locked(
1212
+ operation->self,
1213
+ operation->map,
1214
+ operation->key,
1215
+ result,
1216
+ operation->hash,
1217
+ &execution
1218
+ );
1219
+ }
1220
+ weak_map_finish_reservation_locked(operation->map, &operation->reservation);
1221
+ pthread_mutex_unlock(&operation->map->lock);
1222
+ operation->complete = true;
1223
+ return stored ? result : Qnil;
1224
+ }
1225
+
1226
+ static VALUE
1227
+ weak_map_upsert(int argc, VALUE *argv, VALUE self)
1228
+ {
1229
+ rb_check_frozen(self);
1230
+ VALUE key;
1231
+ VALUE initial;
1232
+ VALUE keywords = Qnil;
1233
+ VALUE keyword_values[1] = {Qundef};
1234
+ ID keyword_ids[] = {rb_intern("timeout")};
1235
+ rb_scan_args(argc, argv, "2:", &key, &initial, &keywords);
1236
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1237
+
1238
+ weak_map_t *map = get_weak_map(self);
1239
+ key = weak_map_prepare_key(map, key);
1240
+ weak_map_operation_t operation = {
1241
+ .self = self,
1242
+ .map = map,
1243
+ .key = key,
1244
+ .complete = false,
1245
+ };
1246
+ containers_check_shareable(initial);
1247
+ rb_need_block();
1248
+ operation.hash = weak_map_key_hash(map, key);
1249
+ rb_check_frozen(self);
1250
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1251
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1252
+ );
1253
+
1254
+ weak_map_execution_context_t execution;
1255
+ if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
1256
+ bool found;
1257
+ size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
1258
+ if (RB_OBJ_FROZEN(self)) {
1259
+ pthread_mutex_unlock(&map->lock);
1260
+ rb_check_frozen(self);
1261
+ }
1262
+ if (!found) {
1263
+ weak_map_store_locked(self, map, key, initial, operation.hash, &execution);
1264
+ pthread_mutex_unlock(&map->lock);
1265
+ return initial;
1266
+ }
1267
+ operation.current = map->slots[index].value;
1268
+ operation.key = found ? map->slots[index].key : key;
1269
+ weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
1270
+ operation.current, operation.hash, &execution);
1271
+ pthread_mutex_unlock(&map->lock);
1272
+ return rb_ensure(weak_map_upsert_body, (VALUE)&operation, weak_map_operation_cleanup, (VALUE)&operation);
1273
+ }
1274
+
1275
+ static VALUE
1276
+ weak_map_compare_keys_by_identity_p(VALUE self)
1277
+ {
1278
+ return get_weak_map(self)->compare_keys_by_identity ? Qtrue : Qfalse;
1279
+ }
1280
+
1281
+ static VALUE
1282
+ weak_map_compare_values_by_identity_p(VALUE self)
1283
+ {
1284
+ return get_weak_map(self)->compare_values_by_identity ? Qtrue : Qfalse;
1285
+ }
1286
+
1287
+ static VALUE
1288
+ weak_map_weak_keys_p(VALUE self)
1289
+ {
1290
+ return get_weak_map(self)->weak_keys ? Qtrue : Qfalse;
1291
+ }
1292
+
1293
+ static VALUE
1294
+ weak_map_weak_values_p(VALUE self)
1295
+ {
1296
+ return get_weak_map(self)->weak_values ? Qtrue : Qfalse;
1297
+ }
1298
+
1299
+ static VALUE
1300
+ weak_map_size(VALUE self)
1301
+ {
1302
+ weak_map_t *map = get_weak_map(self);
1303
+ size_t size;
1304
+ weak_map_execution_context_t execution;
1305
+ weak_map_lock_state(map, &execution);
1306
+ size = map->size;
1307
+ pthread_mutex_unlock(&map->lock);
1308
+ return SIZET2NUM(size);
1309
+ }
1310
+
1311
+ static VALUE
1312
+ weak_map_enumerator_size(VALUE self, VALUE arguments, VALUE enumerator)
1313
+ {
1314
+ (void)arguments;
1315
+ (void)enumerator;
1316
+ return weak_map_size(self);
1317
+ }
1318
+
1319
+ static VALUE
1320
+ weak_map_entries_snapshot(weak_map_t *map)
1321
+ {
1322
+ for (;;) {
1323
+ size_t capacity;
1324
+ weak_map_execution_context_t execution;
1325
+ weak_map_lock_state(map, &execution);
1326
+ capacity = map->size;
1327
+ pthread_mutex_unlock(&map->lock);
1328
+
1329
+ VALUE entries = rb_ary_new_capa((long)(capacity * 2));
1330
+ rb_ary_resize(entries, (long)(capacity * 2));
1331
+ weak_map_lock_state(map, &execution);
1332
+ if (map->size > capacity) {
1333
+ pthread_mutex_unlock(&map->lock);
1334
+ continue;
1335
+ }
1336
+ VALUE *entry_slots = RARRAY_PTR(entries);
1337
+ long entry_count = 0;
1338
+ for (size_t index = 0; index < map->capacity; index++) {
1339
+ weak_map_slot_t *slot = &map->slots[index];
1340
+ if (slot->state != WEAK_MAP_OCCUPIED) continue;
1341
+ RB_OBJ_WRITE(entries, &entry_slots[entry_count++], slot->key);
1342
+ RB_OBJ_WRITE(entries, &entry_slots[entry_count++], slot->value);
1343
+ }
1344
+ pthread_mutex_unlock(&map->lock);
1345
+ rb_ary_resize(entries, entry_count);
1346
+ return entries;
1347
+ }
1348
+ }
1349
+
1350
+ static VALUE
1351
+ weak_map_keys(VALUE self)
1352
+ {
1353
+ VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
1354
+ long entry_count = RARRAY_LEN(entries);
1355
+ VALUE keys = rb_ary_new_capa(entry_count / 2);
1356
+ for (long index = 0; index < entry_count; index += 2) {
1357
+ rb_ary_push(keys, RARRAY_AREF(entries, index));
1358
+ }
1359
+ RB_GC_GUARD(entries);
1360
+ return keys;
1361
+ }
1362
+
1363
+ static VALUE
1364
+ weak_map_each(VALUE self)
1365
+ {
1366
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
1367
+ VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
1368
+ long entry_count = RARRAY_LEN(entries);
1369
+ for (long index = 0; index < entry_count; index += 2) {
1370
+ rb_yield(rb_assoc_new(RARRAY_AREF(entries, index), RARRAY_AREF(entries, index + 1)));
1371
+ }
1372
+ RB_GC_GUARD(entries);
1373
+ return self;
1374
+ }
1375
+
1376
+ /* A bounded live traversal. No native lock is held across a Ruby yield.
1377
+ * Insertions, rehashing and clearing invalidate the cursor. Deletions and
1378
+ * value replacement are permitted while the table layout stays unchanged. */
1379
+ static VALUE
1380
+ weak_map_each_live(VALUE self)
1381
+ {
1382
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
1383
+ weak_map_t *map = get_weak_map(self);
1384
+ weak_map_execution_context_t execution;
1385
+ size_t cursor = 0;
1386
+ uint64_t epoch = 0;
1387
+ bool started = false;
1388
+ const long batch_slots = 64;
1389
+ VALUE batch = rb_ary_new_capa(batch_slots);
1390
+ rb_ary_resize(batch, batch_slots);
1391
+
1392
+ for (;;) {
1393
+ VALUE *storage = RARRAY_PTR(batch);
1394
+ for (long index = 0; index < batch_slots; index++) {
1395
+ RB_OBJ_WRITE(batch, &storage[index], Qnil);
1396
+ }
1397
+ weak_map_lock_state(map, &execution);
1398
+ if (started && epoch != map->iteration_epoch) {
1399
+ pthread_mutex_unlock(&map->lock);
1400
+ rb_raise(rb_eRuntimeError, "map structurally changed during live iteration");
1401
+ }
1402
+ epoch = map->iteration_epoch;
1403
+ started = true;
1404
+ long length = 0;
1405
+ while (cursor < map->capacity && length < batch_slots) {
1406
+ weak_map_slot_t *slot = &map->slots[cursor++];
1407
+ if (slot->state != WEAK_MAP_OCCUPIED) continue;
1408
+ RB_OBJ_WRITE(batch, &storage[length++], slot->key);
1409
+ RB_OBJ_WRITE(batch, &storage[length++], slot->value);
1410
+ }
1411
+ pthread_mutex_unlock(&map->lock);
1412
+ if (length == 0) break;
1413
+ for (long index = 0; index < length; index += 2) {
1414
+ rb_yield(rb_assoc_new(RARRAY_AREF(batch, index), RARRAY_AREF(batch, index + 1)));
1415
+ }
1416
+ }
1417
+ RB_GC_GUARD(batch);
1418
+ return self;
1419
+ }
1420
+
1421
+ static VALUE
1422
+ weak_map_each_key(VALUE self)
1423
+ {
1424
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
1425
+ VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
1426
+ long entry_count = RARRAY_LEN(entries);
1427
+ for (long index = 0; index < entry_count; index += 2) {
1428
+ rb_yield(RARRAY_AREF(entries, index));
1429
+ }
1430
+ RB_GC_GUARD(entries);
1431
+ return self;
1432
+ }
1433
+
1434
+ static VALUE
1435
+ weak_map_each_value(VALUE self)
1436
+ {
1437
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, weak_map_enumerator_size);
1438
+ VALUE entries = weak_map_entries_snapshot(get_weak_map(self));
1439
+ long entry_count = RARRAY_LEN(entries);
1440
+ for (long index = 1; index < entry_count; index += 2) {
1441
+ rb_yield(RARRAY_AREF(entries, index));
1442
+ }
1443
+ RB_GC_GUARD(entries);
1444
+ return self;
1445
+ }
1446
+
1447
+ static VALUE
1448
+ weak_map_get_with_timeout(int argc, VALUE *argv, VALUE self)
1449
+ {
1450
+ VALUE key;
1451
+ VALUE keywords = Qnil;
1452
+ VALUE keyword_values[1] = {Qundef};
1453
+ ID keyword_ids[] = {rb_intern("timeout")};
1454
+ rb_scan_args(argc, argv, "1:", &key, &keywords);
1455
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1456
+
1457
+ weak_map_t *map = get_weak_map(self);
1458
+ key = weak_map_prepare_key(map, key);
1459
+ st_index_t hash = weak_map_key_hash(map, key);
1460
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1461
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1462
+ );
1463
+ weak_map_execution_context_t execution;
1464
+ if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
1465
+ return weak_map_timeout_result();
1466
+ }
1467
+
1468
+ bool found;
1469
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
1470
+ VALUE result = found ? map->slots[index].value : Qnil;
1471
+ pthread_mutex_unlock(&map->lock);
1472
+ return result;
1473
+ }
1474
+
1475
+ static VALUE
1476
+ weak_map_store_with_timeout(int argc, VALUE *argv, VALUE self)
1477
+ {
1478
+ rb_check_frozen(self);
1479
+ VALUE key;
1480
+ VALUE value;
1481
+ VALUE keywords = Qnil;
1482
+ VALUE keyword_values[1] = {Qundef};
1483
+ ID keyword_ids[] = {rb_intern("timeout")};
1484
+ rb_scan_args(argc, argv, "2:", &key, &value, &keywords);
1485
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1486
+
1487
+ weak_map_t *map = get_weak_map(self);
1488
+ key = weak_map_prepare_key(map, key);
1489
+ containers_check_shareable(value);
1490
+ st_index_t hash = weak_map_key_hash(map, key);
1491
+ rb_check_frozen(self);
1492
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1493
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1494
+ );
1495
+ weak_map_execution_context_t execution;
1496
+ if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
1497
+ return weak_map_timeout_result();
1498
+ }
1499
+ weak_map_store_locked(self, map, key, value, hash, &execution);
1500
+ pthread_mutex_unlock(&map->lock);
1501
+ return value;
1502
+ }
1503
+
1504
+ static VALUE
1505
+ weak_map_swap(int argc, VALUE *argv, VALUE self)
1506
+ {
1507
+ rb_check_frozen(self);
1508
+ VALUE key;
1509
+ VALUE replacement;
1510
+ VALUE keywords = Qnil;
1511
+ VALUE keyword_values[1] = {Qundef};
1512
+ ID keyword_ids[] = {rb_intern("timeout")};
1513
+ rb_scan_args(argc, argv, "2:", &key, &replacement, &keywords);
1514
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1515
+
1516
+ weak_map_t *map = get_weak_map(self);
1517
+ key = weak_map_prepare_key(map, key);
1518
+ containers_check_shareable(replacement);
1519
+ st_index_t hash = weak_map_key_hash(map, key);
1520
+ rb_check_frozen(self);
1521
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1522
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1523
+ );
1524
+ weak_map_execution_context_t execution;
1525
+ if (!weak_map_lock_for_key_with_timeout(map, key, hash, &timeout, &execution)) {
1526
+ return weak_map_timeout_result();
1527
+ }
1528
+
1529
+ bool found;
1530
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
1531
+ if (RB_OBJ_FROZEN(self)) {
1532
+ pthread_mutex_unlock(&map->lock);
1533
+ rb_check_frozen(self);
1534
+ }
1535
+ VALUE previous = found ? map->slots[index].value : Qnil;
1536
+ weak_map_store_locked(self, map, key, replacement, hash, &execution);
1537
+ pthread_mutex_unlock(&map->lock);
1538
+ return previous;
1539
+ }
1540
+
1541
+ static bool
1542
+ weak_map_values_equal(weak_map_t *map, VALUE left, VALUE right)
1543
+ {
1544
+ return map->compare_values_by_identity ? left == right : RTEST(rb_equal(left, right));
1545
+ }
1546
+
1547
+ static VALUE
1548
+ weak_map_wait_for_value(int argc, VALUE *argv, VALUE self, bool non_nil)
1549
+ {
1550
+ VALUE key;
1551
+ VALUE expected = Qnil;
1552
+ VALUE keywords = Qnil;
1553
+ VALUE keyword_values[1] = {Qundef};
1554
+ ID keyword_ids[] = {rb_intern("timeout")};
1555
+ if (non_nil) rb_scan_args(argc, argv, "1:", &key, &keywords);
1556
+ else rb_scan_args(argc, argv, "2:", &key, &expected, &keywords);
1557
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1558
+
1559
+ weak_map_t *map = get_weak_map(self);
1560
+ key = weak_map_prepare_key(map, key);
1561
+ if (!non_nil) containers_check_shareable(expected);
1562
+ st_index_t hash = weak_map_key_hash(map, key);
1563
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1564
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1565
+ );
1566
+
1567
+ for (;;) {
1568
+ bool found;
1569
+ VALUE current = Qnil;
1570
+ uint64_t generation;
1571
+ weak_map_execution_context_t execution;
1572
+ weak_map_lock_state(map, &execution);
1573
+ weak_map_reservation_t *entry = weak_map_find_reservation_locked(map, key, hash, &execution);
1574
+ size_t index = weak_map_find_slot(map, key, hash, &found, &execution);
1575
+ if (found) current = map->slots[index].value;
1576
+ generation = map->generation;
1577
+ pthread_mutex_unlock(&map->lock);
1578
+
1579
+ bool ready = non_nil ? !NIL_P(current) : !weak_map_values_equal(map, current, expected);
1580
+ if (ready) return current;
1581
+
1582
+ weak_map_lock_state(map, &execution);
1583
+ if (map->generation != generation) {
1584
+ pthread_mutex_unlock(&map->lock);
1585
+ continue;
1586
+ }
1587
+ if (entry) weak_map_check_reservation_wait_locked(map, entry, &execution);
1588
+ pthread_mutex_unlock(&map->lock);
1589
+ if (!weak_map_wait_once(map, generation, &timeout)) return weak_map_timeout_result();
1590
+ }
1591
+ }
1592
+
1593
+ static VALUE
1594
+ weak_map_wait_until_changed(int argc, VALUE *argv, VALUE self)
1595
+ {
1596
+ return weak_map_wait_for_value(argc, argv, self, false);
1597
+ }
1598
+
1599
+ static VALUE
1600
+ weak_map_wait_until_non_nil(int argc, VALUE *argv, VALUE self)
1601
+ {
1602
+ return weak_map_wait_for_value(argc, argv, self, true);
1603
+ }
1604
+
1605
+ static VALUE
1606
+ weak_map_update(int argc, VALUE *argv, VALUE self)
1607
+ {
1608
+ rb_check_frozen(self);
1609
+ VALUE key;
1610
+ VALUE keywords = Qnil;
1611
+ VALUE keyword_values[1] = {Qundef};
1612
+ ID keyword_ids[] = {rb_intern("timeout")};
1613
+ rb_scan_args(argc, argv, "1:", &key, &keywords);
1614
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
1615
+
1616
+ weak_map_t *map = get_weak_map(self);
1617
+ key = weak_map_prepare_key(map, key);
1618
+ weak_map_operation_t operation = {
1619
+ .self = self,
1620
+ .map = map,
1621
+ .key = key,
1622
+ .complete = false,
1623
+ };
1624
+ rb_need_block();
1625
+ operation.hash = weak_map_key_hash(map, key);
1626
+ rb_check_frozen(self);
1627
+ weak_map_timeout_t timeout = weak_map_parse_timeout(
1628
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
1629
+ );
1630
+
1631
+ weak_map_execution_context_t execution;
1632
+ if (!weak_map_lock_for_key_with_timeout(map, key, operation.hash, &timeout, &execution)) return Qnil;
1633
+ bool found;
1634
+ size_t index = weak_map_find_slot(map, key, operation.hash, &found, &execution);
1635
+ if (RB_OBJ_FROZEN(self)) {
1636
+ pthread_mutex_unlock(&map->lock);
1637
+ rb_check_frozen(self);
1638
+ }
1639
+ operation.current = found ? map->slots[index].value : Qnil;
1640
+ operation.key = found ? map->slots[index].key : key;
1641
+ weak_map_begin_reservation_locked(map, &operation.reservation, operation.key,
1642
+ operation.current, operation.hash, &execution);
1643
+ pthread_mutex_unlock(&map->lock);
1644
+ return rb_ensure(
1645
+ weak_map_upsert_body,
1646
+ (VALUE)&operation,
1647
+ weak_map_operation_cleanup,
1648
+ (VALUE)&operation
1649
+ );
1650
+ }
1651
+
1652
+ static void
1653
+ define_weak_map_methods(VALUE klass)
1654
+ {
1655
+ rb_define_alloc_func(klass, weak_map_allocate);
1656
+ rb_define_method(klass, "initialize", weak_map_initialize, -1);
1657
+ rb_define_method(klass, "[]", weak_map_get, 1);
1658
+ rb_define_method(klass, "[]=", weak_map_set, 2);
1659
+ rb_define_method(klass, "check_mutation", weak_map_check_mutation, 0);
1660
+ rb_define_method(klass, "prepare_mutation_key", weak_map_prepare_mutation_key, 1);
1661
+ rb_define_method(klass, "fetch", weak_map_fetch, -1);
1662
+ rb_define_method(klass, "get", weak_map_get_with_timeout, -1);
1663
+ rb_define_method(klass, "store", weak_map_store_with_timeout, -1);
1664
+ rb_define_method(klass, "swap", weak_map_swap, -1);
1665
+ rb_define_method(klass, "update", weak_map_update, -1);
1666
+ rb_define_method(klass, "modify", weak_map_modify, 1);
1667
+ rb_define_method(klass, "wait_until_changed", weak_map_wait_until_changed, -1);
1668
+ rb_define_method(klass, "wait_until_non_nil", weak_map_wait_until_non_nil, -1);
1669
+ rb_define_method(klass, "store_if_absent", weak_map_store_if_absent, -1);
1670
+ rb_define_method(klass, "key?", weak_map_key_p, 1);
1671
+ rb_define_method(klass, "delete", weak_map_delete, 1);
1672
+ rb_define_method(klass, "clear", weak_map_clear, 0);
1673
+ rb_define_method(klass, "compare_and_set", weak_map_compare_and_set, -1);
1674
+ rb_define_method(klass, "upsert", weak_map_upsert, -1);
1675
+ rb_define_method(klass, "compare_keys_by_identity?", weak_map_compare_keys_by_identity_p, 0);
1676
+ rb_define_method(klass, "compare_values_by_identity?", weak_map_compare_values_by_identity_p, 0);
1677
+ rb_define_method(klass, "weak_keys?", weak_map_weak_keys_p, 0);
1678
+ rb_define_method(klass, "weak_values?", weak_map_weak_values_p, 0);
1679
+ rb_define_method(klass, "getkey", weak_map_getkey, 1);
1680
+ rb_define_method(klass, "size", weak_map_size, 0);
1681
+ rb_define_method(klass, "keys", weak_map_keys, 0);
1682
+ rb_define_method(klass, "each", weak_map_each, 0);
1683
+ rb_define_method(klass, "each_live", weak_map_each_live, 0);
1684
+ rb_define_method(klass, "each_pair", weak_map_each, 0);
1685
+ rb_define_method(klass, "each_key", weak_map_each_key, 0);
1686
+ rb_define_method(klass, "each_value", weak_map_each_value, 0);
1687
+ }
1688
+
1689
+ void
1690
+ containers_init_weak_maps(VALUE namespace)
1691
+ {
1692
+ eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
1693
+ weak_map_keep = rb_const_get(namespace, rb_intern("MAP_KEEP"));
1694
+ weak_map_delete_token = rb_const_get(namespace, rb_intern("MAP_DELETE"));
1695
+ rb_global_variable(&weak_map_keep);
1696
+ rb_global_variable(&weak_map_delete_token);
1697
+ cWeakKeyMap = rb_define_class_under(namespace, "WeakKeyMap", rb_cObject);
1698
+ cWeakValueMap = rb_define_class_under(namespace, "WeakValueMap", rb_cObject);
1699
+ cWeakMap = rb_define_class_under(namespace, "WeakMap", rb_cObject);
1700
+ define_weak_map_methods(cWeakKeyMap);
1701
+ define_weak_map_methods(cWeakValueMap);
1702
+ define_weak_map_methods(cWeakMap);
1703
+ rb_define_const(namespace, "NATIVE_WEAK_MAPS", Qtrue);
1704
+ }
1705
+
1706
+ #else
1707
+
1708
+ void
1709
+ containers_init_weak_maps(VALUE namespace)
1710
+ {
1711
+ (void)namespace;
1712
+ }
1713
+
1714
+ #endif