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,129 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ require "farce/internal/autoloads"
6
+ require "timeout"
7
+
8
+ experimental = Warning[:experimental]
9
+
10
+ # simplecov:disable
11
+ begin
12
+ Warning[:experimental] = false
13
+ IO::Buffer.new(0) if defined?(IO::Buffer)
14
+ Ractor.new {} if defined?(Ractor) # rubocop:disable Lint/EmptyBlock
15
+ ensure
16
+ Warning[:experimental] = experimental
17
+ end
18
+ # simplecov:enable
19
+
20
+ module Farce
21
+ # @!visibility private
22
+ module Internal # :nodoc: all
23
+ EMPTY_ARRAY = [].freeze
24
+ INTERRUPT_MASK = { Exception => :never }.freeze
25
+ MAP_KEEP = Object.new.freeze
26
+ MAP_DELETE = Object.new.freeze
27
+
28
+ # A non-main Ractor cannot call CONFIG.frozen if the main Ractor didn't do so before,
29
+ # as CONFIG is not shareable yet. We don't want to eagerly freeze it, otherwise a user can't change
30
+ # any configuration options after loading Farce.
31
+ #
32
+ # We can't do so via `Farce.on_main`, as creating the MainScheduler already needs the configuration.
33
+ # So we rely on the fact that autoloads run on the main Ractor.
34
+ autoload :FROZEN_CONFIG, "farce/internal/_frozen_config"
35
+
36
+ include Autoloads
37
+ extend self
38
+
39
+ # @api private
40
+ def marshal_protocol_method?(name) = MarshalSupport.protocol_method?(name)
41
+
42
+ # @api private
43
+ def marshal_shareable?(value) = Ractor.shareable?(value)
44
+
45
+ # Get the value from an options hash with only the `:self` key, and raise an error if there are any other keys.
46
+ def self_option(options, default = nil)
47
+ new_self = options.key?(:self) ? options.delete(:self) : (block_given? ? yield : default)
48
+
49
+ if options.any?
50
+ raise ArgumentError, "unknown keyword: #{options.keys.first.inspect}" if options.size == 1
51
+ raise ArgumentError, "unknown keywords: #{options.keys.map(&:inspect).join(", ")}"
52
+ end
53
+
54
+ new_self
55
+ end
56
+
57
+ def timeout_deadline(timeout)
58
+ return if timeout.nil?
59
+
60
+ timeout = Float(timeout)
61
+ unless timeout.finite? && !timeout.negative?
62
+ raise ArgumentError, "timeout must be a finite, non-negative number or nil"
63
+ end
64
+ Clock.now + timeout
65
+ end
66
+
67
+ def remaining_timeout(deadline)
68
+ return unless deadline
69
+ remaining = deadline - Clock.now
70
+ remaining.negative? ? 0 : remaining
71
+ end
72
+
73
+ # Repeat with one timeout budget. Even a zero timeout permits the first check.
74
+ def with_timeout(timeout)
75
+ raise LocalJumpError, "no block given" unless block_given?
76
+ deadline = timeout_deadline(timeout)
77
+ loop do
78
+ yield remaining_timeout(deadline), deadline
79
+ return if deadline && Clock.now >= deadline
80
+ end
81
+ end
82
+
83
+ def wait_until(receiver, *keys, timeout: nil)
84
+ raise LocalJumpError, "no block given" unless block_given?
85
+ with_timeout(timeout) do |_, deadline|
86
+ current = keys.empty? ? receiver.value : receiver[*keys]
87
+ return current if yield(current)
88
+ receiver.wait_until_changed(*keys, current, timeout: remaining_timeout(deadline)) { return }
89
+ end
90
+ end
91
+
92
+ def garbage_collectable?(object)
93
+ case object
94
+ when Integer, Float, Complex, Rational, Symbol, true, false, nil then false
95
+ else true
96
+ end
97
+ end
98
+
99
+ def walker_constants(object, inherit) = object.constants(inherit)
100
+ def storage_thread(thread) = thread
101
+ def prepare_mutable_numeric(_) = nil
102
+ def prepare_map_access(_klass, _kind) = nil
103
+ def finalize_engine = nil
104
+
105
+ def delegate(from, to, *methods)
106
+ methods.each do |method|
107
+ if to.is_a?(Module)
108
+ name = to.name
109
+ const = Object.const_get(name, false) if name && Object.const_defined?(name, false)
110
+ to = "::#{name}" if const.equal?(to)
111
+ end
112
+
113
+ if to.is_a?(String) || to.is_a?(Symbol)
114
+ from.class_eval <<-RUBY, __FILE__, __LINE__ + 1
115
+ # simplecov:disable
116
+ def #{method}(...)
117
+ #{to}.#{method}(...)
118
+ end
119
+ # simplecov:enable
120
+ RUBY
121
+ else
122
+ from.define_method(method) do |*args, **kwargs, &block|
123
+ to.send(method, *args, **kwargs, &block)
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
data/lib/farce/lazy.rb ADDED
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A ractor-shareable lazy value that is computed on demand.
7
+ # The factory runs once. Its result is transferred according to {#mode}.
8
+ # With the default `:copy` mode, each Ractor receives its own cached copy.
9
+ # The factory and its bound receiver must remain Ractor-shareable.
10
+ # Use {Strict::Lazy} for direct shareable results or {Unshared::Lazy} for local factories.
11
+ #
12
+ # You can use it to make the following, common Ruby idiom ractor-safe:
13
+ #
14
+ # ```ruby
15
+ # class MyClass
16
+ # def expensive_attribute = @expensive_attribute ||= compute_expensive_attribute
17
+ #
18
+ # private
19
+ #
20
+ # def compute_expensive_attribute
21
+ # sleep 0.5 # pretend like we're working
22
+ # 42
23
+ # end
24
+ # end
25
+ #
26
+ # # this will freeze the object without the expensive attribute being computed
27
+ # object = Ractor.make_shareable(MyClass.new)
28
+ #
29
+ # # this will fail
30
+ # Ractor.new(object) { it.expensive_attribute }
31
+ #
32
+ #
33
+ # object = MyClass.new
34
+ #
35
+ # # this will work, but compute the expensive attribute five times, and also deep-copy any other
36
+ # # unshareable data the object might hold
37
+ # 5.times { Ractor.new(object) { it.expensive_attribute } }
38
+ # ```
39
+ #
40
+ # You can use `Farce::Lazy` to make expensive attributes ractor-safe:
41
+ #
42
+ # ```ruby
43
+ # class MyClass
44
+ # include Farce::Shareable
45
+ #
46
+ # def initialize
47
+ # @expensive_attribute = Farce::Lazy.new do
48
+ # sleep 0.5 # pretend like we're working
49
+ # 42
50
+ # end
51
+ # super
52
+ # end
53
+ #
54
+ # def expensive_attribute = @expensive_attribute.value
55
+ # end
56
+ #
57
+ # object = MyClass.new
58
+ #
59
+ # # Will only compute the expensive attribute once, and return the same value to all Ractors.
60
+ # 5.times { Ractor.new(object) { it.expensive_attribute } }
61
+ # ```
62
+ class Lazy < Farce::Abstract::Lazy
63
+ include Shareable::Tracked
64
+
65
+ # @!macro modes
66
+ # @overload initialize(factory, mode: :copy)
67
+ # @param factory [Class, Proc, #call] the shareable factory for the value
68
+ # @param mode [Symbol] the transfer mode for the computed result
69
+ # @overload initialize(mode: :copy, self: nil)
70
+ # @param mode [Symbol] the transfer mode for the computed result
71
+ # @param self [BasicObject] the shareable receiver to bind to the block
72
+ # @yield computes the value on first access
73
+ # @yieldreturn [BasicObject] the result to transfer
74
+ def initialize(factory = nil, mode: :copy, **, &)
75
+ @manager = marshal_mode_manager(mode)
76
+ super(factory, **, &)
77
+ end
78
+
79
+ # @return [Symbol] the transfer mode for the computed result
80
+ def mode = @manager.mode
81
+
82
+ # Compute once and return the result according to the configured transfer mode.
83
+ # Repeated reads return the same value within the current Ractor.
84
+ # @return [BasicObject] the computed result
85
+ def value = @manager.unwrap(super)
86
+
87
+ # Resolve the slot before freezing it. This does not freeze the result.
88
+ # @return [self]
89
+ def freeze
90
+ value
91
+ internal_atom.freeze
92
+ super
93
+ end
94
+
95
+ private
96
+
97
+ def compute_value = @manager.wrap(super)
98
+ def display_value(inspector) = super(inspector, @manager)
99
+ end
100
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # @!parse
7
+ # # A {Reference} for a {Lazy} value.
8
+ # #
9
+ # # Results use the lazy value's transfer mode, which defaults to `:copy`.
10
+ # #
11
+ # # Calling `freeze` resolves and freezes the target. Checking `frozen?` before
12
+ # # the first successful freeze does not resolve it.
13
+ # #
14
+ # # @example
15
+ # # initialized = Farce::Counter.new
16
+ # #
17
+ # # lazy_ref = Farce::LazyRef.new do
18
+ # # initialized.increment
19
+ # # Farce::Map.new
20
+ # # end
21
+ # #
22
+ # # initialized.value # => 0
23
+ # # lazy_ref[:key] = :value
24
+ # # initialized.value # => 1
25
+ # class LazyRef < Farce::Reference
26
+ # # @!macro modes
27
+ # # @overload initialize(factory, mode: :copy)
28
+ # # @param mode [Symbol] the transfer mode for the computed result
29
+ # # @param [Class, Proc, #call] factory The factory to use for creating the value. Must be ractor-shareable.
30
+ # #
31
+ # # @overload initialize(self: nil, mode: :copy)
32
+ # # @param mode [Symbol] the transfer mode for the computed result
33
+ # # @yield The block to use for creating the value. Must be ractor-shareable.
34
+ # # @yieldreceiver [BasicObject] The `self` parameter provided, or `nil` if not provided.
35
+ # # @yieldreturn [BasicObject] The value to be returned by the lazy instance.
36
+ # # @param [BasicObject] self The `self` parameter to be provided to the block. Must be ractor-shareable.
37
+ # def initialize(...) = nil
38
+ # end
39
+ LazyRef = Reference[Lazy]
40
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable lease that moves one resource on checkout and checkin.
7
+ #
8
+ # @example Reusing an object across Ractors
9
+ # lease = Farce::Lease.new { [] }
10
+ # finished = Farce::Port.new
11
+ # Farce::Ractor.new(lease, finished) do |shared, result|
12
+ # size = shared.checkout do |items|
13
+ # items << :processed
14
+ # items.length
15
+ # end
16
+ # result << size
17
+ # end
18
+ #
19
+ # finished.receive # => 1
20
+ # lease.checkout { |items| items.dup } # => [:processed]
21
+ class Lease < Farce::Abstract::Lease
22
+ include Shareable::Unfreezable
23
+
24
+ private
25
+
26
+ def new_internal_lease(resource) = Internal::Lease.new(resource)
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable map that moves each resource on checkout and checkin.
7
+ #
8
+ # @example Updating independently leased resources
9
+ # leases = Farce::LeaseMap.new { { primary: [], replica: [] } }
10
+ # leases.checkout(:primary) { |items| items << :updated }
11
+ # leases.checkout(:primary, &:dup) # => [:updated]
12
+ #
13
+ # @example Automatically checking out resources as they are accessed
14
+ # leases = Farce::LeaseMap.new { { primary: [], replica: [] } }
15
+ #
16
+ # leases.auto_lease do
17
+ # leases[:primary] << :updated
18
+ # leases[:replica] << :replicated
19
+ # leases[:primary] << :verified # Reuses the same checkout
20
+ # end
21
+ #
22
+ # # Both resources are checked back in when the block exits, even on an exception.
23
+ # leases.available?(:primary) # => true
24
+ # leases.checkout(:primary, &:dup) # => [:updated, :verified]
25
+ class LeaseMap < Farce::Abstract::LeaseMap
26
+ include Shareable::Unfreezable
27
+
28
+ private
29
+
30
+ def new_internal_lease_map(mapping)
31
+ Internal::LeaseMap.new(mapping, lease_class: Farce::Lease, registry_class: Farce::Map)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable pool that moves resources on checkout and checkin.
7
+ #
8
+ # @example Reusing an object across Ractors
9
+ # pool = Farce::LeasePool.new(max_size: 2) { [] }
10
+ # result = Farce::Ractor.new(pool) do |shared|
11
+ # shared.checkout do |items|
12
+ # items << :worker
13
+ # items.length
14
+ # end
15
+ # end.value
16
+ #
17
+ # result # => 1
18
+ class LeasePool < Farce::Abstract::LeasePool
19
+ include Shareable::Unfreezable
20
+
21
+ private
22
+
23
+ def prepare_factory(factory)
24
+ Ractor.shareable?(factory) ? factory : Ractor.shareable_proc(&factory)
25
+ end
26
+
27
+ def new_internal_pool(max_size) = Internal::LeasePool.new(max_size)
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable bounded map that evicts the least frequently used entry.
7
+ # Equal-frequency victims are selected by recency. Transfer modes apply to values.
8
+ # Individual reads and writes increment frequency. Observation does not.
9
+ class LFUMap < Farce::Abstract::LFUMap
10
+ include Shareable::Delegated
11
+
12
+ # @!macro modes
13
+ # @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Abstract::Map, #each, nil]
14
+ # Optional initial entries. Entries are stored sequentially and may be evicted.
15
+ # @param mode [Symbol] The default value transfer mode.
16
+ def initialize(entries = nil, mode: :copy, **)
17
+ @manager = marshal_mode_manager(mode)
18
+ super(entries, **)
19
+ end
20
+
21
+ # The default transfer mode for values.
22
+ # @return [Symbol]
23
+ def mode = @manager.mode
24
+
25
+ # Keys must be shareable, except equality-mode strings that can be snapshotted.
26
+ # @return [true]
27
+ def shareable_keys? = true
28
+
29
+ # Values are stored in Ractor-shareable representations.
30
+ # @return [true]
31
+ def shareable_values? = true
32
+
33
+ protected
34
+
35
+ def unwrap_value(value) = @manager.unwrap(value)
36
+
37
+ def wrap_value(value)
38
+ check_frozen!
39
+ @manager.wrap(value)
40
+ end
41
+
42
+ private
43
+
44
+ def each_for_inspect(&) = internal_map.each(&)
45
+ def inspect_value(inspector, value) = super(inspector, value, @manager)
46
+ def new_bounded_map(...) = Internal::StrictLFUMap.new(...)
47
+ def freeze_backend = internal_map
48
+
49
+ def new_key_locks(compare_keys_by_identity:)
50
+ Internal::KeyLockMap.new(registry_class: Farce::Strict::Map, compare_keys_by_identity:)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable atomic reference with an independent mutable value in each scope.
8
+ #
9
+ # @!method initialize(value = nil, compare_by_identity: false, scope: :ractor)
10
+ # @!macro scopes
11
+ # @param value [BasicObject, nil] the initial value
12
+ # @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
13
+ # @param scope [Symbol] the scope of the atomic reference
14
+ # @return [Atom]
15
+ class Atom < Abstract::Atom
16
+ include Shareable::Tracked
17
+ include Scoped::Tracked
18
+
19
+ protected def internal_atom = scoped_value
20
+ private def new_scoped_value(...) = Internal::UnsharedAtom.new(...)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable atomic counter with an independent value in each scope.
8
+ # Each scope starts at the initial integer. Reset affects only the current scope.
9
+ class Counter < Numeric
10
+ include Abstract::Counter
11
+ include Shareable::Tracked
12
+ include Scoped::Tracked
13
+
14
+ # The initial value used for new scopes and by {#reset}.
15
+ # @return [Integer]
16
+ attr_reader :initial
17
+
18
+ # @!macro scopes
19
+ # @param value [Numeric, String, #to_int] the initial value, converted to an Integer
20
+ # @param scope [Symbol] the scope of the counter
21
+ def initialize(value = 0, scope: :ractor)
22
+ raise FrozenError.new("can't initialize a frozen counter", receiver: self) if frozen?
23
+ @initial = Integer(value)
24
+ super(@initial, scope:)
25
+ end
26
+
27
+ # Return the current integer value.
28
+ # @return [Integer]
29
+ def value = internal_counter.value
30
+
31
+ alias get value
32
+
33
+ # Store an integer value.
34
+ # @param value [Integer] the replacement value
35
+ # @return [Integer] the stored value
36
+ def store(value) = internal_counter.store(value)
37
+
38
+ alias value= store
39
+
40
+ # Replace the value and return the previous integer.
41
+ # @param value [Integer] the replacement value
42
+ # @return [Integer]
43
+ def swap(value) = internal_counter.swap(value)
44
+
45
+ # Replace the value atomically if it matches the expected integer.
46
+ # @param expected [Integer] the expected value
47
+ # @param replacement [Integer] the replacement value
48
+ # @return [Boolean] whether the value changed
49
+ def compare_and_set(expected, replacement) = internal_counter.compare_and_set(expected, replacement)
50
+
51
+ # Increment the counter atomically.
52
+ # @param by [Numeric, String, #to_int] the amount, converted to an Integer
53
+ # @return [self]
54
+ def increment(by = 1)
55
+ internal_counter.increment(by)
56
+ self
57
+ end
58
+
59
+ # Decrement the counter atomically.
60
+ # @param by [Numeric, String, #to_int] the amount, converted to an Integer
61
+ # @return [self]
62
+ def decrement(by = 1)
63
+ internal_counter.decrement(by)
64
+ self
65
+ end
66
+
67
+ alias subtract decrement
68
+ alias remove decrement
69
+
70
+ private
71
+
72
+ def marshal_initialize(arguments, options, configuration)
73
+ @initial = Integer(arguments.first)
74
+ super
75
+ end
76
+
77
+ def initialize_copy(other)
78
+ # Numeric#initialize_copy cannot copy mutable Numeric subclasses on JRuby.
79
+ @farce_freeze_state = Internal::Flag.new(false)
80
+ Internal::Storage.scope(scope)[self] = Internal::Counter.new(other.value)
81
+ end
82
+
83
+ def change_signal = internal_counter.change_signal
84
+ def internal_counter = scoped_value
85
+ def new_scoped_value(...) = Internal::Counter.new(...)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable atomic boolean with an independent value in each scope.
8
+ # Only true and false are accepted. Read {#value} to test its state.
9
+ class Flag
10
+ include Abstract::Flag
11
+ include Shareable::Tracked
12
+ include Scoped::Tracked
13
+
14
+ # @!method initialize(value = false, scope: :ractor)
15
+ # @!macro scopes
16
+ # @param value [Boolean] the initial value
17
+ # @param scope [Symbol] the scope of the flag
18
+ # @raise [ArgumentError] if the value is not true or false
19
+
20
+ # @return [Boolean] the current value
21
+ def value = scoped_value.value
22
+
23
+ alias get value
24
+
25
+ # Set the flag to true.
26
+ # @return [true]
27
+ def set = scoped_value.set
28
+
29
+ # Set the flag to false.
30
+ # @return [false]
31
+ def unset = scoped_value.unset
32
+
33
+ # Store a boolean value.
34
+ # @param value [Boolean] the new value
35
+ # @return [Boolean] the new value
36
+ def store(value) = scoped_value.store(value)
37
+
38
+ alias value= store
39
+
40
+ # Replace the value and return its previous state.
41
+ # @param value [Boolean] the new value
42
+ # @return [Boolean] the previous value
43
+ def swap(value) = scoped_value.swap(value)
44
+
45
+ # Replace the value atomically if it matches the expected boolean.
46
+ # @param expected [Boolean] the expected value
47
+ # @param replacement [Boolean] the replacement value
48
+ # @return [Boolean] whether the value changed
49
+ def compare_and_set(expected, replacement) = scoped_value.compare_and_set(expected, replacement)
50
+
51
+ # Atomically invert the value.
52
+ # @return [Boolean] the new value
53
+ def toggle = scoped_value.toggle
54
+
55
+ private
56
+
57
+ def initialize_copy(other)
58
+ super
59
+ Internal::Storage.scope(scope)[self] = Internal::Flag.new(other.value)
60
+ end
61
+
62
+ def new_scoped_value(...) = Internal::Flag.new(...)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable lazy value computed once per scope. Results may be mutable.
8
+ class Lazy < Abstract::Lazy
9
+ include Shareable::Unfreezable
10
+ include Scoped
11
+
12
+ # @!macro scopes
13
+ # @overload initialize(factory, scope: :ractor)
14
+ # @param factory [Class, Proc, #call] a shareable factory for each scope's value
15
+ # @param scope [Symbol] the scope of the lazy value
16
+ # @overload initialize(scope: :ractor, self: nil)
17
+ # @param scope [Symbol] the scope of the lazy value
18
+ # @param self [BasicObject] the shareable receiver to bind to the block
19
+ # @yield computes the value on first access in each scope
20
+ # @yieldreceiver [BasicObject] the self parameter, or nil if omitted
21
+ # @yieldreturn [BasicObject] the initial value for the scope, which may be mutable
22
+ # @return [Lazy]
23
+ def initialize(factory = nil, scope: :ractor, **, &)
24
+ @factory = prepare_factory(factory, **, &)
25
+ @state_key = Object.new.freeze
26
+ super(scope:)
27
+ end
28
+
29
+ private
30
+
31
+ def marshal_current_value = Internal::Storage.scope(scope)[@state_key]&.value
32
+
33
+ def eager_scoped_value? = false
34
+ def internal_atom = Internal::Storage.store_if_absent(@state_key, scope:) { new_scoped_value }
35
+ def new_scoped_value = Internal::UnsharedAtom.new
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # @!parse
8
+ # # A {Reference} for a {Lazy Local::Lazy} value.
9
+ # # Freezing affects the current scope's target. Afterward, `frozen?` reflects
10
+ # # the current target and may initialize it when entering another scope.
11
+ # #
12
+ # # @example
13
+ # # # Delegate to a Ractor-local hash
14
+ # # lazy_ref = Farce::Local::LazyRef.new { {} }
15
+ # #
16
+ # # lazy_ref[:key] = :value
17
+ # # lazy_ref[:key] # => :value
18
+ # #
19
+ # # Ractor.new(lazy_ref) do |lazy_ref|
20
+ # # lazy_ref[:key] # => nil
21
+ # # end
22
+ # class LazyRef < Farce::Reference
23
+ # # @overload initialize(factory, scope: :ractor)
24
+ # # @param [Class, Proc, #call] factory The factory to use for creating the value. Must be ractor-shareable.
25
+ # # @param scope [Symbol] the scope of the lazy value
26
+ # #
27
+ # # @overload initialize(self: nil, scope: :ractor)
28
+ # # @yield The block to use for creating the value. Must be ractor-shareable.
29
+ # # @yieldreceiver [BasicObject] The `self` parameter provided, or `nil` if not provided.
30
+ # # @yieldreturn [BasicObject] The value to be returned by the lazy instance.
31
+ # # @param [BasicObject] self The `self` parameter to be provided to the block. Must be ractor-shareable.
32
+ # # @param scope [Symbol] the scope of the lazy value
33
+ # end
34
+ LazyRef = Reference[Farce::Local::Lazy]
35
+ end
36
+ end