farce 0 → 0.1.0

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 (420) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/CODE_OF_CONDUCT.md +26 -0
  4. data/CONTRIBUTING.md +71 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +1525 -0
  7. data/SECURITY.md +10 -0
  8. data/docs/benchmarks.md +185 -0
  9. data/docs/gems/dry-types.md +290 -0
  10. data/docs/gems/msgpack.md +70 -0
  11. data/docs/gems/ractor-shim.md +63 -0
  12. data/docs/modes.md +628 -0
  13. data/docs/scopes.md +649 -0
  14. data/docs/variants.md +346 -0
  15. data/ext/ext_helper.rb +20 -0
  16. data/ext/farce/README.md +21 -0
  17. data/ext/farce/atom.c +1023 -0
  18. data/ext/farce/bounded_map.c +1683 -0
  19. data/ext/farce/containers.h +77 -0
  20. data/ext/farce/counter.c +399 -0
  21. data/ext/farce/darwin.c +100 -0
  22. data/ext/farce/depend +12 -0
  23. data/ext/farce/dict.c +1523 -0
  24. data/ext/farce/dict.h +152 -0
  25. data/ext/farce/drivers.c +237 -0
  26. data/ext/farce/exchanger.c +299 -0
  27. data/ext/farce/extconf.rb +99 -0
  28. data/ext/farce/farce.c +359 -0
  29. data/ext/farce/flag.c +288 -0
  30. data/ext/farce/io.c +338 -0
  31. data/ext/farce/lock.c +510 -0
  32. data/ext/farce/map.c +2240 -0
  33. data/ext/farce/priority_queue.c +2056 -0
  34. data/ext/farce/queue.c +1059 -0
  35. data/ext/farce/reactor.c +820 -0
  36. data/ext/farce/reactor.h +103 -0
  37. data/ext/farce/shareable.h +31 -0
  38. data/ext/farce/signal.c +350 -0
  39. data/ext/farce/transaction.c +354 -0
  40. data/ext/farce/transaction.h +40 -0
  41. data/ext/farce/tree_map.c +1953 -0
  42. data/ext/farce/trie.c +2020 -0
  43. data/ext/farce/unshareable.c +155 -0
  44. data/ext/farce/unshared_io_pool.h +234 -0
  45. data/ext/farce/unshared_signal.c +263 -0
  46. data/ext/farce/unshared_wait.h +193 -0
  47. data/ext/farce/unsupported.c +6 -0
  48. data/ext/farce/vector.c +1195 -0
  49. data/ext/farce/weak_map.c +1714 -0
  50. data/ext/java/org/farce/BoundedMap.java +394 -0
  51. data/ext/java/org/farce/FiberScheduler.java +141 -0
  52. data/ext/java/org/farce/PriorityKey.java +45 -0
  53. data/ext/java/org/farce/PriorityQueue.java +373 -0
  54. data/ext/java/org/farce/QueueSignal.java +28 -0
  55. data/ext/rebind/README.md +14 -0
  56. data/ext/rebind/extconf.rb +10 -0
  57. data/ext/rebind/rebind.c +186 -0
  58. data/lib/farce/_yard/internal.rb +13 -0
  59. data/lib/farce/_yard/macros.rb +61 -0
  60. data/lib/farce/_yard/ractor.rb +46 -0
  61. data/lib/farce/abstract/atom.rb +186 -0
  62. data/lib/farce/abstract/bounded_map.rb +232 -0
  63. data/lib/farce/abstract/collection.rb +151 -0
  64. data/lib/farce/abstract/concurrent_map.rb +381 -0
  65. data/lib/farce/abstract/counter.rb +193 -0
  66. data/lib/farce/abstract/duplicable_map.rb +229 -0
  67. data/lib/farce/abstract/exchanger.rb +26 -0
  68. data/lib/farce/abstract/flag.rb +71 -0
  69. data/lib/farce/abstract/lazy.rb +115 -0
  70. data/lib/farce/abstract/lease.rb +104 -0
  71. data/lib/farce/abstract/lease_map.rb +261 -0
  72. data/lib/farce/abstract/lease_pool.rb +91 -0
  73. data/lib/farce/abstract/lfu_map.rb +20 -0
  74. data/lib/farce/abstract/lru_map.rb +25 -0
  75. data/lib/farce/abstract/map.rb +345 -0
  76. data/lib/farce/abstract/molecule.rb +245 -0
  77. data/lib/farce/abstract/port.rb +74 -0
  78. data/lib/farce/abstract/priority_queue.rb +117 -0
  79. data/lib/farce/abstract/queue.rb +269 -0
  80. data/lib/farce/abstract/scheduler.rb +111 -0
  81. data/lib/farce/abstract/set.rb +910 -0
  82. data/lib/farce/abstract/sorted_set.rb +172 -0
  83. data/lib/farce/abstract/timer_queue.rb +136 -0
  84. data/lib/farce/abstract/tree_map.rb +269 -0
  85. data/lib/farce/abstract/value.rb +68 -0
  86. data/lib/farce/abstract/vector.rb +1126 -0
  87. data/lib/farce/abstract/weak_atom.rb +32 -0
  88. data/lib/farce/abstract/weak_key_map.rb +12 -0
  89. data/lib/farce/abstract/weak_map.rb +13 -0
  90. data/lib/farce/abstract/weak_set.rb +12 -0
  91. data/lib/farce/abstract/weak_value_map.rb +12 -0
  92. data/lib/farce/abstract.rb +17 -0
  93. data/lib/farce/atom.rb +293 -0
  94. data/lib/farce/class_mirror.rb +87 -0
  95. data/lib/farce/clock.rb +121 -0
  96. data/lib/farce/config.rb +229 -0
  97. data/lib/farce/counter.rb +71 -0
  98. data/lib/farce/deduper.rb +122 -0
  99. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  101. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  102. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  103. data/lib/farce/engine/jruby/map.rb +505 -0
  104. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  105. data/lib/farce/engine/jruby/signal.rb +147 -0
  106. data/lib/farce/engine/jruby.rb +63 -0
  107. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  108. data/lib/farce/engine/jvm/counter.rb +102 -0
  109. data/lib/farce/engine/jvm/extension.rb +32 -0
  110. data/lib/farce/engine/jvm/farce.jar +0 -0
  111. data/lib/farce/engine/jvm/flag.rb +79 -0
  112. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  113. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  114. data/lib/farce/engine/jvm/types.rb +180 -0
  115. data/lib/farce/engine/jvm.rb +19 -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/vault.rb +56 -0
  121. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  122. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  123. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  124. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  125. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  126. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  127. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  128. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  129. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  130. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  131. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  132. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  133. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  134. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  135. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  136. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  137. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  138. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  139. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  140. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  141. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  142. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  143. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  144. data/lib/farce/engine/ruby.rb +130 -0
  145. data/lib/farce/engine/shared/atom.rb +10 -0
  146. data/lib/farce/engine/shared/exchanger.rb +130 -0
  147. data/lib/farce/engine/shared/identity_key.rb +22 -0
  148. data/lib/farce/engine/shared/lease.rb +10 -0
  149. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  150. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  151. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  152. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  153. data/lib/farce/engine/shared/port.rb +44 -0
  154. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  155. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  156. data/lib/farce/engine/shared/queue.rb +233 -0
  157. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  158. data/lib/farce/engine/shared/rebindable.rb +15 -0
  159. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  160. data/lib/farce/engine/shared/strict_map.rb +117 -0
  161. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  162. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  163. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  164. data/lib/farce/engine/shared/trie.rb +317 -0
  165. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  166. data/lib/farce/engine/shared/unshareable.rb +14 -0
  167. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  168. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  169. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  170. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  171. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  172. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  173. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  174. data/lib/farce/engine/shared/vault.rb +31 -0
  175. data/lib/farce/engine/shared/vector.rb +83 -0
  176. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  177. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  178. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  179. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  180. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  181. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  182. data/lib/farce/engine/shared.rb +63 -0
  183. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  184. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  185. data/lib/farce/engine/truffleruby/map.rb +656 -0
  186. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  187. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  188. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  189. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  190. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  191. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  192. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  193. data/lib/farce/engine/truffleruby.rb +148 -0
  194. data/lib/farce/envelope.rb +335 -0
  195. data/lib/farce/error.rb +35 -0
  196. data/lib/farce/exchanger.rb +41 -0
  197. data/lib/farce/flag.rb +27 -0
  198. data/lib/farce/integrations/active_support/blank.rb +68 -0
  199. data/lib/farce/integrations/active_support/clock.rb +17 -0
  200. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  201. data/lib/farce/integrations/active_support/map.rb +175 -0
  202. data/lib/farce/integrations/active_support/set.rb +60 -0
  203. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  204. data/lib/farce/integrations/active_support/vector.rb +231 -0
  205. data/lib/farce/integrations/active_support.rb +9 -0
  206. data/lib/farce/integrations/activesupport.rb +5 -0
  207. data/lib/farce/integrations/bson.rb +108 -0
  208. data/lib/farce/integrations/cbor.rb +51 -0
  209. data/lib/farce/integrations/concurrent.rb +112 -0
  210. data/lib/farce/integrations/dry-types.rb +5 -0
  211. data/lib/farce/integrations/dry_types.rb +694 -0
  212. data/lib/farce/integrations/json.rb +7 -0
  213. data/lib/farce/integrations/msgpack.rb +112 -0
  214. data/lib/farce/integrations/oj.rb +69 -0
  215. data/lib/farce/integrations/psych.rb +191 -0
  216. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  217. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  218. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  219. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  220. data/lib/farce/integrations/shared/to_json.rb +42 -0
  221. data/lib/farce/integrations/sorted_set.rb +11 -0
  222. data/lib/farce/integrations/weakref.rb +38 -0
  223. data/lib/farce/integrations/yajl.rb +10 -0
  224. data/lib/farce/integrations.rb +156 -0
  225. data/lib/farce/internal/_frozen_config.rb +11 -0
  226. data/lib/farce/internal/autoloads.rb +47 -0
  227. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  228. data/lib/farce/internal/converter.rb +106 -0
  229. data/lib/farce/internal/copyable.rb +31 -0
  230. data/lib/farce/internal/delegation.rb +20 -0
  231. data/lib/farce/internal/external_transaction.rb +59 -0
  232. data/lib/farce/internal/fake_ractor.rb +179 -0
  233. data/lib/farce/internal/freeze.rb +118 -0
  234. data/lib/farce/internal/inspect.rb +157 -0
  235. data/lib/farce/internal/key_lock_map.rb +29 -0
  236. data/lib/farce/internal/key_normalizer.rb +289 -0
  237. data/lib/farce/internal/lease_initialization.rb +115 -0
  238. data/lib/farce/internal/lease_map.rb +441 -0
  239. data/lib/farce/internal/lease_pool_state.rb +228 -0
  240. data/lib/farce/internal/lease_state.rb +342 -0
  241. data/lib/farce/internal/lease_waiting.rb +13 -0
  242. data/lib/farce/internal/managed_queue.rb +26 -0
  243. data/lib/farce/internal/map_value_modes.rb +227 -0
  244. data/lib/farce/internal/marshal_support.rb +227 -0
  245. data/lib/farce/internal/mixin.rb +20 -0
  246. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  247. data/lib/farce/internal/noncopyable.rb +17 -0
  248. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  249. data/lib/farce/internal/pool_supervisor.rb +41 -0
  250. data/lib/farce/internal/pool_worker.rb +61 -0
  251. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  252. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  253. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  254. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  255. data/lib/farce/internal/portable_transaction.rb +164 -0
  256. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  257. data/lib/farce/internal/reservation_waiting.rb +24 -0
  258. data/lib/farce/internal/scheduled_task.rb +48 -0
  259. data/lib/farce/internal/scheduler_io.rb +171 -0
  260. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  261. data/lib/farce/internal/select_scheduler.rb +235 -0
  262. data/lib/farce/internal/storage.rb +162 -0
  263. data/lib/farce/internal/strict_lease.rb +30 -0
  264. data/lib/farce/internal/strict_lease_map.rb +20 -0
  265. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  266. data/lib/farce/internal/thread_pool.rb +166 -0
  267. data/lib/farce/internal/transaction_conflict.rb +9 -0
  268. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  269. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  270. data/lib/farce/internal/undefined.rb +22 -0
  271. data/lib/farce/internal/unshared_lease.rb +29 -0
  272. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  273. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  274. data/lib/farce/internal/value_serialization.rb +13 -0
  275. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  276. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  277. data/lib/farce/internal.rb +129 -0
  278. data/lib/farce/lazy.rb +100 -0
  279. data/lib/farce/lazy_ref.rb +40 -0
  280. data/lib/farce/lease.rb +28 -0
  281. data/lib/farce/lease_map.rb +34 -0
  282. data/lib/farce/lease_pool.rb +29 -0
  283. data/lib/farce/lfu_map.rb +53 -0
  284. data/lib/farce/local/atom.rb +23 -0
  285. data/lib/farce/local/counter.rb +88 -0
  286. data/lib/farce/local/flag.rb +65 -0
  287. data/lib/farce/local/lazy.rb +38 -0
  288. data/lib/farce/local/lazy_ref.rb +36 -0
  289. data/lib/farce/local/lease.rb +77 -0
  290. data/lib/farce/local/lease_map.rb +80 -0
  291. data/lib/farce/local/lease_pool.rb +38 -0
  292. data/lib/farce/local/lfu_map.rb +55 -0
  293. data/lib/farce/local/lru_map.rb +62 -0
  294. data/lib/farce/local/map.rb +47 -0
  295. data/lib/farce/local/molecule.rb +45 -0
  296. data/lib/farce/local/priority_queue.rb +53 -0
  297. data/lib/farce/local/queue.rb +32 -0
  298. data/lib/farce/local/scoped.rb +166 -0
  299. data/lib/farce/local/set.rb +18 -0
  300. data/lib/farce/local/sorted_set.rb +50 -0
  301. data/lib/farce/local/timer_queue.rb +41 -0
  302. data/lib/farce/local/tree_map.rb +47 -0
  303. data/lib/farce/local/vector.rb +30 -0
  304. data/lib/farce/local/weak_atom.rb +23 -0
  305. data/lib/farce/local/weak_key_map.rb +39 -0
  306. data/lib/farce/local/weak_map.rb +39 -0
  307. data/lib/farce/local/weak_set.rb +18 -0
  308. data/lib/farce/local/weak_value_map.rb +39 -0
  309. data/lib/farce/local.rb +35 -0
  310. data/lib/farce/lock.rb +54 -0
  311. data/lib/farce/lru_map.rb +63 -0
  312. data/lib/farce/map.rb +58 -0
  313. data/lib/farce/mode_manager.rb +142 -0
  314. data/lib/farce/molecule.rb +60 -0
  315. data/lib/farce/mutable.rb +171 -0
  316. data/lib/farce/pool.rb +332 -0
  317. data/lib/farce/port.rb +134 -0
  318. data/lib/farce/priority_queue.rb +70 -0
  319. data/lib/farce/proxy/register.rb +101 -0
  320. data/lib/farce/proxy/supervisor.rb +137 -0
  321. data/lib/farce/proxy/wrapper.rb +49 -0
  322. data/lib/farce/proxy.rb +291 -0
  323. data/lib/farce/queue.rb +60 -0
  324. data/lib/farce/ractor.rb +269 -0
  325. data/lib/farce/read_write_lock.rb +256 -0
  326. data/lib/farce/reference.rb +158 -0
  327. data/lib/farce/resolv/dns.rb +42 -0
  328. data/lib/farce/resolv.rb +85 -0
  329. data/lib/farce/scheduler.rb +536 -0
  330. data/lib/farce/set.rb +39 -0
  331. data/lib/farce/shareable.rb +150 -0
  332. data/lib/farce/signal.rb +97 -0
  333. data/lib/farce/sorted_set.rb +36 -0
  334. data/lib/farce/strict/atom.rb +34 -0
  335. data/lib/farce/strict/counter.rb +10 -0
  336. data/lib/farce/strict/exchanger.rb +25 -0
  337. data/lib/farce/strict/flag.rb +10 -0
  338. data/lib/farce/strict/lazy.rb +26 -0
  339. data/lib/farce/strict/lazy_ref.rb +26 -0
  340. data/lib/farce/strict/lease.rb +27 -0
  341. data/lib/farce/strict/lease_map.rb +33 -0
  342. data/lib/farce/strict/lease_pool.rb +28 -0
  343. data/lib/farce/strict/lfu_map.rb +27 -0
  344. data/lib/farce/strict/lru_map.rb +28 -0
  345. data/lib/farce/strict/map.rb +54 -0
  346. data/lib/farce/strict/molecule.rb +16 -0
  347. data/lib/farce/strict/port.rb +40 -0
  348. data/lib/farce/strict/priority_queue.rb +20 -0
  349. data/lib/farce/strict/queue.rb +17 -0
  350. data/lib/farce/strict/set.rb +14 -0
  351. data/lib/farce/strict/sorted_set.rb +19 -0
  352. data/lib/farce/strict/timer_queue.rb +13 -0
  353. data/lib/farce/strict/tree_map.rb +27 -0
  354. data/lib/farce/strict/vector.rb +24 -0
  355. data/lib/farce/strict/weak_atom.rb +31 -0
  356. data/lib/farce/strict/weak_key_map.rb +49 -0
  357. data/lib/farce/strict/weak_map.rb +49 -0
  358. data/lib/farce/strict/weak_set.rb +14 -0
  359. data/lib/farce/strict/weak_value_map.rb +48 -0
  360. data/lib/farce/strict.rb +28 -0
  361. data/lib/farce/system.rb +40 -0
  362. data/lib/farce/thread_scheduler.rb +70 -0
  363. data/lib/farce/timer_queue.rb +66 -0
  364. data/lib/farce/transaction/atom.rb +73 -0
  365. data/lib/farce/transaction/map.rb +25 -0
  366. data/lib/farce/transaction/map_operations.rb +143 -0
  367. data/lib/farce/transaction/molecule.rb +90 -0
  368. data/lib/farce/transaction/mutable.rb +61 -0
  369. data/lib/farce/transaction/set.rb +17 -0
  370. data/lib/farce/transaction/set_operations.rb +48 -0
  371. data/lib/farce/transaction/sorted_set.rb +18 -0
  372. data/lib/farce/transaction/tree_map.rb +78 -0
  373. data/lib/farce/transaction/vector.rb +139 -0
  374. data/lib/farce/transaction/wrapper.rb +202 -0
  375. data/lib/farce/transaction.rb +343 -0
  376. data/lib/farce/tree_map.rb +56 -0
  377. data/lib/farce/unsafe/lfu_map.rb +36 -0
  378. data/lib/farce/unsafe/lru_map.rb +36 -0
  379. data/lib/farce/unsafe/tree_map.rb +21 -0
  380. data/lib/farce/unsafe.rb +29 -0
  381. data/lib/farce/unshareable.rb +67 -0
  382. data/lib/farce/unshared/atom.rb +24 -0
  383. data/lib/farce/unshared/counter.rb +10 -0
  384. data/lib/farce/unshared/flag.rb +10 -0
  385. data/lib/farce/unshared/lazy.rb +38 -0
  386. data/lib/farce/unshared/lazy_ref.rb +26 -0
  387. data/lib/farce/unshared/lease.rb +27 -0
  388. data/lib/farce/unshared/lease_map.rb +26 -0
  389. data/lib/farce/unshared/lease_pool.rb +25 -0
  390. data/lib/farce/unshared/lfu_map.rb +20 -0
  391. data/lib/farce/unshared/lru_map.rb +20 -0
  392. data/lib/farce/unshared/map.rb +49 -0
  393. data/lib/farce/unshared/molecule.rb +16 -0
  394. data/lib/farce/unshared/priority_queue.rb +16 -0
  395. data/lib/farce/unshared/queue.rb +27 -0
  396. data/lib/farce/unshared/set.rb +14 -0
  397. data/lib/farce/unshared/sorted_set.rb +28 -0
  398. data/lib/farce/unshared/timer_queue.rb +16 -0
  399. data/lib/farce/unshared/tree_map.rb +21 -0
  400. data/lib/farce/unshared/vector.rb +19 -0
  401. data/lib/farce/unshared/weak_atom.rb +31 -0
  402. data/lib/farce/unshared/weak_key_map.rb +44 -0
  403. data/lib/farce/unshared/weak_map.rb +44 -0
  404. data/lib/farce/unshared/weak_set.rb +14 -0
  405. data/lib/farce/unshared/weak_value_map.rb +43 -0
  406. data/lib/farce/unshared.rb +28 -0
  407. data/lib/farce/vector.rb +228 -0
  408. data/lib/farce/version.rb +8 -0
  409. data/lib/farce/walker/definitions.rb +186 -0
  410. data/lib/farce/walker/modification.rb +355 -0
  411. data/lib/farce/walker.rb +319 -0
  412. data/lib/farce/weak_atom.rb +121 -0
  413. data/lib/farce/weak_key_map.rb +31 -0
  414. data/lib/farce/weak_map.rb +33 -0
  415. data/lib/farce/weak_ref.rb +67 -0
  416. data/lib/farce/weak_set.rb +82 -0
  417. data/lib/farce/weak_value.rb +195 -0
  418. data/lib/farce/weak_value_map.rb +32 -0
  419. data/lib/farce.rb +519 -0
  420. metadata +435 -8
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A priority queue that stores and returns values directly.
8
+ class PriorityQueue < Abstract::PriorityQueue
9
+ include Internal::StrictQueueValues unless Internal.native_ractors?
10
+ include Shareable::Unfreezable
11
+
12
+ def initialize(capacity: nil, default_priority: 0, order: :ascending, track_age: false)
13
+ unless Ractor.shareable?(default_priority)
14
+ raise Ractor::IsolationError, "default priority is not Ractor-shareable"
15
+ end
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A FIFO queue that stores and returns values directly.
8
+ class Queue < Abstract::Queue
9
+ include Shareable::Unfreezable
10
+
11
+ def initialize(capacity: 1024, track_age: false)
12
+ @queue = Internal::Queue.new(capacity:, track_age:)
13
+ super()
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable concurrent set of shareable elements.
8
+ class Set < Farce::Abstract::Set
9
+ include Shareable::Delegated
10
+
11
+ private def new_map(...) = Farce::Strict::Map.new(...)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable concurrent set maintained in ascending order.
8
+ class SortedSet < Farce::Abstract::SortedSet
9
+ include Shareable::Delegated
10
+
11
+ private
12
+
13
+ def new_map(entries = nil, compare_keys_by_identity: false, **)
14
+ raise ArgumentError, "sorted sets do not support identity comparison" if compare_keys_by_identity
15
+ Farce::Strict::TreeMap.new(entries)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A timer queue that stores and returns values directly.
8
+ class TimerQueue < Abstract::TimerQueue
9
+ include Internal::StrictQueueValues unless Internal.native_ractors?
10
+ include Shareable::Unfreezable
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable map that keeps shareable entries sorted by key.
8
+ # Mutable string keys become immutable. Other keys and all values must already be Ractor-shareable.
9
+ #
10
+ # @example Keeping direct values in key order
11
+ # map = Farce::Strict::TreeMap.new(2 => "two".freeze, 1 => "one".freeze)
12
+ #
13
+ # map.to_a # => [[1, "one"], [2, "two"]]
14
+ class TreeMap < Abstract::TreeMap
15
+ include Shareable::Delegated
16
+
17
+ # Values must already be Ractor-shareable.
18
+ # @return [true]
19
+ def shareable_values? = true
20
+
21
+ private
22
+
23
+ def new_tree_map(...) = Internal::StrictTreeMap.new(...)
24
+ def freeze_backend = internal_map
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable vector that stores and returns shareable values directly.
8
+ class Vector < Abstract::Vector
9
+ include Shareable::Delegated
10
+
11
+ # @param source [Array, nil] Initial values. The source array is not retained.
12
+ # @param compare_by_identity [Boolean] Whether values are compared by identity.
13
+ def initialize(source = nil, compare_by_identity: false)
14
+ @vector = Internal::Vector.new(source, compare_by_identity:)
15
+ super()
16
+ end
17
+
18
+ # (see Farce::Abstract::Vector#shareable_values?)
19
+ def shareable_values? = true
20
+
21
+ private def freeze_backend = @vector
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable weak atomic reference that stores shareable values directly.
8
+ #
9
+ # @example Referencing a shareable object without keeping it alive
10
+ # resource = Object.new.freeze
11
+ # atom = Farce::Strict::WeakAtom.new(resource, compare_by_identity: true)
12
+ # atom.value.equal?(resource) # => true
13
+ #
14
+ # resource = nil
15
+ # # Once the object is collected, atom.value returns nil.
16
+ #
17
+ # @example Lazily storing a shareable value
18
+ # atom = Farce::Strict::WeakAtom.new
19
+ # value = atom.store_if_absent { [1, 2, 3].freeze }
20
+ # atom.value.equal?(value) # => true
21
+ # # Keeping value referenced also keeps the atom's referent alive.
22
+ class WeakAtom < Abstract::WeakAtom
23
+ include Shareable::Delegated
24
+
25
+ private
26
+
27
+ def internal_atom_class = Internal::WeakAtom
28
+ def freeze_backend = @atom
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable concurrent map with weak shareable keys and strong shareable values.
8
+ #
9
+ # @example Associating metadata without retaining its owner
10
+ # owner = Object.new.freeze
11
+ # map = Farce::Strict::WeakKeyMap.new
12
+ # map[owner] = :active
13
+ # map[owner] # => :active
14
+ #
15
+ # # The entry can disappear once its key is collected.
16
+ # owner = nil
17
+ class WeakKeyMap < Abstract::WeakKeyMap
18
+ include Shareable::Delegated
19
+
20
+ def initialize(
21
+ initial_mapping = nil,
22
+ normalize_keys: nil,
23
+ compare_by_identity: false,
24
+ compare_keys_by_identity: compare_by_identity,
25
+ compare_values_by_identity: compare_by_identity
26
+ )
27
+ initial_mapping = convert_entries(initial_mapping)
28
+ restoring = Internal::KeyNormalizer.restoration?(normalize_keys)
29
+ normalizer = Internal::KeyNormalizer.build(normalize_keys, shareable: true)
30
+ populate = initial_mapping && ((normalizer && !restoring) || !initial_mapping.is_a?(Hash))
31
+ @map = Internal::StrictWeakKeyMap.new(
32
+ populate ? nil : initial_mapping,
33
+ compare_by_identity:,
34
+ compare_keys_by_identity:,
35
+ compare_values_by_identity:,
36
+ )
37
+ Internal::KeyNormalizer.install_concurrent(self, normalizer) unless restoring
38
+ initial_mapping.each { |key, value| self[key] = value } if populate
39
+ Internal::KeyNormalizer.install_concurrent(self, normalizer) if restoring
40
+ super()
41
+ end
42
+
43
+ def shareable_keys? = true
44
+ def shareable_values? = true
45
+
46
+ private def freeze_backend = internal_map
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable concurrent map that retains shareable keys and values weakly.
8
+ #
9
+ # @example Keeping a relationship while both objects remain alive
10
+ # key = Object.new.freeze
11
+ # value = Object.new.freeze
12
+ # map = Farce::Strict::WeakMap.new({ key => value })
13
+ # map[key].equal?(value) # => true
14
+ #
15
+ # # The entry can disappear once its key or value is collected.
16
+ # value = nil
17
+ class WeakMap < Abstract::WeakMap
18
+ include Shareable::Delegated
19
+
20
+ def initialize(
21
+ initial_mapping = nil,
22
+ normalize_keys: nil,
23
+ compare_by_identity: false,
24
+ compare_keys_by_identity: compare_by_identity,
25
+ compare_values_by_identity: compare_by_identity
26
+ )
27
+ initial_mapping = convert_entries(initial_mapping)
28
+ restoring = Internal::KeyNormalizer.restoration?(normalize_keys)
29
+ normalizer = Internal::KeyNormalizer.build(normalize_keys, shareable: true)
30
+ populate = initial_mapping && ((normalizer && !restoring) || !initial_mapping.is_a?(Hash))
31
+ @map = Internal::StrictWeakMap.new(
32
+ populate ? nil : initial_mapping,
33
+ compare_by_identity:,
34
+ compare_keys_by_identity:,
35
+ compare_values_by_identity:,
36
+ )
37
+ Internal::KeyNormalizer.install_concurrent(self, normalizer) unless restoring
38
+ initial_mapping.each { |key, value| self[key] = value } if populate
39
+ Internal::KeyNormalizer.install_concurrent(self, normalizer) if restoring
40
+ super()
41
+ end
42
+
43
+ def shareable_keys? = true
44
+ def shareable_values? = true
45
+
46
+ private def freeze_backend = internal_map
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable concurrent set that retains shareable elements weakly.
8
+ class WeakSet < Farce::Abstract::WeakSet
9
+ include Shareable::Delegated
10
+
11
+ private def new_map(...) = Farce::Strict::WeakMap.new(...)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Strict
7
+ # A Ractor-shareable concurrent map with strong shareable keys and weak shareable values.
8
+ #
9
+ # @example Caching a value without keeping it alive
10
+ # value = Object.new.freeze
11
+ # map = Farce::Strict::WeakValueMap.new({ result: value })
12
+ # map[:result].equal?(value) # => true
13
+ #
14
+ # # The entry can disappear once its value is collected.
15
+ # value = nil
16
+ class WeakValueMap < Abstract::WeakValueMap
17
+ include Shareable::Delegated
18
+
19
+ def initialize(
20
+ initial_mapping = nil,
21
+ normalize_keys: nil,
22
+ compare_by_identity: false,
23
+ compare_keys_by_identity: compare_by_identity,
24
+ compare_values_by_identity: compare_by_identity
25
+ )
26
+ initial_mapping = convert_entries(initial_mapping)
27
+ restoring = Internal::KeyNormalizer.restoration?(normalize_keys)
28
+ normalizer = Internal::KeyNormalizer.build(normalize_keys, shareable: true)
29
+ populate = initial_mapping && ((normalizer && !restoring) || !initial_mapping.is_a?(Hash))
30
+ @map = Internal::StrictWeakValueMap.new(
31
+ populate ? nil : initial_mapping,
32
+ compare_by_identity:,
33
+ compare_keys_by_identity:,
34
+ compare_values_by_identity:,
35
+ )
36
+ Internal::KeyNormalizer.install_concurrent(self, normalizer) unless restoring
37
+ initial_mapping.each { |key, value| self[key] = value } if populate
38
+ Internal::KeyNormalizer.install_concurrent(self, normalizer) if restoring
39
+ super()
40
+ end
41
+
42
+ def shareable_keys? = true
43
+ def shareable_values? = true
44
+
45
+ private def freeze_backend = internal_map
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # Shareable containers that store shareable values directly.
7
+ module Strict
8
+ include Internal::Autoloads
9
+
10
+ # @overload enfarce(object, freeze: nil)
11
+ # Converts vanilla Ruby objects into their {Farce::Strict} equivalents.
12
+ #
13
+ # @example
14
+ # Farce::Strict.enfarce({ foo: ["bar"] }) { Ractor.make_shareable(it) }
15
+ # # => #<Farce::Strict::Map {foo: #<Farce::Strict::Vector ["bar"]>}>
16
+ #
17
+ # @yield [object] Optional block, called with any object that doesn't have a specific conversion defined.
18
+ # @yieldparam object [BasicObject] the object to convert
19
+ # @yieldreturn [BasicObject] the converted object
20
+ # @param object [BasicObject] The root object to convert
21
+ # @param freeze [Boolean, nil]
22
+ # Whether to freeze the converted objects.
23
+ # If set to `nil` (default), the freezing behavior will depend on the original object's frozen state.
24
+ # @return [BasicObject] the converted object
25
+ # @see Farce.enfarce
26
+ def self.enfarce(object, **, &) = Internal::Converter.new(self, **, &).convert(object)
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ require "etc"
6
+
7
+ module Farce
8
+ # Information about the system running Farce.
9
+ module System
10
+ WINDOWS = Gem.win_platform?
11
+ private_constant :WINDOWS
12
+
13
+ # Whether the current operating system is Windows.
14
+ # Safe to call from any Ractor.
15
+ # @return [Boolean]
16
+ def self.windows? = WINDOWS
17
+
18
+ # Number of logical CPUs, optionally limited to a CPU type.
19
+ # Returns nil when the platform cannot report the requested CPU type.
20
+ # Safe to call from any Ractor.
21
+ # @param type [nil, Symbol] nil for all CPUs, :performance, or :efficiency
22
+ # @return [Integer, nil]
23
+ # @raise [ArgumentError] if the CPU type is invalid
24
+ def self.cpu_count(type = nil)
25
+ case type
26
+ when nil
27
+ if defined?(Internal::Darwin) && Internal::Darwin.respond_to?(:cpu_count)
28
+ Internal::Darwin.cpu_count
29
+ else
30
+ Etc.nprocessors
31
+ end
32
+ when :performance, :efficiency
33
+ method = :"#{type}_cpu_count"
34
+ Internal::Darwin.public_send(method) if defined?(Internal::Darwin) && Internal::Darwin.respond_to?(method)
35
+ else
36
+ raise ArgumentError, "invalid CPU type: #{type.inspect}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A scheduler that starts a new thread for each scheduled task. Executes synchronous tasks inline.
7
+ # Creating an instance starts no threads. Blocks and arguments stay local to the caller.
8
+ # Tasks are not tracked, and closing this stateless scheduler has no effect.
9
+ # Default instances support Marshal. Instances with a custom thread factory reject dumping.
10
+ class ThreadScheduler < Farce::Abstract::Scheduler
11
+ include Shareable::Immutable
12
+ include Internal::Inspect
13
+
14
+ def initialize(&factory)
15
+ @factory = factory
16
+ super()
17
+ end
18
+
19
+ # @api private
20
+ def marshal_dump
21
+ raise TypeError, "scheduler factories cannot be marshaled" if @factory
22
+ [1]
23
+ end
24
+
25
+ # @api private
26
+ def marshal_load(data)
27
+ Internal::MarshalSupport.payload(data, 0)
28
+ initialize
29
+ end
30
+
31
+ # Starts a thread in the caller's Ractor, preserving the block and arguments.
32
+ #
33
+ # @overload schedule(*args, **options)
34
+ # @param args [Array<Object>] Arguments passed to the block.
35
+ # @param options [Hash] Scheduler options are ignored. All tasks use local arguments.
36
+ # @yield [*args] The task to run.
37
+ # @return [self]
38
+ def schedule(*, **, &)
39
+ raise LocalJumpError, "Cannot schedule without a block" unless block_given?
40
+ @factory ? @factory.call(*, &) : Thread.new(*, &)
41
+ self
42
+ end
43
+
44
+ # Runs the block on the caller's thread and fiber. Exceptions propagate to the caller.
45
+ #
46
+ # @overload execute(*args, **options)
47
+ # @param args [Array<Object>] Arguments passed to the block.
48
+ # @param options [Hash] Scheduler options are ignored. All tasks use local arguments.
49
+ # @yield [*args] The task to run.
50
+ # @return [self]
51
+ def execute(*, **)
52
+ raise LocalJumpError, "Cannot yield without a block" unless block_given?
53
+ yield(*)
54
+ self
55
+ end
56
+
57
+ # @return [self] Does not wait for or stop task threads.
58
+ def close = self
59
+
60
+ # @return [false] This stateless scheduler is always available.
61
+ def closed? = false
62
+
63
+ # @return [Symbol] Always `:running`.
64
+ def state = :running
65
+
66
+ # @param wait [Boolean] Ignored because tasks always run in the caller's Ractor.
67
+ # @return [true]
68
+ def local?(wait: true) = true # rubocop:disable Lint/UnusedMethodArgument
69
+ end
70
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A shareable timer queue with transfer modes for unshareable values.
7
+ class TimerQueue < Abstract::TimerQueue
8
+ include Internal::ManagedQueue
9
+ include Shareable::Unfreezable
10
+
11
+ # (see Farce::Abstract::TimerQueue#initialize)
12
+ # @!macro modes
13
+ # @param mode [Symbol] the transfer mode for unshareable values
14
+ def initialize(capacity: nil, mode: :copy, track_age: false)
15
+ @manager = ModeManager.new(mode:)
16
+ super(capacity:, track_age:)
17
+ end
18
+
19
+ # (see Farce::Abstract::TimerQueue#push)
20
+ # @!macro modes
21
+ # @param mode [Symbol] the transfer mode for unshareable values
22
+ def push(value, non_block = false, mode: nil, timeout: nil, **time_options) # rubocop:disable Style/OptionalBooleanParameter
23
+ at = Clock.parse(time_options)
24
+ push_to_storage(at, non_block, @manager.wrap(value, mode:), timeout:)
25
+ end
26
+
27
+ # (see Farce::Abstract::TimerQueue#try_push)
28
+ # @!macro modes
29
+ # @param mode [Symbol] the transfer mode for unshareable values
30
+ def try_push(value, mode: nil, **time_options)
31
+ at = Clock.parse(time_options)
32
+ return true if @queue.push(at, @manager.wrap(value, mode:))
33
+ block_given? ? yield : false
34
+ end
35
+
36
+ # (see Farce::Abstract::TimerQueue#pop)
37
+ def pop(non_block = false, timeout: nil) # rubocop:disable Style/OptionalBooleanParameter
38
+ return try_pop { raise ThreadError, "queue empty" } if non_block
39
+ deadline = timeout_at(timeout) unless timeout.nil?
40
+
41
+ while true
42
+ empty = false
43
+ value = @queue.pop_before(Clock.now) { empty = true }
44
+ return @manager.unwrap(value) unless empty
45
+ return block_given? ? yield : nil if UNDEFINED.equal?(wait_for_timestamp(deadline))
46
+ end
47
+ end
48
+
49
+ # (see Farce::Abstract::TimerQueue#try_pop)
50
+ def try_pop
51
+ empty = false
52
+ value = @queue.pop_before(Clock.now) { empty = true }
53
+ return @manager.unwrap(value) unless empty
54
+ yield if block_given?
55
+ end
56
+
57
+ # (see Farce::Abstract::TimerQueue#peek)
58
+ # @note Peeking at a moved value claims it for this Ractor while leaving it queued.
59
+ def peek
60
+ empty = false
61
+ value = @queue.peek { empty = true }
62
+ return @manager.unwrap(value) unless empty
63
+ yield if block_given?
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ class Transaction
7
+ # An {Abstract::Atom Atom} that is part of a transaction.
8
+ class Atom < Abstract::Atom
9
+ include Wrapper
10
+
11
+ # (see Abstract::Atom#compare_by_identity?)
12
+ # @see Abstract::Atom#compare_by_identity?
13
+ def compare_by_identity? = access { @object.compare_by_identity? }
14
+
15
+ # (see Abstract::Atom#value)
16
+ # @see Abstract::Atom#value
17
+ def value = access { unwrap_stored(@working.value) }
18
+ alias get value
19
+
20
+ # (see Abstract::Atom#value=)
21
+ # @see Abstract::Atom#value=
22
+ def value=(value)
23
+ store(value)
24
+ end
25
+
26
+ # (see Abstract::Atom#store)
27
+ # @see Abstract::Atom#store
28
+ def store(value, mode: nil)
29
+ write { unwrap_stored(@working.store(wrap(value, mode:))) }
30
+ end
31
+
32
+ # (see Abstract::Atom#swap)
33
+ # @see Abstract::Atom#swap
34
+ def swap(value, mode: nil)
35
+ write { unwrap_stored(@working.swap(wrap(value, mode:))) }
36
+ end
37
+
38
+ # (see Abstract::Atom#update)
39
+ # @see Abstract::Atom#update
40
+ def update(mode: nil)
41
+ require_block!(block_given?)
42
+ write { unwrap_stored(@working.store(wrap(yield(unwrap_stored(@working.value)), mode:))) }
43
+ end
44
+
45
+ # (see Abstract::Atom#store_if_absent)
46
+ # @see Abstract::Atom#store_if_absent
47
+ def store_if_absent(mode: nil)
48
+ require_block!(block_given?)
49
+ write { value.nil? ? store(yield, mode:) : value }
50
+ end
51
+
52
+ # (see Abstract::Atom#upsert)
53
+ # @see Abstract::Atom#upsert
54
+ def upsert(initial, mode: nil)
55
+ require_block!(block_given?)
56
+ write { store(value.nil? ? initial : yield(value), mode:) }
57
+ end
58
+
59
+ # (see Abstract::Atom#compare_and_set)
60
+ # @see Abstract::Atom#compare_and_set
61
+ def compare_and_set(expected, replacement, mode: nil)
62
+ write do
63
+ next compared(@working.compare_and_set(expected, replacement)) unless @manager
64
+ next compared(false) unless matches?(@working.value, expected, identity: @object.compare_by_identity?)
65
+ @working.store(wrap(replacement, mode:))
66
+ true
67
+ end
68
+ end
69
+
70
+ Wrapper.inherit(self, :compare_by_identity?, :unwrap)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ class Transaction
7
+ # A {Abstract::ConcurrentMap ConcurrentMap} that is part of a transaction.
8
+ #
9
+ # Reads and writes validate the keys they access, allowing concurrent changes
10
+ # to other keys. Size reads validate cardinality without observing contents.
11
+ # Enumeration, keys, getkey, and clear capture and validate the full map.
12
+ # Staged changes are visible through this wrapper and publish only on commit.
13
+ # Key validation compares current values by identity, not per-key write history.
14
+ # Commit captures a full snapshot for maps accessed by key. Changes after
15
+ # that snapshot is captured can still cause the attempt to fail.
16
+ class Map < Abstract::ConcurrentMap
17
+ include MapOperations
18
+
19
+ Wrapper.inherit(self, *MapOperations::READ_HELPERS, :delete_if, :reject!, :compact!, :keep_if,
20
+ :select!, :filter!, :transform_values!, :merge!)
21
+
22
+ private def enlist(backend) = @transaction.enlist(backend, per_key: true)
23
+ end
24
+ end
25
+ end