farce 0.0.1.alpha2-x86-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,1953 @@
1
+ #include "containers.h"
2
+ #include "transaction.h"
3
+ #include <ruby/atomic.h>
4
+ #include <ruby/fiber/scheduler.h>
5
+ #include <ruby/io.h>
6
+ #include <ruby/ractor.h>
7
+ #include <ruby/thread.h>
8
+
9
+ #include <errno.h>
10
+ #include <fcntl.h>
11
+ #include <math.h>
12
+ #include <pthread.h>
13
+ #include <stdbool.h>
14
+ #include <stddef.h>
15
+ #include <stdint.h>
16
+ #include <unistd.h>
17
+
18
+ /*
19
+ * A clean-room implementation of the conventional red-black-tree algorithms.
20
+ *
21
+ * This file intentionally shares no implementation with the Kazlib-backed
22
+ * priority queue. The generic link layer knows nothing about Ruby objects;
23
+ * each map node stores one key/value pair.
24
+ */
25
+
26
+ typedef enum {
27
+ TC_RED = 0,
28
+ TC_BLACK = 1
29
+ } tc_color;
30
+
31
+ enum {
32
+ TC_MAP_UNINITIALIZED = 0,
33
+ TC_MAP_PUBLISHING = 1,
34
+ TC_MAP_PUBLISHED = 2,
35
+ TC_MAP_INITIALIZED = 3,
36
+ };
37
+
38
+ static inline bool
39
+ tc_core_map_publication_busy(rb_atomic_t state)
40
+ {
41
+ return state == TC_MAP_PUBLISHING || state == TC_MAP_PUBLISHED;
42
+ }
43
+
44
+ typedef struct tc_link {
45
+ struct tc_link *left;
46
+ struct tc_link *right;
47
+ struct tc_link *parent;
48
+ tc_color color;
49
+ } tc_link;
50
+
51
+ static ID tc_id_compare;
52
+ static VALUE tc_eIsolationError;
53
+
54
+ static inline tc_color
55
+ tc_link_color(const tc_link *link)
56
+ {
57
+ return link == NULL ? TC_BLACK : link->color;
58
+ }
59
+
60
+ static inline void
61
+ tc_link_set_color(tc_link *link, tc_color color)
62
+ {
63
+ if (link != NULL) link->color = color;
64
+ }
65
+
66
+ static tc_link *
67
+ tc_link_minimum(tc_link *link)
68
+ {
69
+ if (link == NULL) return NULL;
70
+ while (link->left != NULL) link = link->left;
71
+ return link;
72
+ }
73
+
74
+ static tc_link *
75
+ tc_link_maximum(tc_link *link)
76
+ {
77
+ if (link == NULL) return NULL;
78
+ while (link->right != NULL) link = link->right;
79
+ return link;
80
+ }
81
+
82
+ static tc_link *
83
+ tc_link_successor(tc_link *link)
84
+ {
85
+ tc_link *parent;
86
+
87
+ if (link == NULL) return NULL;
88
+ if (link->right != NULL) return tc_link_minimum(link->right);
89
+
90
+ parent = link->parent;
91
+ while (parent != NULL && link == parent->right) {
92
+ link = parent;
93
+ parent = parent->parent;
94
+ }
95
+ return parent;
96
+ }
97
+
98
+ static void
99
+ tc_rotate_left(tc_link **root, tc_link *upper)
100
+ {
101
+ tc_link *lower = upper->right;
102
+
103
+ upper->right = lower->left;
104
+ if (lower->left != NULL) lower->left->parent = upper;
105
+
106
+ lower->parent = upper->parent;
107
+ if (upper->parent == NULL) {
108
+ *root = lower;
109
+ }
110
+ else if (upper == upper->parent->left) {
111
+ upper->parent->left = lower;
112
+ }
113
+ else {
114
+ upper->parent->right = lower;
115
+ }
116
+
117
+ lower->left = upper;
118
+ upper->parent = lower;
119
+ }
120
+
121
+ static void
122
+ tc_rotate_right(tc_link **root, tc_link *upper)
123
+ {
124
+ tc_link *lower = upper->left;
125
+
126
+ upper->left = lower->right;
127
+ if (lower->right != NULL) lower->right->parent = upper;
128
+
129
+ lower->parent = upper->parent;
130
+ if (upper->parent == NULL) {
131
+ *root = lower;
132
+ }
133
+ else if (upper == upper->parent->right) {
134
+ upper->parent->right = lower;
135
+ }
136
+ else {
137
+ upper->parent->left = lower;
138
+ }
139
+
140
+ lower->right = upper;
141
+ upper->parent = lower;
142
+ }
143
+
144
+ static void
145
+ tc_insert_fix(tc_link **root, tc_link *node)
146
+ {
147
+ while (node->parent != NULL && node->parent->color == TC_RED) {
148
+ tc_link *parent = node->parent;
149
+ tc_link *grandparent = parent->parent;
150
+
151
+ if (parent == grandparent->left) {
152
+ tc_link *uncle = grandparent->right;
153
+ if (tc_link_color(uncle) == TC_RED) {
154
+ parent->color = TC_BLACK;
155
+ uncle->color = TC_BLACK;
156
+ grandparent->color = TC_RED;
157
+ node = grandparent;
158
+ }
159
+ else {
160
+ if (node == parent->right) {
161
+ node = parent;
162
+ tc_rotate_left(root, node);
163
+ parent = node->parent;
164
+ grandparent = parent->parent;
165
+ }
166
+ parent->color = TC_BLACK;
167
+ grandparent->color = TC_RED;
168
+ tc_rotate_right(root, grandparent);
169
+ }
170
+ }
171
+ else {
172
+ tc_link *uncle = grandparent->left;
173
+ if (tc_link_color(uncle) == TC_RED) {
174
+ parent->color = TC_BLACK;
175
+ uncle->color = TC_BLACK;
176
+ grandparent->color = TC_RED;
177
+ node = grandparent;
178
+ }
179
+ else {
180
+ if (node == parent->left) {
181
+ node = parent;
182
+ tc_rotate_right(root, node);
183
+ parent = node->parent;
184
+ grandparent = parent->parent;
185
+ }
186
+ parent->color = TC_BLACK;
187
+ grandparent->color = TC_RED;
188
+ tc_rotate_left(root, grandparent);
189
+ }
190
+ }
191
+ }
192
+
193
+ (*root)->color = TC_BLACK;
194
+ }
195
+
196
+ static void
197
+ tc_insert_link(tc_link **root, tc_link *parent, tc_link *node, bool insert_left)
198
+ {
199
+ node->left = NULL;
200
+ node->right = NULL;
201
+ node->parent = parent;
202
+ node->color = TC_RED;
203
+
204
+ if (parent == NULL) {
205
+ *root = node;
206
+ }
207
+ else if (insert_left) {
208
+ parent->left = node;
209
+ }
210
+ else {
211
+ parent->right = node;
212
+ }
213
+
214
+ tc_insert_fix(root, node);
215
+ }
216
+
217
+ static void
218
+ tc_transplant(tc_link **root, tc_link *old_link, tc_link *new_link)
219
+ {
220
+ if (old_link->parent == NULL) {
221
+ *root = new_link;
222
+ }
223
+ else if (old_link == old_link->parent->left) {
224
+ old_link->parent->left = new_link;
225
+ }
226
+ else {
227
+ old_link->parent->right = new_link;
228
+ }
229
+
230
+ if (new_link != NULL) new_link->parent = old_link->parent;
231
+ }
232
+
233
+ /* `node` may be NULL. `parent` is its logical parent in that case. */
234
+ static void
235
+ tc_delete_fix(tc_link **root, tc_link *node, tc_link *parent)
236
+ {
237
+ while (node != *root && tc_link_color(node) == TC_BLACK) {
238
+ tc_link *sibling;
239
+
240
+ if (parent == NULL) break;
241
+
242
+ if (node == parent->left) {
243
+ sibling = parent->right;
244
+
245
+ if (tc_link_color(sibling) == TC_RED) {
246
+ sibling->color = TC_BLACK;
247
+ parent->color = TC_RED;
248
+ tc_rotate_left(root, parent);
249
+ sibling = parent->right;
250
+ }
251
+
252
+ if (sibling == NULL) {
253
+ node = parent;
254
+ parent = node->parent;
255
+ continue;
256
+ }
257
+
258
+ if (tc_link_color(sibling->left) == TC_BLACK &&
259
+ tc_link_color(sibling->right) == TC_BLACK) {
260
+ sibling->color = TC_RED;
261
+ node = parent;
262
+ parent = node->parent;
263
+ }
264
+ else {
265
+ if (tc_link_color(sibling->right) == TC_BLACK) {
266
+ tc_link_set_color(sibling->left, TC_BLACK);
267
+ sibling->color = TC_RED;
268
+ tc_rotate_right(root, sibling);
269
+ sibling = parent->right;
270
+ }
271
+
272
+ sibling->color = parent->color;
273
+ parent->color = TC_BLACK;
274
+ tc_link_set_color(sibling->right, TC_BLACK);
275
+ tc_rotate_left(root, parent);
276
+ node = *root;
277
+ parent = NULL;
278
+ }
279
+ }
280
+ else {
281
+ sibling = parent->left;
282
+
283
+ if (tc_link_color(sibling) == TC_RED) {
284
+ sibling->color = TC_BLACK;
285
+ parent->color = TC_RED;
286
+ tc_rotate_right(root, parent);
287
+ sibling = parent->left;
288
+ }
289
+
290
+ if (sibling == NULL) {
291
+ node = parent;
292
+ parent = node->parent;
293
+ continue;
294
+ }
295
+
296
+ if (tc_link_color(sibling->right) == TC_BLACK &&
297
+ tc_link_color(sibling->left) == TC_BLACK) {
298
+ sibling->color = TC_RED;
299
+ node = parent;
300
+ parent = node->parent;
301
+ }
302
+ else {
303
+ if (tc_link_color(sibling->left) == TC_BLACK) {
304
+ tc_link_set_color(sibling->right, TC_BLACK);
305
+ sibling->color = TC_RED;
306
+ tc_rotate_left(root, sibling);
307
+ sibling = parent->left;
308
+ }
309
+
310
+ sibling->color = parent->color;
311
+ parent->color = TC_BLACK;
312
+ tc_link_set_color(sibling->left, TC_BLACK);
313
+ tc_rotate_right(root, parent);
314
+ node = *root;
315
+ parent = NULL;
316
+ }
317
+ }
318
+ }
319
+
320
+ tc_link_set_color(node, TC_BLACK);
321
+ }
322
+
323
+ static void
324
+ tc_remove_link(tc_link **root, tc_link *removed)
325
+ {
326
+ tc_link *moved = removed;
327
+ tc_link *replacement;
328
+ tc_link *replacement_parent;
329
+ tc_color original_color = moved->color;
330
+
331
+ if (removed->left == NULL) {
332
+ replacement = removed->right;
333
+ replacement_parent = removed->parent;
334
+ tc_transplant(root, removed, removed->right);
335
+ }
336
+ else if (removed->right == NULL) {
337
+ replacement = removed->left;
338
+ replacement_parent = removed->parent;
339
+ tc_transplant(root, removed, removed->left);
340
+ }
341
+ else {
342
+ moved = tc_link_minimum(removed->right);
343
+ original_color = moved->color;
344
+ replacement = moved->right;
345
+
346
+ if (moved->parent == removed) {
347
+ replacement_parent = moved;
348
+ if (replacement != NULL) replacement->parent = moved;
349
+ }
350
+ else {
351
+ replacement_parent = moved->parent;
352
+ tc_transplant(root, moved, moved->right);
353
+ moved->right = removed->right;
354
+ moved->right->parent = moved;
355
+ }
356
+
357
+ tc_transplant(root, removed, moved);
358
+ moved->left = removed->left;
359
+ moved->left->parent = moved;
360
+ moved->color = removed->color;
361
+ }
362
+
363
+ if (original_color == TC_BLACK) {
364
+ tc_delete_fix(root, replacement, replacement_parent);
365
+ }
366
+ }
367
+
368
+ typedef struct {
369
+ unsigned int *guard;
370
+ VALUE left;
371
+ VALUE right;
372
+ } tc_call_args;
373
+
374
+ static VALUE
375
+ tc_compare_body(VALUE opaque)
376
+ {
377
+ tc_call_args *args = (tc_call_args *)opaque;
378
+ VALUE result = rb_funcall(args->left, tc_id_compare, 1, args->right);
379
+ return INT2NUM(rb_cmpint(result, args->left, args->right));
380
+ }
381
+
382
+ static VALUE
383
+ tc_call_ensure(VALUE opaque)
384
+ {
385
+ tc_call_args *args = (tc_call_args *)opaque;
386
+ (*args->guard)--;
387
+ return Qnil;
388
+ }
389
+
390
+ static int
391
+ tc_compare_keys(unsigned int *guard, VALUE left, VALUE right)
392
+ {
393
+ tc_call_args args = {guard, left, right};
394
+ VALUE result;
395
+
396
+ if (FIXNUM_P(left) && FIXNUM_P(right)) {
397
+ intptr_t left_value = (intptr_t)left;
398
+ intptr_t right_value = (intptr_t)right;
399
+ if (left_value < right_value) return -1;
400
+ if (left_value > right_value) return 1;
401
+ return 0;
402
+ }
403
+ if (CLASS_OF(left) == rb_cString && CLASS_OF(right) == rb_cString) {
404
+ return rb_str_cmp(left, right);
405
+ }
406
+ if (CLASS_OF(left) == rb_cFloat && CLASS_OF(right) == rb_cFloat) {
407
+ double left_value = RFLOAT_VALUE(left);
408
+ double right_value = RFLOAT_VALUE(right);
409
+
410
+ if (!isnan(left_value) && !isnan(right_value)) {
411
+ if (left_value < right_value) return -1;
412
+ if (left_value > right_value) return 1;
413
+ return 0;
414
+ }
415
+ }
416
+
417
+ (*guard)++;
418
+ result = rb_ensure(tc_compare_body, (VALUE)&args, tc_call_ensure, (VALUE)&args);
419
+ return NUM2INT(result);
420
+ }
421
+
422
+ typedef struct tc_map_node {
423
+ tc_link link;
424
+ VALUE key;
425
+ VALUE value;
426
+ } tc_map_node;
427
+
428
+ typedef struct {
429
+ tc_link *root;
430
+ size_t size;
431
+ unsigned int guard;
432
+ uint64_t revision;
433
+ } tc_map;
434
+
435
+ #define TC_MAP_NODE(link_pointer) ((tc_map_node *)(link_pointer))
436
+
437
+ static void
438
+ tc_map_store_node_value(VALUE self, tc_map_node *node, VALUE value)
439
+ {
440
+ RB_OBJ_WRITE(self, &node->value, value);
441
+ }
442
+
443
+ static void
444
+ tc_map_mark_node(tc_link *link)
445
+ {
446
+ tc_map_node *node;
447
+
448
+ if (link == NULL) return;
449
+ node = TC_MAP_NODE(link);
450
+ rb_gc_mark_movable(node->key);
451
+ rb_gc_mark_movable(node->value);
452
+ tc_map_mark_node(link->left);
453
+ tc_map_mark_node(link->right);
454
+ }
455
+
456
+ static void
457
+ tc_map_validate_shareable_node(tc_link *link)
458
+ {
459
+ if (link == NULL) return;
460
+ tc_map_node *node = TC_MAP_NODE(link);
461
+ containers_check_shareable(node->key);
462
+ containers_check_shareable(node->value);
463
+ tc_map_validate_shareable_node(link->left);
464
+ tc_map_validate_shareable_node(link->right);
465
+ }
466
+
467
+ static void
468
+ tc_map_compact_node(tc_link *link)
469
+ {
470
+ tc_map_node *node;
471
+
472
+ if (link == NULL) return;
473
+ node = TC_MAP_NODE(link);
474
+ node->key = rb_gc_location(node->key);
475
+ node->value = rb_gc_location(node->value);
476
+ tc_map_compact_node(link->left);
477
+ tc_map_compact_node(link->right);
478
+ }
479
+
480
+ static void
481
+ tc_map_free_node(tc_link *link)
482
+ {
483
+ tc_map_node *node;
484
+
485
+ if (link == NULL) return;
486
+ tc_map_free_node(link->left);
487
+ tc_map_free_node(link->right);
488
+ node = TC_MAP_NODE(link);
489
+ xfree(node);
490
+ }
491
+
492
+ static tc_map_node *
493
+ tc_map_find(tc_map *map, VALUE key, tc_map_node **parent_out, bool *left_out)
494
+ {
495
+ tc_link *link = map->root;
496
+ tc_map_node *parent = NULL;
497
+ bool insert_left = false;
498
+
499
+ while (link != NULL) {
500
+ tc_map_node *node = TC_MAP_NODE(link);
501
+ int comparison = tc_compare_keys(&map->guard, key, node->key);
502
+
503
+ if (comparison == 0) {
504
+ if (parent_out != NULL) *parent_out = parent;
505
+ if (left_out != NULL) *left_out = insert_left;
506
+ return node;
507
+ }
508
+
509
+ parent = node;
510
+ insert_left = comparison < 0;
511
+ link = insert_left ? link->left : link->right;
512
+ }
513
+
514
+ if (parent_out != NULL) *parent_out = parent;
515
+ if (left_out != NULL) *left_out = insert_left;
516
+ return NULL;
517
+ }
518
+
519
+ static VALUE
520
+ tc_map_store_unlocked(VALUE self, tc_map *map, VALUE key, VALUE value,
521
+ bool honor_freeze)
522
+ {
523
+ tc_map_node *parent;
524
+ tc_map_node *node;
525
+ bool insert_left;
526
+
527
+ node = tc_map_find(map, key, &parent, &insert_left);
528
+ /* A comparator is arbitrary Ruby code and may freeze the receiver. The
529
+ * pre-operation check is therefore insufficient: honor local-container
530
+ * freeze semantics again after the last callback and before committing. */
531
+ if (honor_freeze) rb_check_frozen(self);
532
+ if (node != NULL) {
533
+ tc_map_store_node_value(self, node, value);
534
+ map->revision++;
535
+ return value;
536
+ }
537
+
538
+ node = ALLOC(tc_map_node);
539
+ node->key = Qnil;
540
+ node->value = Qnil;
541
+ RB_OBJ_WRITE(self, &node->key, key);
542
+ RB_OBJ_WRITE(self, &node->value, value);
543
+ tc_insert_link(&map->root, parent == NULL ? NULL : &parent->link,
544
+ &node->link, insert_left);
545
+ map->size++;
546
+ map->revision++;
547
+ return value;
548
+ }
549
+ /* ------------------------------------------------------------------------- */
550
+ /* UnsafeTreeMap / TreeMap / ShareableTreeMap */
551
+
552
+ /* The three maps deliberately share the exact same node layout and red-black
553
+ * tree operations. The policy bits decide whether a public operation takes
554
+ * the mutex and whether values and the container must be Ractor-shareable.
555
+ * Shared containers require shareable keys and values. The mutable local
556
+ * adapter can retain ordinary Ruby keys because its comparator callbacks never
557
+ * cross a Ractor boundary. */
558
+ typedef struct tc_core_map_waiter tc_core_map_waiter;
559
+
560
+ struct tc_core_map_waiter {
561
+ int read_fd;
562
+ int write_fd;
563
+ bool notified;
564
+ tc_core_map_waiter *next;
565
+ };
566
+
567
+ typedef struct {
568
+ tc_map map; /* Must stay first: the generic map algorithm operates on it. */
569
+ pthread_mutex_t lock;
570
+ VALUE owner_fiber;
571
+ VALUE owner_ruby_thread;
572
+ tc_core_map_waiter *waiters;
573
+ tc_core_map_waiter *last_waiter;
574
+ /* Omitted from dmark so the publisher itself is not part of the
575
+ * recursively shareable graph. */
576
+ VALUE publication_owner_fiber;
577
+ VALUE publication_owner_ruby_thread;
578
+ bool mutex_initialized;
579
+ bool synchronized;
580
+ bool shareable_container;
581
+ bool shareable_keys;
582
+ bool transaction_reserved;
583
+ rb_atomic_t publication_state;
584
+ } tc_core_map;
585
+
586
+ static const rb_data_type_t tc_unsafe_map_type;
587
+ static const rb_data_type_t tc_map_type;
588
+ static const rb_data_type_t tc_shared_map_type;
589
+
590
+ static void tc_core_map_wait_for_publication(VALUE self, tc_core_map *core);
591
+
592
+ static void
593
+ tc_core_map_mark(void *opaque)
594
+ {
595
+ tc_core_map *core = opaque;
596
+ if (core == NULL) return;
597
+ tc_map_mark_node(core->map.root);
598
+ rb_gc_mark_movable(core->owner_fiber);
599
+ rb_gc_mark_movable(core->owner_ruby_thread);
600
+ }
601
+
602
+ static void
603
+ tc_core_map_compact(void *opaque)
604
+ {
605
+ tc_core_map *core = opaque;
606
+ if (core == NULL) return;
607
+ tc_map_compact_node(core->map.root);
608
+ core->owner_fiber = rb_gc_location(core->owner_fiber);
609
+ core->owner_ruby_thread = rb_gc_location(core->owner_ruby_thread);
610
+ core->publication_owner_fiber =
611
+ rb_gc_location(core->publication_owner_fiber);
612
+ core->publication_owner_ruby_thread =
613
+ rb_gc_location(core->publication_owner_ruby_thread);
614
+ }
615
+
616
+ static void
617
+ tc_core_map_free(void *opaque)
618
+ {
619
+ tc_core_map *core = opaque;
620
+
621
+ if (core == NULL) return;
622
+ tc_map_free_node(core->map.root);
623
+ if (core->mutex_initialized) pthread_mutex_destroy(&core->lock);
624
+ xfree(core);
625
+ }
626
+
627
+ static size_t
628
+ tc_core_map_memsize(const void *opaque)
629
+ {
630
+ const tc_core_map *core = opaque;
631
+
632
+ return core == NULL
633
+ ? 0
634
+ : sizeof(*core) + core->map.size * sizeof(tc_map_node);
635
+ }
636
+
637
+ static const rb_data_type_t tc_unsafe_map_type = {
638
+ .wrap_struct_name = "Farce::Internal::UnsafeTreeMap",
639
+ .function = {
640
+ .dmark = tc_core_map_mark,
641
+ .dfree = tc_core_map_free,
642
+ .dsize = tc_core_map_memsize,
643
+ .dcompact = tc_core_map_compact,
644
+ },
645
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
646
+ };
647
+
648
+ static const rb_data_type_t tc_map_type = {
649
+ .wrap_struct_name = "Farce::Internal::TreeMap",
650
+ .function = {
651
+ .dmark = tc_core_map_mark,
652
+ .dfree = tc_core_map_free,
653
+ .dsize = tc_core_map_memsize,
654
+ .dcompact = tc_core_map_compact,
655
+ },
656
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
657
+ };
658
+
659
+ static const rb_data_type_t tc_shared_map_type = {
660
+ .wrap_struct_name = "Farce::Internal::ShareableTreeMap",
661
+ .function = {
662
+ .dmark = tc_core_map_mark,
663
+ .dfree = tc_core_map_free,
664
+ .dsize = tc_core_map_memsize,
665
+ .dcompact = tc_core_map_compact,
666
+ },
667
+ .flags = RUBY_TYPED_FROZEN_SHAREABLE,
668
+ };
669
+
670
+ static VALUE
671
+ tc_core_map_allocate(VALUE klass, const rb_data_type_t *type,
672
+ bool synchronized, bool shareable_container,
673
+ bool shareable_keys)
674
+ {
675
+ tc_core_map *core;
676
+ VALUE self = TypedData_Make_Struct(klass, tc_core_map, type, core);
677
+ int error;
678
+
679
+ core->map.root = NULL;
680
+ core->map.size = 0;
681
+ core->map.guard = 0;
682
+ core->map.revision = 0;
683
+ core->transaction_reserved = false;
684
+ core->owner_fiber = Qnil;
685
+ core->owner_ruby_thread = Qnil;
686
+ core->waiters = NULL;
687
+ core->last_waiter = NULL;
688
+ core->publication_owner_fiber = Qnil;
689
+ core->publication_owner_ruby_thread = Qnil;
690
+ core->mutex_initialized = false;
691
+ core->synchronized = synchronized;
692
+ core->shareable_container = shareable_container;
693
+ core->shareable_keys = shareable_keys;
694
+ RUBY_ATOMIC_SET(core->publication_state, TC_MAP_UNINITIALIZED);
695
+
696
+ if (synchronized) {
697
+ error = pthread_mutex_init(&core->lock, NULL);
698
+ if (error != 0) rb_syserr_fail(error, "pthread_mutex_init");
699
+ core->mutex_initialized = true;
700
+ }
701
+ return self;
702
+ }
703
+
704
+ static VALUE
705
+ tc_unsafe_map_allocate(VALUE klass)
706
+ {
707
+ return tc_core_map_allocate(klass, &tc_unsafe_map_type, false, false, true);
708
+ }
709
+
710
+ static VALUE
711
+ tc_map_allocate(VALUE klass)
712
+ {
713
+ return tc_core_map_allocate(klass, &tc_map_type, true, false, true);
714
+ }
715
+
716
+ static VALUE
717
+ tc_mutable_map_allocate(VALUE klass)
718
+ {
719
+ return tc_core_map_allocate(klass, &tc_map_type, true, false, false);
720
+ }
721
+
722
+ static VALUE
723
+ tc_shareable_map_allocate(VALUE klass)
724
+ {
725
+ return tc_core_map_allocate(klass, &tc_shared_map_type, true, true, true);
726
+ }
727
+
728
+ static tc_core_map *
729
+ tc_core_map_get_raw(VALUE self)
730
+ {
731
+ tc_core_map *core;
732
+
733
+ if (rb_typeddata_is_kind_of(self, &tc_unsafe_map_type)) {
734
+ TypedData_Get_Struct(self, tc_core_map, &tc_unsafe_map_type, core);
735
+ return core;
736
+ }
737
+ if (rb_typeddata_is_kind_of(self, &tc_map_type)) {
738
+ TypedData_Get_Struct(self, tc_core_map, &tc_map_type, core);
739
+ return core;
740
+ }
741
+ if (rb_typeddata_is_kind_of(self, &tc_shared_map_type)) {
742
+ TypedData_Get_Struct(self, tc_core_map, &tc_shared_map_type, core);
743
+ return core;
744
+ }
745
+ rb_raise(rb_eTypeError, "wrong tree map type");
746
+ }
747
+
748
+ static void
749
+ tc_core_map_validate_references(VALUE self)
750
+ {
751
+ tc_core_map *core;
752
+ TypedData_Get_Struct(self, tc_core_map, &tc_shared_map_type, core);
753
+ tc_map_validate_shareable_node(core->map.root);
754
+ }
755
+
756
+ static tc_core_map *
757
+ tc_core_map_get(VALUE self)
758
+ {
759
+ tc_core_map *core = tc_core_map_get_raw(self);
760
+
761
+ if (core->synchronized) {
762
+ rb_atomic_t state = RUBY_ATOMIC_LOAD(core->publication_state);
763
+
764
+ /* A clean frozen typed object can become visible before recursive
765
+ * publication returns. Join every active publication, then recheck so
766
+ * a failed traversal cannot expose a half-published shared map. The
767
+ * steady-state path is one atomic load. */
768
+ if (tc_core_map_publication_busy(state)) {
769
+ tc_core_map_wait_for_publication(self, core);
770
+ state = RUBY_ATOMIC_LOAD(core->publication_state);
771
+ }
772
+ if (state != TC_MAP_INITIALIZED) {
773
+ rb_raise(rb_eRuntimeError, "uninitialized tree map");
774
+ }
775
+ return core;
776
+ }
777
+ if (RUBY_ATOMIC_LOAD(core->publication_state) != TC_MAP_INITIALIZED) {
778
+ rb_raise(rb_eRuntimeError, "uninitialized tree map");
779
+ }
780
+ return core;
781
+ }
782
+
783
+ typedef struct {
784
+ tc_core_map *core;
785
+ tc_core_map_waiter waiter;
786
+ } tc_core_map_fiber_wait;
787
+
788
+ static void
789
+ tc_core_map_set_fd_flags(int fd)
790
+ {
791
+ #ifdef _WIN32
792
+ (void)fd;
793
+ #else
794
+ int flags = fcntl(fd, F_GETFL, 0);
795
+ if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
796
+ flags = fcntl(fd, F_GETFD, 0);
797
+ if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
798
+ #endif
799
+ }
800
+
801
+ /* Called with core->lock held. The waiter remains linked until its ensure
802
+ * cleanup so an interrupt cannot leave a dangling stack pointer behind. */
803
+ static void
804
+ tc_core_map_notify_one_locked(tc_core_map *core)
805
+ {
806
+ unsigned char byte = 1;
807
+
808
+ for (tc_core_map_waiter *waiter = core->waiters;
809
+ waiter != NULL;
810
+ waiter = waiter->next) {
811
+ ssize_t result;
812
+
813
+ if (waiter->notified) continue;
814
+ do {
815
+ result = write(waiter->write_fd, &byte, 1);
816
+ } while (result < 0 && errno == EINTR);
817
+ if (result == 1 ||
818
+ (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
819
+ waiter->notified = true;
820
+ return;
821
+ }
822
+ }
823
+ }
824
+
825
+ static VALUE
826
+ tc_core_map_fiber_wait_body(VALUE opaque)
827
+ {
828
+ tc_core_map_fiber_wait *wait = (tc_core_map_fiber_wait *)opaque;
829
+ return containers_wait_for_readable(wait->waiter.read_fd, Qnil) ? Qtrue : Qfalse;
830
+ }
831
+
832
+ static VALUE
833
+ tc_core_map_fiber_wait_cleanup(VALUE opaque)
834
+ {
835
+ tc_core_map_fiber_wait *wait = (tc_core_map_fiber_wait *)opaque;
836
+ tc_core_map_waiter *previous = NULL;
837
+ tc_core_map_waiter **cursor;
838
+
839
+ pthread_mutex_lock(&wait->core->lock);
840
+ for (cursor = &wait->core->waiters; *cursor != NULL; cursor = &(*cursor)->next) {
841
+ if (*cursor == &wait->waiter) {
842
+ *cursor = wait->waiter.next;
843
+ if (wait->core->last_waiter == &wait->waiter) {
844
+ wait->core->last_waiter = previous;
845
+ }
846
+ break;
847
+ }
848
+ previous = *cursor;
849
+ }
850
+ /* Notification makes a waiter runnable, but does not reserve ownership.
851
+ * Always pass the baton if a notified waiter leaves while ownerless,
852
+ * including cancellation after rb_io_wait returned but before the lock
853
+ * acquisition loop claimed ownership. */
854
+ if (wait->waiter.notified &&
855
+ NIL_P(wait->core->owner_fiber) &&
856
+ !tc_core_map_publication_busy(
857
+ RUBY_ATOMIC_LOAD(wait->core->publication_state)
858
+ )) {
859
+ tc_core_map_notify_one_locked(wait->core);
860
+ }
861
+ pthread_mutex_unlock(&wait->core->lock);
862
+
863
+ close(wait->waiter.read_fd);
864
+ close(wait->waiter.write_fd);
865
+ return Qnil;
866
+ }
867
+
868
+ /* Called with core->lock held and always returns with it released. */
869
+ static void
870
+ tc_core_map_wait_once(tc_core_map *core)
871
+ {
872
+ int descriptors[2];
873
+
874
+ if (pipe(descriptors) != 0) {
875
+ pthread_mutex_unlock(&core->lock);
876
+ rb_sys_fail("pipe");
877
+ }
878
+ tc_core_map_set_fd_flags(descriptors[0]);
879
+ tc_core_map_set_fd_flags(descriptors[1]);
880
+
881
+ tc_core_map_fiber_wait wait = {
882
+ .core = core,
883
+ .waiter = {
884
+ .read_fd = descriptors[0],
885
+ .write_fd = descriptors[1],
886
+ .notified = false,
887
+ .next = NULL,
888
+ },
889
+ };
890
+
891
+ if (core->last_waiter != NULL) core->last_waiter->next = &wait.waiter;
892
+ else core->waiters = &wait.waiter;
893
+ core->last_waiter = &wait.waiter;
894
+ pthread_mutex_unlock(&core->lock);
895
+
896
+ (void)rb_ensure(
897
+ tc_core_map_fiber_wait_body,
898
+ (VALUE)&wait,
899
+ tc_core_map_fiber_wait_cleanup,
900
+ (VALUE)&wait
901
+ );
902
+ }
903
+
904
+ /* CRuby needs logical Fiber ownership rather than pthread ownership: multiple
905
+ * scheduled Fibers can contend on the same native thread. Descriptor waits
906
+ * let rb_io_wait park only the current Fiber while its owner resumes. */
907
+ static void
908
+ tc_core_map_lock(VALUE self, tc_core_map *core)
909
+ {
910
+ VALUE current = rb_fiber_current();
911
+ VALUE current_thread = rb_thread_current();
912
+ VALUE scheduler = rb_fiber_scheduler_current();
913
+
914
+ (void)self;
915
+ for (;;) {
916
+ bool publication_busy;
917
+
918
+ pthread_mutex_lock(&core->lock);
919
+ publication_busy = tc_core_map_publication_busy(
920
+ RUBY_ATOMIC_LOAD(core->publication_state)
921
+ );
922
+ if (core->owner_fiber == current ||
923
+ (publication_busy && core->publication_owner_fiber == current)) {
924
+ pthread_mutex_unlock(&core->lock);
925
+ rb_raise(rb_eThreadError, "deadlock; recursive tree map access");
926
+ }
927
+ if (NIL_P(core->owner_fiber) && !publication_busy) {
928
+ core->owner_fiber = current;
929
+ core->owner_ruby_thread = current_thread;
930
+ pthread_mutex_unlock(&core->lock);
931
+ return;
932
+ }
933
+ if ((core->owner_ruby_thread == current_thread ||
934
+ (publication_busy &&
935
+ core->publication_owner_ruby_thread == current_thread)) &&
936
+ NIL_P(scheduler)) {
937
+ pthread_mutex_unlock(&core->lock);
938
+ rb_raise(
939
+ rb_eThreadError,
940
+ "deadlock; tree map is owned by another unscheduled fiber"
941
+ );
942
+ }
943
+ tc_core_map_wait_once(core);
944
+ }
945
+ }
946
+
947
+ static void
948
+ tc_core_map_unlock(tc_core_map *core)
949
+ {
950
+ rb_atomic_t state;
951
+
952
+ pthread_mutex_lock(&core->lock);
953
+ state = RUBY_ATOMIC_LOAD(core->publication_state);
954
+ if (state == TC_MAP_PUBLISHED) {
955
+ RUBY_ATOMIC_SET(core->publication_state, TC_MAP_INITIALIZED);
956
+ }
957
+ else if (state == TC_MAP_PUBLISHING) {
958
+ RUBY_ATOMIC_SET(core->publication_state, TC_MAP_UNINITIALIZED);
959
+ }
960
+ core->owner_fiber = Qnil;
961
+ core->owner_ruby_thread = Qnil;
962
+ core->publication_owner_fiber = Qnil;
963
+ core->publication_owner_ruby_thread = Qnil;
964
+ tc_core_map_notify_one_locked(core);
965
+ pthread_mutex_unlock(&core->lock);
966
+ }
967
+
968
+ typedef struct {
969
+ VALUE self;
970
+ tc_core_map *core;
971
+ bool acquired;
972
+ } tc_core_map_publication_wait;
973
+
974
+ static VALUE
975
+ tc_core_map_publication_wait_body(VALUE opaque)
976
+ {
977
+ tc_core_map_publication_wait *wait =
978
+ (tc_core_map_publication_wait *)opaque;
979
+
980
+ tc_core_map_lock(wait->self, wait->core);
981
+ wait->acquired = true;
982
+ return Qnil;
983
+ }
984
+
985
+ static VALUE
986
+ tc_core_map_publication_wait_ensure(VALUE opaque)
987
+ {
988
+ tc_core_map_publication_wait *wait =
989
+ (tc_core_map_publication_wait *)opaque;
990
+
991
+ if (wait->acquired) tc_core_map_unlock(wait->core);
992
+ return Qnil;
993
+ }
994
+
995
+ static void
996
+ tc_core_map_wait_for_publication(VALUE self, tc_core_map *core)
997
+ {
998
+ tc_core_map_publication_wait wait = {
999
+ .self = self,
1000
+ .core = core,
1001
+ .acquired = false,
1002
+ };
1003
+
1004
+ /* Retaining the publisher as logical owner makes recursive publication
1005
+ * callbacks fail with the ordinary ThreadError, while other Ractors and
1006
+ * scheduler-managed Fibers park until the outer operation unlocks. */
1007
+ (void)rb_ensure(
1008
+ tc_core_map_publication_wait_body,
1009
+ (VALUE)&wait,
1010
+ tc_core_map_publication_wait_ensure,
1011
+ (VALUE)&wait
1012
+ );
1013
+ }
1014
+
1015
+ typedef VALUE (*tc_core_map_operation)(VALUE opaque);
1016
+
1017
+ typedef struct {
1018
+ VALUE self;
1019
+ tc_core_map *core;
1020
+ VALUE key;
1021
+ VALUE value;
1022
+ bool found;
1023
+ } tc_core_map_arguments;
1024
+
1025
+ typedef struct {
1026
+ tc_core_map_operation operation;
1027
+ VALUE argument;
1028
+ } tc_core_map_call;
1029
+
1030
+ static VALUE
1031
+ tc_core_map_call_body(VALUE opaque)
1032
+ {
1033
+ tc_core_map_call *call = (tc_core_map_call *)opaque;
1034
+ return call->operation(call->argument);
1035
+ }
1036
+
1037
+ static VALUE
1038
+ tc_core_map_unlock_ensure(VALUE opaque)
1039
+ {
1040
+ tc_core_map_unlock((tc_core_map *)opaque);
1041
+ return Qnil;
1042
+ }
1043
+
1044
+ static VALUE
1045
+ tc_core_map_call_locked(VALUE self, tc_core_map *core,
1046
+ tc_core_map_operation operation,
1047
+ void *arguments)
1048
+ {
1049
+ tc_core_map_call call;
1050
+
1051
+ if (!core->synchronized) return operation((VALUE)arguments);
1052
+ tc_core_map_lock(self, core);
1053
+ call.operation = operation;
1054
+ call.argument = (VALUE)arguments;
1055
+ return rb_ensure(
1056
+ tc_core_map_call_body,
1057
+ (VALUE)&call,
1058
+ tc_core_map_unlock_ensure,
1059
+ (VALUE)core
1060
+ );
1061
+ }
1062
+
1063
+ static void
1064
+ tc_core_map_check_key(const tc_core_map *core, VALUE key)
1065
+ {
1066
+ if (core->shareable_keys && !rb_ractor_shareable_p(key)) {
1067
+ rb_raise(tc_eIsolationError, "key is not shareable: %" PRIsVALUE,
1068
+ rb_inspect(key));
1069
+ }
1070
+ }
1071
+
1072
+ static void
1073
+ tc_core_map_check_value(const tc_core_map *core, VALUE value)
1074
+ {
1075
+ if (core->shareable_container && !rb_ractor_shareable_p(value)) {
1076
+ rb_raise(tc_eIsolationError, "value is not shareable: %" PRIsVALUE,
1077
+ rb_inspect(value));
1078
+ }
1079
+ }
1080
+
1081
+ static void
1082
+ tc_core_map_check_mutation(VALUE self, const tc_core_map *core)
1083
+ {
1084
+ rb_check_frozen(self);
1085
+ if (core->map.guard != 0) {
1086
+ rb_raise(rb_eRuntimeError, "container cannot be modified during comparison");
1087
+ }
1088
+ }
1089
+
1090
+ static int
1091
+ tc_core_map_initialize_entry(VALUE key, VALUE value, VALUE opaque)
1092
+ {
1093
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1094
+
1095
+ key = containers_normalize_string_key(key);
1096
+ tc_core_map_check_key(arguments->core, key);
1097
+ tc_core_map_check_value(arguments->core, value);
1098
+ tc_map_store_unlocked(
1099
+ arguments->self,
1100
+ &arguments->core->map,
1101
+ key,
1102
+ value,
1103
+ true
1104
+ );
1105
+ return ST_CONTINUE;
1106
+ }
1107
+
1108
+ typedef struct {
1109
+ VALUE self;
1110
+ tc_core_map *core;
1111
+ } tc_core_map_publication;
1112
+
1113
+ static VALUE
1114
+ tc_core_map_make_shareable(VALUE opaque)
1115
+ {
1116
+ tc_core_map_publication *publication =
1117
+ (tc_core_map_publication *)opaque;
1118
+
1119
+ return containers_publish_native_with_references(
1120
+ publication->self,
1121
+ tc_core_map_validate_references
1122
+ );
1123
+ }
1124
+
1125
+ static VALUE
1126
+ tc_core_map_finish_publication(VALUE opaque)
1127
+ {
1128
+ tc_core_map_publication *publication =
1129
+ (tc_core_map_publication *)opaque;
1130
+ bool shareable = rb_ractor_shareable_p(publication->self);
1131
+
1132
+ /* rb_ractor_make_shareable can be interrupted after setting the object's
1133
+ * shareable flag. Publish the initialized bit whenever the object reached
1134
+ * that coherent state, even if the caller is about to receive an async
1135
+ * exception. The outer operation ensure retains the publishing state until
1136
+ * it clears the logical lock and wakes a waiter. */
1137
+ pthread_mutex_lock(&publication->core->lock);
1138
+ if (shareable) {
1139
+ RUBY_ATOMIC_SET(publication->core->publication_state, TC_MAP_PUBLISHED);
1140
+ }
1141
+ pthread_mutex_unlock(&publication->core->lock);
1142
+ return Qnil;
1143
+ }
1144
+
1145
+ static VALUE
1146
+ tc_core_map_publish_shareable(VALUE self, tc_core_map *core)
1147
+ {
1148
+ tc_core_map_publication publication = {
1149
+ .self = self,
1150
+ .core = core,
1151
+ };
1152
+
1153
+ /* Install the gate before primitive freeze can make this typed object
1154
+ * Ractor-visible. Move the logical owner out of dmark before recursive
1155
+ * sharing, while retaining it for callback reentry/deadlock detection. */
1156
+ pthread_mutex_lock(&core->lock);
1157
+ RUBY_ATOMIC_SET(core->publication_state, TC_MAP_PUBLISHING);
1158
+ core->publication_owner_fiber = core->owner_fiber;
1159
+ core->publication_owner_ruby_thread = core->owner_ruby_thread;
1160
+ core->owner_fiber = Qnil;
1161
+ core->owner_ruby_thread = Qnil;
1162
+ pthread_mutex_unlock(&core->lock);
1163
+ return rb_ensure(
1164
+ tc_core_map_make_shareable,
1165
+ (VALUE)&publication,
1166
+ tc_core_map_finish_publication,
1167
+ (VALUE)&publication
1168
+ );
1169
+ }
1170
+
1171
+ static void
1172
+ tc_core_map_check_initializable(VALUE self, tc_core_map *core)
1173
+ {
1174
+ rb_atomic_t state = RUBY_ATOMIC_LOAD(core->publication_state);
1175
+
1176
+ if (state == TC_MAP_INITIALIZED) {
1177
+ rb_raise(rb_eRuntimeError, "tree map is already initialized");
1178
+ }
1179
+ rb_check_frozen(self);
1180
+
1181
+ /* Publication marks the object busy before primitive freeze. Reject the
1182
+ * narrow pre-freeze window here so a competing initializer cannot enter
1183
+ * #to_hash while publication is already in progress. */
1184
+ if (RUBY_ATOMIC_LOAD(core->publication_state) != TC_MAP_UNINITIALIZED) {
1185
+ rb_raise(rb_eRuntimeError, "tree map is already initialized");
1186
+ }
1187
+ }
1188
+
1189
+ static VALUE
1190
+ tc_core_map_initialize_body(VALUE opaque)
1191
+ {
1192
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1193
+ tc_core_map *core = arguments->core;
1194
+
1195
+ /* #to_hash runs before this synchronized body and may itself initialize
1196
+ * the receiver. Two shared-map initializers can also pass the fast outer
1197
+ * check concurrently. The check under the operation lock is authoritative
1198
+ * and must precede every mutation. */
1199
+ if (RUBY_ATOMIC_LOAD(core->publication_state) != TC_MAP_UNINITIALIZED) {
1200
+ rb_raise(rb_eRuntimeError, "tree map is already initialized");
1201
+ }
1202
+ /* Shareable maps are frozen only after initialization succeeds. A freeze
1203
+ * performed before initialization or from #to_hash/comparison callbacks
1204
+ * must still prevent publication, just as it does for the ordinary
1205
+ * variants and the Ruby/JVM implementations. */
1206
+ rb_check_frozen(arguments->self);
1207
+ tc_core_map_check_mutation(arguments->self, core);
1208
+ tc_map_free_node(core->map.root);
1209
+ core->map.root = NULL;
1210
+ core->map.size = 0;
1211
+ if (!NIL_P(arguments->value)) {
1212
+ rb_hash_foreach(arguments->value, tc_core_map_initialize_entry, opaque);
1213
+ }
1214
+
1215
+ if (core->shareable_container) {
1216
+ /* Publication installs its logical gate before primitive freezing and
1217
+ * keeps that whole transition under nested ensure cleanup. */
1218
+ return tc_core_map_publish_shareable(arguments->self, core);
1219
+ }
1220
+ RUBY_ATOMIC_SET(core->publication_state, TC_MAP_INITIALIZED);
1221
+ return arguments->self;
1222
+ }
1223
+
1224
+ static VALUE
1225
+ tc_core_map_initialize(int argc, VALUE *argv, VALUE self)
1226
+ {
1227
+ tc_core_map *core = tc_core_map_get_raw(self);
1228
+ tc_core_map_arguments arguments = {
1229
+ .self = self,
1230
+ .core = core,
1231
+ .key = Qnil,
1232
+ .value = Qnil,
1233
+ };
1234
+ VALUE entries = Qnil;
1235
+ VALUE hash;
1236
+
1237
+ rb_scan_args(argc, argv, "01", &entries);
1238
+ tc_core_map_check_initializable(self, core);
1239
+
1240
+ if (!NIL_P(entries)) {
1241
+ hash = rb_check_hash_type(entries);
1242
+ if (NIL_P(hash)) {
1243
+ rb_raise(rb_eTypeError, "entries must be a Hash or respond to #to_hash");
1244
+ }
1245
+ arguments.value = hash;
1246
+ }
1247
+
1248
+ /* A descendant's #freeze can recursively call #initialize while the first
1249
+ * publication is traversing its instance variables. The shared receiver is
1250
+ * primitively frozen before that traversal, so reject the recursive call
1251
+ * here, before it can wait on the publisher's logical lock. */
1252
+ tc_core_map_check_initializable(self, core);
1253
+
1254
+ return tc_core_map_call_locked(
1255
+ self,
1256
+ core,
1257
+ tc_core_map_initialize_body,
1258
+ &arguments
1259
+ );
1260
+ }
1261
+
1262
+ static VALUE
1263
+ tc_core_map_initialize_copy(VALUE self, VALUE other)
1264
+ {
1265
+ (void)self;
1266
+ (void)other;
1267
+ rb_raise(rb_eTypeError, "tree maps cannot be copied");
1268
+ }
1269
+
1270
+ static VALUE
1271
+ tc_core_map_aref_body(VALUE opaque)
1272
+ {
1273
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1274
+ tc_map_node *node = tc_map_find(
1275
+ &arguments->core->map,
1276
+ arguments->key,
1277
+ NULL,
1278
+ NULL
1279
+ );
1280
+
1281
+ arguments->found = node != NULL;
1282
+ return node == NULL ? Qnil : node->value;
1283
+ }
1284
+
1285
+ static VALUE
1286
+ tc_core_map_aref(VALUE self, VALUE key)
1287
+ {
1288
+ tc_core_map *core = tc_core_map_get(self);
1289
+
1290
+ key = containers_normalize_string_key(key);
1291
+ tc_core_map_arguments arguments = {
1292
+ .self = self,
1293
+ .core = core,
1294
+ .key = key,
1295
+ .value = Qnil,
1296
+ };
1297
+
1298
+ tc_core_map_check_key(core, key);
1299
+ return tc_core_map_call_locked(self, core, tc_core_map_aref_body, &arguments);
1300
+ }
1301
+
1302
+ static VALUE
1303
+ tc_core_map_prepare_key(VALUE self, VALUE key)
1304
+ {
1305
+ tc_core_map *core = tc_core_map_get(self);
1306
+ key = containers_normalize_string_key(key);
1307
+ tc_core_map_check_key(core, key);
1308
+ return key;
1309
+ }
1310
+
1311
+ static VALUE
1312
+ tc_core_map_fetch(int argc, VALUE *argv, VALUE self)
1313
+ {
1314
+ VALUE key;
1315
+ VALUE normalized_key;
1316
+ VALUE default_value;
1317
+ VALUE result;
1318
+ bool default_given;
1319
+ bool block_given;
1320
+ tc_core_map *core;
1321
+ tc_core_map_arguments arguments;
1322
+
1323
+ rb_scan_args(argc, argv, "11", &key, &default_value);
1324
+ default_given = argc == 2;
1325
+ block_given = rb_block_given_p();
1326
+ if (block_given && default_given) {
1327
+ rb_warn("block supersedes default value argument");
1328
+ }
1329
+
1330
+ core = tc_core_map_get(self);
1331
+ normalized_key = containers_normalize_string_key(key);
1332
+ arguments = (tc_core_map_arguments){
1333
+ .self = self,
1334
+ .core = core,
1335
+ .key = normalized_key,
1336
+ .value = Qnil,
1337
+ .found = false,
1338
+ };
1339
+ tc_core_map_check_key(core, normalized_key);
1340
+ result = tc_core_map_call_locked(
1341
+ self,
1342
+ core,
1343
+ tc_core_map_aref_body,
1344
+ &arguments
1345
+ );
1346
+
1347
+ if (arguments.found) return result;
1348
+ if (block_given) return rb_yield(key);
1349
+ if (default_given) return default_value;
1350
+ containers_raise_key_error(self, key);
1351
+ return Qnil;
1352
+ }
1353
+
1354
+ static VALUE
1355
+ tc_core_map_key_p(VALUE self, VALUE key)
1356
+ {
1357
+ tc_core_map *core = tc_core_map_get(self);
1358
+
1359
+ key = containers_normalize_string_key(key);
1360
+ tc_core_map_arguments arguments = {
1361
+ .self = self,
1362
+ .core = core,
1363
+ .key = key,
1364
+ .value = Qnil,
1365
+ .found = false,
1366
+ };
1367
+
1368
+ tc_core_map_check_key(core, key);
1369
+ (void)tc_core_map_call_locked(
1370
+ self,
1371
+ core,
1372
+ tc_core_map_aref_body,
1373
+ &arguments
1374
+ );
1375
+ return arguments.found ? Qtrue : Qfalse;
1376
+ }
1377
+
1378
+ static VALUE
1379
+ tc_core_map_getkey_body(VALUE opaque)
1380
+ {
1381
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1382
+ tc_map_node *node = tc_map_find(
1383
+ &arguments->core->map,
1384
+ arguments->key,
1385
+ NULL,
1386
+ NULL
1387
+ );
1388
+
1389
+ return node == NULL ? Qnil : node->key;
1390
+ }
1391
+
1392
+ static VALUE
1393
+ tc_core_map_getkey(VALUE self, VALUE key)
1394
+ {
1395
+ tc_core_map *core = tc_core_map_get(self);
1396
+
1397
+ key = containers_normalize_string_key(key);
1398
+ tc_core_map_arguments arguments = {
1399
+ .self = self,
1400
+ .core = core,
1401
+ .key = key,
1402
+ .value = Qnil,
1403
+ };
1404
+
1405
+ tc_core_map_check_key(core, key);
1406
+ return tc_core_map_call_locked(
1407
+ self,
1408
+ core,
1409
+ tc_core_map_getkey_body,
1410
+ &arguments
1411
+ );
1412
+ }
1413
+
1414
+ static VALUE
1415
+ tc_core_map_store_body(VALUE opaque)
1416
+ {
1417
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1418
+
1419
+ tc_core_map_check_mutation(arguments->self, arguments->core);
1420
+ return tc_map_store_unlocked(
1421
+ arguments->self,
1422
+ &arguments->core->map,
1423
+ arguments->key,
1424
+ arguments->value,
1425
+ true
1426
+ );
1427
+ }
1428
+
1429
+ static VALUE
1430
+ tc_core_map_store(VALUE self, VALUE key, VALUE value)
1431
+ {
1432
+ tc_core_map *core = tc_core_map_get(self);
1433
+
1434
+ key = containers_normalize_string_key(key);
1435
+ tc_core_map_arguments arguments = {
1436
+ .self = self,
1437
+ .core = core,
1438
+ .key = key,
1439
+ .value = value,
1440
+ };
1441
+
1442
+ tc_core_map_check_key(core, key);
1443
+ tc_core_map_check_value(core, value);
1444
+ return tc_core_map_call_locked(self, core, tc_core_map_store_body, &arguments);
1445
+ }
1446
+
1447
+ static VALUE
1448
+ tc_core_map_delete_body(VALUE opaque)
1449
+ {
1450
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1451
+ tc_map *map = &arguments->core->map;
1452
+ tc_map_node *node;
1453
+ VALUE value;
1454
+
1455
+ tc_core_map_check_mutation(arguments->self, arguments->core);
1456
+ node = tc_map_find(map, arguments->key, NULL, NULL);
1457
+ if (node == NULL) return Qnil;
1458
+
1459
+ tc_core_map_check_mutation(arguments->self, arguments->core);
1460
+ value = node->value;
1461
+ tc_remove_link(&map->root, &node->link);
1462
+ map->size--;
1463
+ map->revision++;
1464
+ xfree(node);
1465
+ return value;
1466
+ }
1467
+
1468
+ static VALUE
1469
+ tc_core_map_delete(VALUE self, VALUE key)
1470
+ {
1471
+ tc_core_map *core = tc_core_map_get(self);
1472
+
1473
+ key = containers_normalize_string_key(key);
1474
+ tc_core_map_arguments arguments = {
1475
+ .self = self,
1476
+ .core = core,
1477
+ .key = key,
1478
+ .value = Qnil,
1479
+ };
1480
+
1481
+ tc_core_map_check_key(core, key);
1482
+ return tc_core_map_call_locked(self, core, tc_core_map_delete_body, &arguments);
1483
+ }
1484
+
1485
+ static VALUE
1486
+ tc_core_map_first_key_body(VALUE opaque)
1487
+ {
1488
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1489
+ tc_link *first = tc_link_minimum(arguments->core->map.root);
1490
+
1491
+ return first == NULL ? Qnil : TC_MAP_NODE(first)->key;
1492
+ }
1493
+
1494
+ static VALUE
1495
+ tc_core_map_first_key(VALUE self)
1496
+ {
1497
+ tc_core_map *core = tc_core_map_get(self);
1498
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1499
+
1500
+ return tc_core_map_call_locked(self, core, tc_core_map_first_key_body, &arguments);
1501
+ }
1502
+
1503
+ static VALUE
1504
+ tc_core_map_last_key_body(VALUE opaque)
1505
+ {
1506
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1507
+ tc_link *last = tc_link_maximum(arguments->core->map.root);
1508
+
1509
+ return last == NULL ? Qnil : TC_MAP_NODE(last)->key;
1510
+ }
1511
+
1512
+ static VALUE
1513
+ tc_core_map_last_key(VALUE self)
1514
+ {
1515
+ tc_core_map *core = tc_core_map_get(self);
1516
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1517
+
1518
+ return tc_core_map_call_locked(self, core, tc_core_map_last_key_body, &arguments);
1519
+ }
1520
+
1521
+ static VALUE
1522
+ tc_core_map_shift_body(VALUE opaque)
1523
+ {
1524
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1525
+ tc_map *map = &arguments->core->map;
1526
+ tc_link *first;
1527
+ tc_map_node *node;
1528
+ VALUE pair;
1529
+
1530
+ tc_core_map_check_mutation(arguments->self, arguments->core);
1531
+ first = tc_link_minimum(map->root);
1532
+ if (first == NULL) return Qnil;
1533
+
1534
+ node = TC_MAP_NODE(first);
1535
+ pair = rb_assoc_new(node->key, node->value);
1536
+ tc_remove_link(&map->root, first);
1537
+ map->size--;
1538
+ map->revision++;
1539
+ xfree(node);
1540
+ return pair;
1541
+ }
1542
+
1543
+ static VALUE
1544
+ tc_core_map_shift(VALUE self)
1545
+ {
1546
+ tc_core_map *core = tc_core_map_get(self);
1547
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1548
+
1549
+ return tc_core_map_call_locked(self, core, tc_core_map_shift_body, &arguments);
1550
+ }
1551
+
1552
+ static VALUE
1553
+ tc_core_map_pop_body(VALUE opaque)
1554
+ {
1555
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1556
+ tc_map *map = &arguments->core->map;
1557
+ tc_link *last;
1558
+ tc_map_node *node;
1559
+ VALUE pair;
1560
+
1561
+ tc_core_map_check_mutation(arguments->self, arguments->core);
1562
+ last = tc_link_maximum(map->root);
1563
+ if (last == NULL) return Qnil;
1564
+
1565
+ node = TC_MAP_NODE(last);
1566
+ pair = rb_assoc_new(node->key, node->value);
1567
+ tc_remove_link(&map->root, last);
1568
+ map->size--;
1569
+ map->revision++;
1570
+ xfree(node);
1571
+ return pair;
1572
+ }
1573
+
1574
+ static VALUE
1575
+ tc_core_map_pop(VALUE self)
1576
+ {
1577
+ tc_core_map *core = tc_core_map_get(self);
1578
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1579
+
1580
+ return tc_core_map_call_locked(self, core, tc_core_map_pop_body, &arguments);
1581
+ }
1582
+
1583
+ static VALUE
1584
+ tc_core_map_size_body(VALUE opaque)
1585
+ {
1586
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1587
+ return SIZET2NUM(arguments->core->map.size);
1588
+ }
1589
+
1590
+ static VALUE
1591
+ tc_core_map_size(VALUE self)
1592
+ {
1593
+ tc_core_map *core = tc_core_map_get(self);
1594
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1595
+
1596
+ return tc_core_map_call_locked(self, core, tc_core_map_size_body, &arguments);
1597
+ }
1598
+
1599
+ static VALUE
1600
+ tc_core_map_enumerator_size(VALUE self, VALUE arguments, VALUE enumerator)
1601
+ {
1602
+ (void)arguments;
1603
+ (void)enumerator;
1604
+ return tc_core_map_size(self);
1605
+ }
1606
+
1607
+ static void
1608
+ tc_core_map_snapshot_node(tc_link *link, VALUE entries)
1609
+ {
1610
+ tc_map_node *node;
1611
+
1612
+ if (link == NULL) return;
1613
+ tc_core_map_snapshot_node(link->left, entries);
1614
+ node = TC_MAP_NODE(link);
1615
+ rb_ary_push(entries, node->key);
1616
+ rb_ary_push(entries, node->value);
1617
+ tc_core_map_snapshot_node(link->right, entries);
1618
+ }
1619
+
1620
+ static VALUE
1621
+ tc_core_map_snapshot_body(VALUE opaque)
1622
+ {
1623
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1624
+ tc_map *map = &arguments->core->map;
1625
+ VALUE entries = rb_ary_new_capa((long)(map->size * 2));
1626
+
1627
+ tc_core_map_snapshot_node(map->root, entries);
1628
+ return entries;
1629
+ }
1630
+
1631
+ typedef struct {
1632
+ tc_core_map *core;
1633
+ tc_map_node *node;
1634
+ uint64_t revision;
1635
+ VALUE last_key;
1636
+ bool started;
1637
+ } tc_core_map_cursor;
1638
+
1639
+ static tc_map_node *
1640
+ tc_map_find_greater(tc_map *map, VALUE key)
1641
+ {
1642
+ tc_link *link = map->root;
1643
+ tc_map_node *candidate = NULL;
1644
+
1645
+ while (link != NULL) {
1646
+ tc_map_node *node = TC_MAP_NODE(link);
1647
+ int comparison = tc_compare_keys(&map->guard, key, node->key);
1648
+
1649
+ if (comparison < 0) {
1650
+ candidate = node;
1651
+ link = link->left;
1652
+ }
1653
+ else {
1654
+ link = link->right;
1655
+ }
1656
+ }
1657
+ return candidate;
1658
+ }
1659
+
1660
+ static VALUE
1661
+ tc_core_map_cursor_step(VALUE opaque)
1662
+ {
1663
+ tc_core_map_cursor *cursor = (tc_core_map_cursor *)opaque;
1664
+ tc_map *map = &cursor->core->map;
1665
+ tc_map_node *node;
1666
+
1667
+ if (!cursor->started) {
1668
+ tc_link *first = tc_link_minimum(map->root);
1669
+ node = first == NULL ? NULL : TC_MAP_NODE(first);
1670
+ }
1671
+ else if (cursor->revision == map->revision) {
1672
+ tc_link *next = tc_link_successor(&cursor->node->link);
1673
+ node = next == NULL ? NULL : TC_MAP_NODE(next);
1674
+ }
1675
+ else {
1676
+ node = tc_map_find_greater(map, cursor->last_key);
1677
+ }
1678
+
1679
+ cursor->started = true;
1680
+ cursor->node = node;
1681
+ cursor->revision = map->revision;
1682
+ if (node == NULL) return Qnil;
1683
+ cursor->last_key = node->key;
1684
+ return rb_assoc_new(node->key, node->value);
1685
+ }
1686
+
1687
+ static VALUE
1688
+ tc_core_map_each_live(VALUE self)
1689
+ {
1690
+ tc_core_map *core = tc_core_map_get(self);
1691
+ tc_core_map_cursor cursor = {
1692
+ .core = core,
1693
+ .node = NULL,
1694
+ .revision = 0,
1695
+ .last_key = Qnil,
1696
+ .started = false,
1697
+ };
1698
+ VALUE pair;
1699
+
1700
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, tc_core_map_enumerator_size);
1701
+ while (!NIL_P(pair = tc_core_map_call_locked(
1702
+ self,
1703
+ core,
1704
+ tc_core_map_cursor_step,
1705
+ &cursor
1706
+ ))) {
1707
+ rb_yield(pair);
1708
+ }
1709
+ RB_GC_GUARD(cursor.last_key);
1710
+ return self;
1711
+ }
1712
+
1713
+ static VALUE
1714
+ tc_core_map_each(VALUE self)
1715
+ {
1716
+ VALUE entries;
1717
+ long entry_count;
1718
+ long index;
1719
+ tc_core_map *core;
1720
+ tc_core_map_arguments arguments;
1721
+
1722
+ RETURN_SIZED_ENUMERATOR(self, 0, NULL, tc_core_map_enumerator_size);
1723
+ core = tc_core_map_get(self);
1724
+ arguments = (tc_core_map_arguments){.self = self, .core = core};
1725
+ entries = tc_core_map_call_locked(
1726
+ self,
1727
+ core,
1728
+ tc_core_map_snapshot_body,
1729
+ &arguments
1730
+ );
1731
+ entry_count = RARRAY_LEN(entries);
1732
+ for (index = 0; index < entry_count; index += 2) {
1733
+ rb_yield(rb_assoc_new(
1734
+ RARRAY_AREF(entries, index),
1735
+ RARRAY_AREF(entries, index + 1)
1736
+ ));
1737
+ }
1738
+ RB_GC_GUARD(entries);
1739
+ return self;
1740
+ }
1741
+
1742
+ static VALUE
1743
+ tc_core_map_empty_body(VALUE opaque)
1744
+ {
1745
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1746
+ return arguments->core->map.size == 0 ? Qtrue : Qfalse;
1747
+ }
1748
+
1749
+ static VALUE
1750
+ tc_core_map_empty_p(VALUE self)
1751
+ {
1752
+ tc_core_map *core = tc_core_map_get(self);
1753
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1754
+
1755
+ return tc_core_map_call_locked(self, core, tc_core_map_empty_body, &arguments);
1756
+ }
1757
+
1758
+ static VALUE
1759
+ tc_core_map_clear_body(VALUE opaque)
1760
+ {
1761
+ tc_core_map_arguments *arguments = (tc_core_map_arguments *)opaque;
1762
+ tc_map *map = &arguments->core->map;
1763
+
1764
+ tc_core_map_check_mutation(arguments->self, arguments->core);
1765
+ tc_map_free_node(map->root);
1766
+ map->root = NULL;
1767
+ map->size = 0;
1768
+ map->revision++;
1769
+ return arguments->self;
1770
+ }
1771
+
1772
+ static VALUE
1773
+ tc_core_map_clear(VALUE self)
1774
+ {
1775
+ tc_core_map *core = tc_core_map_get(self);
1776
+ tc_core_map_arguments arguments = {.self = self, .core = core};
1777
+
1778
+ return tc_core_map_call_locked(self, core, tc_core_map_clear_body, &arguments);
1779
+ }
1780
+
1781
+ static bool
1782
+ tc_transaction_valid(farce_transaction_entry_t *entry)
1783
+ {
1784
+ tc_core_map *source = entry->source_data;
1785
+ return NIL_P(source->owner_fiber) &&
1786
+ !tc_core_map_publication_busy(RUBY_ATOMIC_LOAD(source->publication_state)) &&
1787
+ source->map.revision == entry->version;
1788
+ }
1789
+
1790
+ static void
1791
+ tc_transaction_apply(farce_transaction_entry_t *entry)
1792
+ {
1793
+ tc_core_map *source = entry->source_data;
1794
+ tc_core_map *working = entry->working_data;
1795
+ tc_link *root = source->map.root;
1796
+ size_t size = source->map.size;
1797
+ source->map.root = working->map.root;
1798
+ source->map.size = working->map.size;
1799
+ source->map.revision++;
1800
+ working->map.root = root;
1801
+ working->map.size = size;
1802
+ working->map.revision++;
1803
+ }
1804
+
1805
+ static void
1806
+ tc_transaction_notify(farce_transaction_entry_t *entry)
1807
+ {
1808
+ tc_core_map_notify_one_locked(entry->source_data);
1809
+ }
1810
+
1811
+ static void
1812
+ tc_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE thread)
1813
+ {
1814
+ tc_core_map *core = entry->source_data;
1815
+ core->transaction_reserved = true;
1816
+ core->owner_fiber = fiber;
1817
+ core->owner_ruby_thread = thread;
1818
+ }
1819
+
1820
+ static void
1821
+ tc_transaction_release(farce_transaction_entry_t *entry)
1822
+ {
1823
+ tc_core_map *core = entry->source_data;
1824
+ core->transaction_reserved = false;
1825
+ core->owner_fiber = Qnil;
1826
+ core->owner_ruby_thread = Qnil;
1827
+ tc_core_map_notify_one_locked(core);
1828
+ }
1829
+
1830
+ static VALUE
1831
+ tc_core_map_freeze(VALUE self)
1832
+ {
1833
+ tc_core_map *core = tc_core_map_get_raw(self);
1834
+ if (!core->mutex_initialized) return rb_obj_freeze(self);
1835
+ pthread_mutex_lock(&core->lock);
1836
+ if (core->transaction_reserved) {
1837
+ pthread_mutex_unlock(&core->lock);
1838
+ tc_core_map_lock(self, core);
1839
+ rb_obj_freeze(self);
1840
+ tc_core_map_unlock(core);
1841
+ }
1842
+ else {
1843
+ rb_obj_freeze(self);
1844
+ pthread_mutex_unlock(&core->lock);
1845
+ }
1846
+ return self;
1847
+ }
1848
+
1849
+ static const farce_transaction_ops_t tc_transaction_ops = {
1850
+ tc_transaction_valid, tc_transaction_apply, tc_transaction_notify,
1851
+ tc_transaction_reserve, tc_transaction_release,
1852
+ };
1853
+
1854
+ typedef struct {
1855
+ tc_core_map_arguments arguments;
1856
+ farce_transaction_entry_t *entry;
1857
+ } tc_transaction_snapshot_arguments;
1858
+
1859
+ static VALUE
1860
+ tc_transaction_snapshot_body(VALUE opaque)
1861
+ {
1862
+ tc_transaction_snapshot_arguments *snapshot = (void *)opaque;
1863
+ snapshot->entry->version = snapshot->arguments.core->map.revision;
1864
+ return tc_core_map_snapshot_body((VALUE)&snapshot->arguments);
1865
+ }
1866
+
1867
+ static VALUE
1868
+ tc_transaction_snapshot(VALUE self)
1869
+ {
1870
+ tc_core_map *source = tc_core_map_get(self);
1871
+ if (!source->synchronized) {
1872
+ rb_raise(rb_eTypeError, "unsafe tree maps do not support transactions");
1873
+ }
1874
+ VALUE working = rb_class_new_instance(0, NULL, rb_obj_class(self));
1875
+ tc_core_map *copy = tc_core_map_get(working);
1876
+ farce_transaction_entry_t *entry;
1877
+ VALUE result = farce_transaction_entry_new(
1878
+ self, working, source, copy, &source->lock, &tc_transaction_ops, &entry
1879
+ );
1880
+ tc_transaction_snapshot_arguments snapshot = {
1881
+ .arguments = {.self = self, .core = source}, .entry = entry,
1882
+ };
1883
+ VALUE pairs = tc_core_map_call_locked(self, source, tc_transaction_snapshot_body, &snapshot);
1884
+ for (long i = 0; i < RARRAY_LEN(pairs); i += 2) {
1885
+ tc_core_map_store(working, RARRAY_AREF(pairs, i), RARRAY_AREF(pairs, i + 1));
1886
+ }
1887
+ RB_GC_GUARD(pairs);
1888
+ return result;
1889
+ }
1890
+
1891
+ static void
1892
+ tc_core_map_define_methods(VALUE klass)
1893
+ {
1894
+ rb_define_method(klass, "freeze", tc_core_map_freeze, 0);
1895
+ rb_define_method(klass, "transaction_snapshot", tc_transaction_snapshot, 0);
1896
+ rb_define_method(klass, "initialize", tc_core_map_initialize, -1);
1897
+ rb_define_method(klass, "initialize_copy", tc_core_map_initialize_copy, 1);
1898
+ rb_define_method(klass, "[]", tc_core_map_aref, 1);
1899
+ rb_define_method(klass, "prepare_key", tc_core_map_prepare_key, 1);
1900
+ rb_define_method(klass, "[]=", tc_core_map_store, 2);
1901
+ rb_define_method(klass, "fetch", tc_core_map_fetch, -1);
1902
+ rb_define_method(klass, "key?", tc_core_map_key_p, 1);
1903
+ rb_define_method(klass, "getkey", tc_core_map_getkey, 1);
1904
+ rb_define_method(klass, "each", tc_core_map_each, 0);
1905
+ rb_define_method(klass, "each_live", tc_core_map_each_live, 0);
1906
+ rb_define_method(klass, "delete", tc_core_map_delete, 1);
1907
+ rb_define_method(klass, "first_key", tc_core_map_first_key, 0);
1908
+ rb_define_method(klass, "last_key", tc_core_map_last_key, 0);
1909
+ rb_define_method(klass, "shift", tc_core_map_shift, 0);
1910
+ rb_define_method(klass, "pop", tc_core_map_pop, 0);
1911
+ rb_define_method(klass, "size", tc_core_map_size, 0);
1912
+ rb_define_method(klass, "length", tc_core_map_size, 0);
1913
+ rb_define_method(klass, "empty?", tc_core_map_empty_p, 0);
1914
+ rb_define_method(klass, "clear", tc_core_map_clear, 0);
1915
+ }
1916
+ void
1917
+ containers_init_tree_maps(VALUE internal)
1918
+ {
1919
+ VALUE unsafe_map_class;
1920
+ VALUE map_class;
1921
+ VALUE mutable_map_class;
1922
+ VALUE shareable_map_class;
1923
+ VALUE ractor;
1924
+ ID ractor_id = rb_intern("Ractor");
1925
+
1926
+ tc_id_compare = rb_intern("<=>");
1927
+ ractor = rb_const_get(rb_cObject, ractor_id);
1928
+ tc_eIsolationError = rb_const_get(ractor, rb_intern("IsolationError"));
1929
+
1930
+ unsafe_map_class = rb_define_class_under(internal, "UnsafeTreeMap", rb_cObject);
1931
+ rb_define_alloc_func(unsafe_map_class, tc_unsafe_map_allocate);
1932
+ tc_core_map_define_methods(unsafe_map_class);
1933
+
1934
+ map_class = rb_define_class_under(internal, "TreeMap", rb_cObject);
1935
+ rb_define_alloc_func(map_class, tc_map_allocate);
1936
+ tc_core_map_define_methods(map_class);
1937
+
1938
+ mutable_map_class = rb_define_class_under(
1939
+ internal,
1940
+ "MutableTreeMap",
1941
+ rb_cObject
1942
+ );
1943
+ rb_define_alloc_func(mutable_map_class, tc_mutable_map_allocate);
1944
+ tc_core_map_define_methods(mutable_map_class);
1945
+
1946
+ shareable_map_class = rb_define_class_under(
1947
+ internal,
1948
+ "ShareableTreeMap",
1949
+ rb_cObject
1950
+ );
1951
+ rb_define_alloc_func(shareable_map_class, tc_shareable_map_allocate);
1952
+ tc_core_map_define_methods(shareable_map_class);
1953
+ }