farce 0.0.1.alpha2-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +26 -0
  3. data/CONTRIBUTING.md +71 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +1524 -0
  6. data/SECURITY.md +10 -0
  7. data/docs/benchmarks.md +185 -0
  8. data/docs/gems/dry-types.md +290 -0
  9. data/docs/gems/msgpack.md +70 -0
  10. data/docs/gems/ractor-shim.md +63 -0
  11. data/docs/modes.md +628 -0
  12. data/docs/scopes.md +649 -0
  13. data/docs/variants.md +346 -0
  14. data/ext/ext_helper.rb +20 -0
  15. data/ext/farce/README.md +21 -0
  16. data/ext/farce/atom.c +1023 -0
  17. data/ext/farce/bounded_map.c +1683 -0
  18. data/ext/farce/containers.h +77 -0
  19. data/ext/farce/counter.c +399 -0
  20. data/ext/farce/darwin.c +100 -0
  21. data/ext/farce/depend +12 -0
  22. data/ext/farce/dict.c +1523 -0
  23. data/ext/farce/dict.h +152 -0
  24. data/ext/farce/drivers.c +237 -0
  25. data/ext/farce/exchanger.c +299 -0
  26. data/ext/farce/extconf.rb +99 -0
  27. data/ext/farce/farce.c +359 -0
  28. data/ext/farce/flag.c +288 -0
  29. data/ext/farce/io.c +338 -0
  30. data/ext/farce/lock.c +510 -0
  31. data/ext/farce/map.c +2240 -0
  32. data/ext/farce/priority_queue.c +2056 -0
  33. data/ext/farce/queue.c +1059 -0
  34. data/ext/farce/reactor.c +820 -0
  35. data/ext/farce/reactor.h +103 -0
  36. data/ext/farce/shareable.h +31 -0
  37. data/ext/farce/signal.c +350 -0
  38. data/ext/farce/transaction.c +354 -0
  39. data/ext/farce/transaction.h +40 -0
  40. data/ext/farce/tree_map.c +1953 -0
  41. data/ext/farce/trie.c +2020 -0
  42. data/ext/farce/unshareable.c +155 -0
  43. data/ext/farce/unshared_io_pool.h +234 -0
  44. data/ext/farce/unshared_signal.c +263 -0
  45. data/ext/farce/unshared_wait.h +193 -0
  46. data/ext/farce/unsupported.c +6 -0
  47. data/ext/farce/vector.c +1195 -0
  48. data/ext/farce/weak_map.c +1714 -0
  49. data/ext/java/org/farce/BoundedMap.java +394 -0
  50. data/ext/java/org/farce/FiberScheduler.java +141 -0
  51. data/ext/java/org/farce/PriorityKey.java +45 -0
  52. data/ext/java/org/farce/PriorityQueue.java +373 -0
  53. data/ext/java/org/farce/QueueSignal.java +28 -0
  54. data/ext/rebind/README.md +14 -0
  55. data/ext/rebind/extconf.rb +10 -0
  56. data/ext/rebind/rebind.c +186 -0
  57. data/lib/farce/_yard/internal.rb +13 -0
  58. data/lib/farce/_yard/macros.rb +61 -0
  59. data/lib/farce/_yard/ractor.rb +46 -0
  60. data/lib/farce/abstract/atom.rb +186 -0
  61. data/lib/farce/abstract/bounded_map.rb +232 -0
  62. data/lib/farce/abstract/collection.rb +151 -0
  63. data/lib/farce/abstract/concurrent_map.rb +381 -0
  64. data/lib/farce/abstract/counter.rb +193 -0
  65. data/lib/farce/abstract/duplicable_map.rb +229 -0
  66. data/lib/farce/abstract/exchanger.rb +26 -0
  67. data/lib/farce/abstract/flag.rb +71 -0
  68. data/lib/farce/abstract/lazy.rb +115 -0
  69. data/lib/farce/abstract/lease.rb +104 -0
  70. data/lib/farce/abstract/lease_map.rb +261 -0
  71. data/lib/farce/abstract/lease_pool.rb +91 -0
  72. data/lib/farce/abstract/lfu_map.rb +20 -0
  73. data/lib/farce/abstract/lru_map.rb +25 -0
  74. data/lib/farce/abstract/map.rb +345 -0
  75. data/lib/farce/abstract/molecule.rb +245 -0
  76. data/lib/farce/abstract/port.rb +74 -0
  77. data/lib/farce/abstract/priority_queue.rb +117 -0
  78. data/lib/farce/abstract/queue.rb +269 -0
  79. data/lib/farce/abstract/scheduler.rb +111 -0
  80. data/lib/farce/abstract/set.rb +910 -0
  81. data/lib/farce/abstract/sorted_set.rb +172 -0
  82. data/lib/farce/abstract/timer_queue.rb +136 -0
  83. data/lib/farce/abstract/tree_map.rb +269 -0
  84. data/lib/farce/abstract/value.rb +68 -0
  85. data/lib/farce/abstract/vector.rb +1126 -0
  86. data/lib/farce/abstract/weak_atom.rb +32 -0
  87. data/lib/farce/abstract/weak_key_map.rb +12 -0
  88. data/lib/farce/abstract/weak_map.rb +13 -0
  89. data/lib/farce/abstract/weak_set.rb +12 -0
  90. data/lib/farce/abstract/weak_value_map.rb +12 -0
  91. data/lib/farce/abstract.rb +17 -0
  92. data/lib/farce/atom.rb +293 -0
  93. data/lib/farce/class_mirror.rb +87 -0
  94. data/lib/farce/clock.rb +121 -0
  95. data/lib/farce/config.rb +229 -0
  96. data/lib/farce/counter.rb +71 -0
  97. data/lib/farce/deduper.rb +122 -0
  98. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  99. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  101. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  102. data/lib/farce/engine/jruby/map.rb +505 -0
  103. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  104. data/lib/farce/engine/jruby/signal.rb +147 -0
  105. data/lib/farce/engine/jruby.rb +63 -0
  106. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  107. data/lib/farce/engine/jvm/counter.rb +102 -0
  108. data/lib/farce/engine/jvm/extension.rb +32 -0
  109. data/lib/farce/engine/jvm/farce.jar +0 -0
  110. data/lib/farce/engine/jvm/flag.rb +79 -0
  111. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  112. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  113. data/lib/farce/engine/jvm/types.rb +180 -0
  114. data/lib/farce/engine/jvm.rb +19 -0
  115. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  116. data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
  117. data/lib/farce/engine/ruby/3.4/port.rb +186 -0
  118. data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
  119. data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
  120. data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
  121. data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
  122. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  123. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  124. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  125. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  126. data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
  127. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  128. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  129. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  130. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  131. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  132. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  133. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  134. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  135. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  136. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  137. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  138. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  139. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  140. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  141. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  142. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  143. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  144. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  145. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  146. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  147. data/lib/farce/engine/ruby.rb +130 -0
  148. data/lib/farce/engine/shared/atom.rb +10 -0
  149. data/lib/farce/engine/shared/exchanger.rb +130 -0
  150. data/lib/farce/engine/shared/identity_key.rb +22 -0
  151. data/lib/farce/engine/shared/lease.rb +10 -0
  152. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  153. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  154. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  155. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  156. data/lib/farce/engine/shared/port.rb +44 -0
  157. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  158. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  159. data/lib/farce/engine/shared/queue.rb +233 -0
  160. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  161. data/lib/farce/engine/shared/rebindable.rb +15 -0
  162. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  163. data/lib/farce/engine/shared/strict_map.rb +117 -0
  164. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  165. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  166. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  167. data/lib/farce/engine/shared/trie.rb +317 -0
  168. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  169. data/lib/farce/engine/shared/unshareable.rb +14 -0
  170. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  171. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  172. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  173. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  174. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  175. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  176. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  177. data/lib/farce/engine/shared/vault.rb +31 -0
  178. data/lib/farce/engine/shared/vector.rb +83 -0
  179. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  180. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  181. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  182. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  183. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  184. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  185. data/lib/farce/engine/shared.rb +63 -0
  186. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  187. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  188. data/lib/farce/engine/truffleruby/map.rb +656 -0
  189. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  190. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  191. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  192. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  193. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  194. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  195. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  196. data/lib/farce/engine/truffleruby.rb +148 -0
  197. data/lib/farce/envelope.rb +335 -0
  198. data/lib/farce/error.rb +35 -0
  199. data/lib/farce/exchanger.rb +41 -0
  200. data/lib/farce/flag.rb +27 -0
  201. data/lib/farce/integrations/active_support/blank.rb +68 -0
  202. data/lib/farce/integrations/active_support/clock.rb +17 -0
  203. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  204. data/lib/farce/integrations/active_support/map.rb +175 -0
  205. data/lib/farce/integrations/active_support/set.rb +60 -0
  206. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  207. data/lib/farce/integrations/active_support/vector.rb +231 -0
  208. data/lib/farce/integrations/active_support.rb +9 -0
  209. data/lib/farce/integrations/activesupport.rb +5 -0
  210. data/lib/farce/integrations/bson.rb +108 -0
  211. data/lib/farce/integrations/cbor.rb +51 -0
  212. data/lib/farce/integrations/concurrent.rb +112 -0
  213. data/lib/farce/integrations/dry-types.rb +5 -0
  214. data/lib/farce/integrations/dry_types.rb +694 -0
  215. data/lib/farce/integrations/json.rb +7 -0
  216. data/lib/farce/integrations/msgpack.rb +112 -0
  217. data/lib/farce/integrations/oj.rb +69 -0
  218. data/lib/farce/integrations/psych.rb +191 -0
  219. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  220. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  221. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  222. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  223. data/lib/farce/integrations/shared/to_json.rb +42 -0
  224. data/lib/farce/integrations/sorted_set.rb +11 -0
  225. data/lib/farce/integrations/weakref.rb +38 -0
  226. data/lib/farce/integrations/yajl.rb +10 -0
  227. data/lib/farce/integrations.rb +156 -0
  228. data/lib/farce/internal/_frozen_config.rb +11 -0
  229. data/lib/farce/internal/autoloads.rb +47 -0
  230. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  231. data/lib/farce/internal/converter.rb +106 -0
  232. data/lib/farce/internal/copyable.rb +31 -0
  233. data/lib/farce/internal/delegation.rb +20 -0
  234. data/lib/farce/internal/external_transaction.rb +59 -0
  235. data/lib/farce/internal/fake_ractor.rb +179 -0
  236. data/lib/farce/internal/freeze.rb +118 -0
  237. data/lib/farce/internal/inspect.rb +157 -0
  238. data/lib/farce/internal/key_lock_map.rb +29 -0
  239. data/lib/farce/internal/key_normalizer.rb +289 -0
  240. data/lib/farce/internal/lease_initialization.rb +115 -0
  241. data/lib/farce/internal/lease_map.rb +441 -0
  242. data/lib/farce/internal/lease_pool_state.rb +228 -0
  243. data/lib/farce/internal/lease_state.rb +342 -0
  244. data/lib/farce/internal/lease_waiting.rb +13 -0
  245. data/lib/farce/internal/managed_queue.rb +26 -0
  246. data/lib/farce/internal/map_value_modes.rb +227 -0
  247. data/lib/farce/internal/marshal_support.rb +227 -0
  248. data/lib/farce/internal/mixin.rb +20 -0
  249. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  250. data/lib/farce/internal/noncopyable.rb +17 -0
  251. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  252. data/lib/farce/internal/pool_supervisor.rb +41 -0
  253. data/lib/farce/internal/pool_worker.rb +61 -0
  254. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  255. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  256. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  257. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  258. data/lib/farce/internal/portable_transaction.rb +164 -0
  259. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  260. data/lib/farce/internal/reservation_waiting.rb +24 -0
  261. data/lib/farce/internal/scheduled_task.rb +48 -0
  262. data/lib/farce/internal/scheduler_io.rb +171 -0
  263. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  264. data/lib/farce/internal/select_scheduler.rb +235 -0
  265. data/lib/farce/internal/storage.rb +162 -0
  266. data/lib/farce/internal/strict_lease.rb +30 -0
  267. data/lib/farce/internal/strict_lease_map.rb +20 -0
  268. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  269. data/lib/farce/internal/thread_pool.rb +166 -0
  270. data/lib/farce/internal/transaction_conflict.rb +9 -0
  271. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  272. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  273. data/lib/farce/internal/undefined.rb +22 -0
  274. data/lib/farce/internal/unshared_lease.rb +29 -0
  275. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  276. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  277. data/lib/farce/internal/value_serialization.rb +13 -0
  278. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  279. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  280. data/lib/farce/internal.rb +129 -0
  281. data/lib/farce/lazy.rb +100 -0
  282. data/lib/farce/lazy_ref.rb +40 -0
  283. data/lib/farce/lease.rb +28 -0
  284. data/lib/farce/lease_map.rb +34 -0
  285. data/lib/farce/lease_pool.rb +29 -0
  286. data/lib/farce/lfu_map.rb +53 -0
  287. data/lib/farce/local/atom.rb +23 -0
  288. data/lib/farce/local/counter.rb +88 -0
  289. data/lib/farce/local/flag.rb +65 -0
  290. data/lib/farce/local/lazy.rb +38 -0
  291. data/lib/farce/local/lazy_ref.rb +36 -0
  292. data/lib/farce/local/lease.rb +77 -0
  293. data/lib/farce/local/lease_map.rb +80 -0
  294. data/lib/farce/local/lease_pool.rb +38 -0
  295. data/lib/farce/local/lfu_map.rb +55 -0
  296. data/lib/farce/local/lru_map.rb +62 -0
  297. data/lib/farce/local/map.rb +47 -0
  298. data/lib/farce/local/molecule.rb +45 -0
  299. data/lib/farce/local/priority_queue.rb +53 -0
  300. data/lib/farce/local/queue.rb +32 -0
  301. data/lib/farce/local/scoped.rb +166 -0
  302. data/lib/farce/local/set.rb +18 -0
  303. data/lib/farce/local/sorted_set.rb +50 -0
  304. data/lib/farce/local/timer_queue.rb +41 -0
  305. data/lib/farce/local/tree_map.rb +47 -0
  306. data/lib/farce/local/vector.rb +30 -0
  307. data/lib/farce/local/weak_atom.rb +23 -0
  308. data/lib/farce/local/weak_key_map.rb +39 -0
  309. data/lib/farce/local/weak_map.rb +39 -0
  310. data/lib/farce/local/weak_set.rb +18 -0
  311. data/lib/farce/local/weak_value_map.rb +39 -0
  312. data/lib/farce/local.rb +35 -0
  313. data/lib/farce/lock.rb +54 -0
  314. data/lib/farce/lru_map.rb +63 -0
  315. data/lib/farce/map.rb +58 -0
  316. data/lib/farce/mode_manager.rb +142 -0
  317. data/lib/farce/molecule.rb +60 -0
  318. data/lib/farce/mutable.rb +171 -0
  319. data/lib/farce/pool.rb +332 -0
  320. data/lib/farce/port.rb +134 -0
  321. data/lib/farce/priority_queue.rb +70 -0
  322. data/lib/farce/proxy/register.rb +101 -0
  323. data/lib/farce/proxy/supervisor.rb +137 -0
  324. data/lib/farce/proxy/wrapper.rb +49 -0
  325. data/lib/farce/proxy.rb +291 -0
  326. data/lib/farce/queue.rb +60 -0
  327. data/lib/farce/ractor.rb +269 -0
  328. data/lib/farce/read_write_lock.rb +256 -0
  329. data/lib/farce/reference.rb +158 -0
  330. data/lib/farce/resolv/dns.rb +42 -0
  331. data/lib/farce/resolv.rb +85 -0
  332. data/lib/farce/scheduler.rb +536 -0
  333. data/lib/farce/set.rb +39 -0
  334. data/lib/farce/shareable.rb +150 -0
  335. data/lib/farce/signal.rb +97 -0
  336. data/lib/farce/sorted_set.rb +36 -0
  337. data/lib/farce/strict/atom.rb +34 -0
  338. data/lib/farce/strict/counter.rb +10 -0
  339. data/lib/farce/strict/exchanger.rb +25 -0
  340. data/lib/farce/strict/flag.rb +10 -0
  341. data/lib/farce/strict/lazy.rb +26 -0
  342. data/lib/farce/strict/lazy_ref.rb +26 -0
  343. data/lib/farce/strict/lease.rb +27 -0
  344. data/lib/farce/strict/lease_map.rb +33 -0
  345. data/lib/farce/strict/lease_pool.rb +28 -0
  346. data/lib/farce/strict/lfu_map.rb +27 -0
  347. data/lib/farce/strict/lru_map.rb +28 -0
  348. data/lib/farce/strict/map.rb +54 -0
  349. data/lib/farce/strict/molecule.rb +16 -0
  350. data/lib/farce/strict/port.rb +40 -0
  351. data/lib/farce/strict/priority_queue.rb +20 -0
  352. data/lib/farce/strict/queue.rb +17 -0
  353. data/lib/farce/strict/set.rb +14 -0
  354. data/lib/farce/strict/sorted_set.rb +19 -0
  355. data/lib/farce/strict/timer_queue.rb +13 -0
  356. data/lib/farce/strict/tree_map.rb +27 -0
  357. data/lib/farce/strict/vector.rb +24 -0
  358. data/lib/farce/strict/weak_atom.rb +31 -0
  359. data/lib/farce/strict/weak_key_map.rb +49 -0
  360. data/lib/farce/strict/weak_map.rb +49 -0
  361. data/lib/farce/strict/weak_set.rb +14 -0
  362. data/lib/farce/strict/weak_value_map.rb +48 -0
  363. data/lib/farce/strict.rb +28 -0
  364. data/lib/farce/system.rb +40 -0
  365. data/lib/farce/thread_scheduler.rb +70 -0
  366. data/lib/farce/timer_queue.rb +66 -0
  367. data/lib/farce/transaction/atom.rb +73 -0
  368. data/lib/farce/transaction/map.rb +25 -0
  369. data/lib/farce/transaction/map_operations.rb +143 -0
  370. data/lib/farce/transaction/molecule.rb +90 -0
  371. data/lib/farce/transaction/mutable.rb +61 -0
  372. data/lib/farce/transaction/set.rb +17 -0
  373. data/lib/farce/transaction/set_operations.rb +48 -0
  374. data/lib/farce/transaction/sorted_set.rb +18 -0
  375. data/lib/farce/transaction/tree_map.rb +78 -0
  376. data/lib/farce/transaction/vector.rb +139 -0
  377. data/lib/farce/transaction/wrapper.rb +202 -0
  378. data/lib/farce/transaction.rb +343 -0
  379. data/lib/farce/tree_map.rb +56 -0
  380. data/lib/farce/unsafe/lfu_map.rb +36 -0
  381. data/lib/farce/unsafe/lru_map.rb +36 -0
  382. data/lib/farce/unsafe/tree_map.rb +21 -0
  383. data/lib/farce/unsafe.rb +29 -0
  384. data/lib/farce/unshareable.rb +67 -0
  385. data/lib/farce/unshared/atom.rb +24 -0
  386. data/lib/farce/unshared/counter.rb +10 -0
  387. data/lib/farce/unshared/flag.rb +10 -0
  388. data/lib/farce/unshared/lazy.rb +38 -0
  389. data/lib/farce/unshared/lazy_ref.rb +26 -0
  390. data/lib/farce/unshared/lease.rb +27 -0
  391. data/lib/farce/unshared/lease_map.rb +26 -0
  392. data/lib/farce/unshared/lease_pool.rb +25 -0
  393. data/lib/farce/unshared/lfu_map.rb +20 -0
  394. data/lib/farce/unshared/lru_map.rb +20 -0
  395. data/lib/farce/unshared/map.rb +49 -0
  396. data/lib/farce/unshared/molecule.rb +16 -0
  397. data/lib/farce/unshared/priority_queue.rb +16 -0
  398. data/lib/farce/unshared/queue.rb +27 -0
  399. data/lib/farce/unshared/set.rb +14 -0
  400. data/lib/farce/unshared/sorted_set.rb +28 -0
  401. data/lib/farce/unshared/timer_queue.rb +16 -0
  402. data/lib/farce/unshared/tree_map.rb +21 -0
  403. data/lib/farce/unshared/vector.rb +19 -0
  404. data/lib/farce/unshared/weak_atom.rb +31 -0
  405. data/lib/farce/unshared/weak_key_map.rb +44 -0
  406. data/lib/farce/unshared/weak_map.rb +44 -0
  407. data/lib/farce/unshared/weak_set.rb +14 -0
  408. data/lib/farce/unshared/weak_value_map.rb +43 -0
  409. data/lib/farce/unshared.rb +28 -0
  410. data/lib/farce/vector.rb +228 -0
  411. data/lib/farce/version.rb +8 -0
  412. data/lib/farce/walker/definitions.rb +186 -0
  413. data/lib/farce/walker/modification.rb +355 -0
  414. data/lib/farce/walker.rb +319 -0
  415. data/lib/farce/weak_atom.rb +121 -0
  416. data/lib/farce/weak_key_map.rb +31 -0
  417. data/lib/farce/weak_map.rb +33 -0
  418. data/lib/farce/weak_ref.rb +67 -0
  419. data/lib/farce/weak_set.rb +82 -0
  420. data/lib/farce/weak_value.rb +195 -0
  421. data/lib/farce/weak_value_map.rb +32 -0
  422. data/lib/farce.rb +519 -0
  423. metadata +468 -0
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable lease with one independently initialized resource in each scope.
8
+ #
9
+ # @example Keeping an independent object in each Fiber
10
+ # lease = Farce::Local::Lease.new(scope: :fiber) { { name: :initial } }
11
+ # lease.checkout { |state| state[:name] = :parent }
12
+ #
13
+ # Fiber.new do
14
+ # lease.checkout { |state| state[:name] = :child }
15
+ # end.resume # => :child
16
+ #
17
+ # lease.checkout { |state| state[:name] } # => :parent
18
+ class Lease < Abstract::Lease
19
+ include Shareable::Unfreezable
20
+ include Scoped
21
+
22
+ # Create a lease that initializes once on first use in each scope.
23
+ # @!macro scopes
24
+ # @param scope [Symbol] the scope of the lease
25
+ # @yield builds the resource for each initialized scope
26
+ # @yieldreturn [BasicObject] the initial resource for that scope
27
+ def initialize(scope: :ractor, &initializer)
28
+ raise ArgumentError, "a resource constructor block is required" unless initializer
29
+
30
+ @initializer = Ractor.shareable?(initializer) ? initializer : Ractor.shareable_proc(&initializer)
31
+ super(scope:)
32
+ end
33
+
34
+ # (see Farce::Abstract::Lease#checkout)
35
+ def checkout(timeout: nil, &block)
36
+ deadline = timeout_deadline(timeout)
37
+ lease = scoped_lease(timeout: remaining_timeout(deadline))
38
+ raise TimeoutError, "lease checkout timed out" unless lease
39
+
40
+ timeout = remaining_timeout(deadline)
41
+ block ? lease.checkout(timeout:, &block) : lease.checkout(timeout:)
42
+ end
43
+
44
+ # (see Farce::Abstract::Lease#try_checkout)
45
+ def try_checkout(&block)
46
+ lease = scoped_lease(wait: false)
47
+ return unless lease
48
+
49
+ block ? lease.try_checkout(&block) : lease.try_checkout
50
+ end
51
+
52
+ private
53
+
54
+ def eager_scoped_value? = false
55
+ def new_scoped_value = Internal::LeaseInitialization.new
56
+ def internal_lease = scoped_lease
57
+ def scoped_lease(**) = scoped_value.fetch(**) { Internal::UnsharedLease.new(@initializer.call) }
58
+
59
+ def timeout_deadline(timeout)
60
+ return if timeout.nil?
61
+
62
+ timeout = Float(timeout)
63
+ unless timeout.finite? && !timeout.negative?
64
+ raise ArgumentError, "timeout must be a finite, non-negative number or nil"
65
+ end
66
+ Clock.now + timeout
67
+ end
68
+
69
+ def remaining_timeout(deadline)
70
+ return unless deadline
71
+
72
+ remaining = deadline - Clock.now
73
+ remaining.positive? ? remaining : 0
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable lease map with independently initialized resources in each scope.
8
+ #
9
+ # @example Keeping independent resources in each Fiber
10
+ # leases = Farce::Local::LeaseMap.new(scope: :fiber) { { jobs: [] } }
11
+ # leases.checkout(:jobs) { |jobs| jobs << :parent }
12
+ # Fiber.new { leases.checkout(:jobs, &:dup) }.resume # => []
13
+ # leases.checkout(:jobs, &:dup) # => [:parent]
14
+ class LeaseMap < Abstract::LeaseMap
15
+ include Shareable::Unfreezable
16
+ include Scoped
17
+
18
+ # Create a map that initializes once on first use in each scope.
19
+ # @!macro scopes
20
+ # @param scope [Symbol] the scope of the lease map
21
+ # @yield builds the initial mapping for each initialized scope
22
+ # @yieldreturn [Hash, Farce::Abstract::Map, #each] the initial mapping for that scope
23
+ def initialize(scope: :ractor, normalize_keys: nil, &initializer)
24
+ raise ArgumentError, "a resource constructor block is required" unless initializer
25
+
26
+ @initializer = Ractor.shareable?(initializer) ? initializer : Ractor.shareable_proc(&initializer)
27
+ super(scope:, normalize_keys:)
28
+ end
29
+
30
+ private
31
+
32
+ def eager_scoped_value? = false
33
+ def new_scoped_value = Internal::LeaseInitialization.new
34
+ def internal_lease_map = scoped_lease_map
35
+
36
+ def checkout_canonical(key, missing_key:, timeout: nil, &)
37
+ deadline = timeout_deadline(timeout)
38
+ map = scoped_lease_map(timeout: remaining_timeout(deadline))
39
+ raise TimeoutError, "lease checkout timed out" unless map
40
+
41
+ map.checkout(key, timeout: remaining_timeout(deadline), receiver: self, missing_key:, &)
42
+ end
43
+
44
+ def try_checkout_canonical(key, missing_key:, &)
45
+ map = scoped_lease_map(wait: false)
46
+ return unless map
47
+
48
+ map.try_checkout(key, receiver: self, missing_key:, &)
49
+ end
50
+
51
+ def scoped_lease_map(**)
52
+ scoped_value.fetch(**) do
53
+ mapping = prepare_initial_resources(@initializer.call, @key_normalizer)
54
+ Internal::LeaseMap.new(
55
+ mapping,
56
+ lease_class: Farce::Unshared::Lease,
57
+ registry_class: Farce::Unshared::Map,
58
+ )
59
+ end
60
+ end
61
+
62
+ def timeout_deadline(timeout)
63
+ return if timeout.nil?
64
+
65
+ timeout = Float(timeout)
66
+ unless timeout.finite? && !timeout.negative?
67
+ raise ArgumentError, "timeout must be a finite, non-negative number or nil"
68
+ end
69
+ Clock.now + timeout
70
+ end
71
+
72
+ def remaining_timeout(deadline)
73
+ return unless deadline
74
+
75
+ remaining = deadline - Clock.now
76
+ remaining.positive? ? remaining : 0
77
+ end
78
+ end
79
+ end
80
+ 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 pool with independent capacity and resources in each scope.
8
+ #
9
+ # @example Keeping separate pools in each Fiber
10
+ # pool = Farce::Local::LeasePool.new(scope: :fiber, max_size: 1) { [] }
11
+ # pool.checkout { |items| items << :parent }
12
+ #
13
+ # Fiber.new { pool.checkout(&:dup) }.resume # => []
14
+ # pool.checkout(&:dup) # => [:parent]
15
+ class LeasePool < Abstract::LeasePool
16
+ include Shareable::Unfreezable
17
+ include Scoped
18
+
19
+ # Construct an empty pool in each scope.
20
+ # @!macro scopes
21
+ # @param scope [Symbol] the scope of the pool
22
+ # @param max_size [Integer] the maximum number of resources in each scope
23
+ # @yield builds a resource in the requesting scope
24
+ # @yieldreturn [BasicObject] a new resource
25
+ def initialize(max_size:, scope: :ractor, &factory)
26
+ validate_configuration!(max_size, factory)
27
+ @max_size = max_size
28
+ @factory = Ractor.shareable?(factory) ? factory : Ractor.shareable_proc(&factory)
29
+ super(scope:, max_size:)
30
+ end
31
+
32
+ private
33
+
34
+ def new_scoped_value(max_size:) = Internal::UnsharedLeasePool.new(max_size)
35
+ def internal_pool = scoped_value
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable LFU map with independent mutable storage and capacity in each scope.
8
+ #
9
+ # @!method initialize(entries = nil, max_size:, scope: :ractor, **options)
10
+ # @!macro scopes
11
+ # @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Abstract::Map, #each, nil]
12
+ # Initial entries for each new scope. Entries are stored sequentially and may be evicted.
13
+ # @param max_size [Integer] Maximum entries retained by each new scoped map.
14
+ # @param scope [Symbol] The scope of the LFU map.
15
+ # @return [LFUMap]
16
+ class LFUMap < Abstract::LFUMap
17
+ include Shareable::Tracked
18
+ include Scoped::Tracked
19
+
20
+ State = Data.define(:map, :key_locks)
21
+ private_constant :State
22
+
23
+ protected
24
+
25
+ def internal_map = scoped_value.map
26
+
27
+ private
28
+
29
+ def freeze_scoped_value(state) = state.map.freeze
30
+
31
+ def new_copied_scoped_value(map)
32
+ locks = Internal::KeyLockMap.new(
33
+ registry_class: Farce::Unshared::Map,
34
+ compare_keys_by_identity: map.compare_keys_by_identity?,
35
+ )
36
+ State.new(map, locks)
37
+ end
38
+
39
+ def with_key_lock(key, &) = scoped_value.key_locks.synchronize(key, &)
40
+
41
+ def new_scoped_value(
42
+ entries = nil,
43
+ compare_by_identity: false,
44
+ compare_keys_by_identity: compare_by_identity,
45
+ **
46
+ )
47
+ entries = convert_entries(entries)
48
+ map = Internal::LFUMap.new(compare_by_identity:, compare_keys_by_identity:, **)
49
+ locks = Internal::KeyLockMap.new(registry_class: Farce::Unshared::Map, compare_keys_by_identity:)
50
+ entries&.each { map[_1] = _2 }
51
+ State.new(map, locks)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable LRU map with independent mutable storage and capacity in each scope.
8
+ #
9
+ # @!method initialize(entries = nil, max_size:, scope: :ractor, **options)
10
+ # @!macro scopes
11
+ # @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Abstract::Map, #each, nil]
12
+ # Initial entries for each new scope. Entries are stored sequentially and may be evicted.
13
+ # @param max_size [Integer] Maximum entries retained by each new scoped map.
14
+ # @param scope [Symbol] The scope of the LRU map.
15
+ # @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
16
+ # @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
17
+ # @option options [Boolean] compare_values_by_identity (compare_by_identity)
18
+ # whether values are compared by identity
19
+ # @return [LRUMap]
20
+ class LRUMap < Abstract::LRUMap
21
+ include Shareable::Tracked
22
+ include Scoped::Tracked
23
+
24
+ State = Data.define(:map, :key_locks)
25
+ private_constant :State
26
+
27
+ protected
28
+
29
+ def internal_map = scoped_value.map
30
+
31
+ private
32
+
33
+ def freeze_scoped_value(state) = state.map.freeze
34
+
35
+ def new_copied_scoped_value(map)
36
+ locks = Internal::KeyLockMap.new(
37
+ registry_class: Farce::Unshared::Map,
38
+ compare_keys_by_identity: map.compare_keys_by_identity?,
39
+ )
40
+ State.new(map, locks)
41
+ end
42
+
43
+ def with_key_lock(key, &) = scoped_value.key_locks.synchronize(key, &)
44
+
45
+ def new_scoped_value(
46
+ entries = nil,
47
+ compare_by_identity: false,
48
+ compare_keys_by_identity: compare_by_identity,
49
+ **
50
+ )
51
+ entries = convert_entries(entries)
52
+ map = Internal::LRUMap.new(compare_by_identity:, compare_keys_by_identity:, **)
53
+ entries&.each { map[_1] = _2 }
54
+ locks = Internal::KeyLockMap.new(
55
+ registry_class: Farce::Unshared::Map,
56
+ compare_keys_by_identity:,
57
+ )
58
+ State.new(map, locks)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable map with independent mutable storage in each scope.
8
+ #
9
+ # @!parse
10
+ # class Map
11
+ # # @!macro map_initialization
12
+ # # Initial entries are reused for each new scope.
13
+ # # @!macro scopes
14
+ # # @param scope [Symbol] The scope of the map. Defaults to `:ractor`.
15
+ # # @return [Map]
16
+ # def initialize(
17
+ # initial_mapping = nil,
18
+ # scope: :ractor,
19
+ # normalize_keys: nil,
20
+ # compare_by_identity: false,
21
+ # compare_keys_by_identity: compare_by_identity,
22
+ # compare_values_by_identity: compare_by_identity
23
+ # )
24
+ # end
25
+ # end
26
+ class Map < Abstract::ConcurrentMap
27
+ include Shareable::Tracked
28
+ include Scoped::Tracked
29
+
30
+ protected
31
+
32
+ def internal_map = scoped_value
33
+
34
+ private
35
+
36
+ def new_scoped_value(entries = nil, **)
37
+ unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
38
+ return Internal::UnsharedMap.new(entries, **)
39
+ end
40
+
41
+ map = Internal::UnsharedMap.new(nil, **)
42
+ entries&.each { map[_1] = _2 }
43
+ Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable record with independent field values in each selected scope.
8
+ # Use {Abstract::Molecule.define} to declare named fields.
9
+ # Explicitly supplied atoms keep their own storage and comparison policies.
10
+ class Molecule < Abstract::Molecule
11
+ include Shareable
12
+
13
+ # Create a record class with a default scope for newly created field atoms.
14
+ # @!macro scopes
15
+ # @param fields [Array<Symbol, String>] the field names
16
+ # @param scope [Symbol, nil] the default scope, or nil to inherit
17
+ # @param compare_by_identity [Boolean, nil] the default comparison policy
18
+ # @return [Class<Molecule>]
19
+ def self.define(*fields, scope: nil, **)
20
+ raise ArgumentError, "Invalid scope: #{scope.inspect}" unless scope.nil? || Farce::SCOPES.include?(scope)
21
+
22
+ subclass = super(*fields, **)
23
+ subclass.class_eval "def self.default_scope = #{scope.inspect}", __FILE__, __LINE__ unless scope.nil?
24
+ subclass
25
+ end
26
+
27
+ # @return [Symbol] the default scope for new records
28
+ def self.default_scope = :ractor
29
+
30
+ # Initialize fields using this variant's atoms.
31
+ # @!macro scopes
32
+ # @param scope [Symbol] the scope for new field atoms, defaulting to the record class scope
33
+ # @see Abstract::Molecule#initialize
34
+ def initialize(*, scope: self.class.default_scope, **)
35
+ @scope = scope
36
+ super(*, **)
37
+ end
38
+
39
+ # @return [Symbol] the scope used by newly created field atoms
40
+ attr_reader :scope
41
+
42
+ private def create_atom(_, value) = Atom.new(value, scope:, compare_by_identity: compare_by_identity?)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable priority queue with independent mutable storage in each scope.
8
+ class PriorityQueue < Abstract::PriorityQueue
9
+ include Shareable::Unfreezable
10
+ include Scoped
11
+
12
+ # @overload initialize(capacity: nil, default_priority: 0, order: :ascending, scope: :ractor, track_age: false)
13
+ # @!macro scopes
14
+ # @param capacity [Integer, nil] the maximum number of values, or nil for an unbounded queue
15
+ # @param default_priority [BasicObject] the shareable priority used when none is passed to push or try_push
16
+ # @param order [:ascending, :descending] the priority order
17
+ # @param scope [Symbol] the scope of the priority queue
18
+ # @param track_age [Boolean] whether to track enqueue age and queue generations
19
+ # @param fiber_wait [:auto, :io, :block] how scheduled fibers wait on CRuby
20
+ # @return [PriorityQueue]
21
+ def initialize(
22
+ capacity: nil, default_priority: 0, order: :ascending, scope: :ractor,
23
+ track_age: false, fiber_wait: :auto
24
+ )
25
+ unless Ractor.shareable?(default_priority)
26
+ raise Ractor::IsolationError, "default_priority must be Ractor-shareable"
27
+ end
28
+
29
+ @order = normalize_order(order)
30
+ @default_priority = default_priority
31
+ @reverse_order = @order == :descending
32
+ super(capacity:, scope:, track_age:, fiber_wait:)
33
+ end
34
+
35
+ # @return [Symbol] always returns `:local`
36
+ def mode = :local
37
+
38
+ # @api private
39
+ def fiber_wait = internal_signal.fiber_wait
40
+
41
+ private
42
+
43
+ def eager_scoped_value? = false
44
+ def internal_queue = scoped_value.first
45
+ def internal_signal = scoped_value.last
46
+
47
+ def new_scoped_value(capacity: nil, fiber_wait: :auto, track_age: false)
48
+ signal = Internal::UnsharedSignal.for(fiber_wait)
49
+ [Internal::UnsharedPriorityQueue.new(capacity:, signal:, track_age:), signal]
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable queue with independent mutable storage in each scope.
8
+ #
9
+ # @!method initialize(capacity: 1024, track_age: false, scope: :ractor, **options)
10
+ # @!macro scopes
11
+ # @param capacity [Integer, nil] the maximum number of values, or nil for an unbounded queue
12
+ # @param track_age [Boolean] whether to track enqueue age and queue generations
13
+ # @param scope [Symbol] the scope of the queue
14
+ # @option options [:auto, :io, :block] fiber_wait (:auto) how scheduled fibers wait on CRuby
15
+ # @return [Queue]
16
+ class Queue < Abstract::Queue
17
+ include Shareable::Unfreezable
18
+ include Scoped
19
+
20
+ # @return [Symbol] always returns `:local`
21
+ def mode = :local
22
+
23
+ # @api private
24
+ def fiber_wait = internal_queue.fiber_wait
25
+
26
+ private
27
+
28
+ def internal_queue = scoped_value
29
+ def new_scoped_value(...) = Internal::UnsharedQueue.new(...)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # Common scope selection and storage for shareable local objects.
8
+ module Scoped
9
+ include Shareable
10
+
11
+ # Freeze the current backing before recording the handle-global state.
12
+ # @api private
13
+ module Tracked
14
+ include Scoped
15
+
16
+ # @!visibility private
17
+ def self.included(base)
18
+ Scoped.included(base)
19
+ super
20
+ end
21
+
22
+ def freeze
23
+ return Object.instance_method(:freeze).bind_call(self) unless @farce_freeze_state
24
+
25
+ state = @farce_freeze_state
26
+ if state.respond_to?(:native_flag)
27
+ state.set { freeze_scoped_value(scoped_value) }
28
+ self
29
+ else
30
+ freeze_scoped_value(scoped_value)
31
+ super
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def scoped_value
38
+ value = super
39
+ freeze_scoped_value(value) if frozen?
40
+ value
41
+ end
42
+ end
43
+
44
+ module Map
45
+ def initialize(*arguments, normalize_keys: nil, **)
46
+ restoring = Internal::KeyNormalizer.restoration?(normalize_keys)
47
+ normalizer = Internal::KeyNormalizer.build(normalize_keys, shareable: true)
48
+
49
+ if is_a?(Abstract::ConcurrentMap)
50
+ @key_normalizer = normalizer
51
+ else
52
+ Internal::KeyNormalizer.install(self, normalizer, Internal::KeyNormalizer.operations_for(self))
53
+ end
54
+
55
+ if !is_a?(Abstract::LeaseMap) && !arguments.empty?
56
+ entries = convert_entries(arguments.first)
57
+ if entries && ((normalizer && !restoring) || !entries.is_a?(Hash))
58
+ canonical = Internal::KeyNormalizer.canonical_entries
59
+ entries.each do |key, value|
60
+ key = normalizer.call(key) if normalizer && !restoring
61
+ canonical << [key, value]
62
+ end
63
+ entries = canonical
64
+ end
65
+ arguments = arguments.dup
66
+ arguments[0] = entries
67
+ end
68
+ super(*arguments, **)
69
+ end
70
+
71
+ private
72
+
73
+ def install_copied_map(map) = Internal::Storage.scope(scope)[self] = new_copied_scoped_value(map)
74
+ def new_copied_scoped_value(map) = map
75
+ end
76
+
77
+ MANAGER = ModeManager.new
78
+ private_constant :MANAGER, :Map
79
+
80
+ # @!visibility private
81
+ def self.included(base)
82
+ base.include Map if base < Abstract::Map
83
+ super
84
+ end
85
+
86
+ # @return [Symbol] The scope used to resolve this object's contents.
87
+ attr_reader :scope
88
+
89
+ # Constructor arguments configure each scope's independent backing object.
90
+ # @!macro scopes
91
+ # @param arguments [Array<BasicObject>] positional arguments for the backing object
92
+ # @param options [Hash{Symbol => BasicObject}] keyword arguments for the backing object
93
+ # @param scope [Symbol] The storage scope. Defaults to :ractor.
94
+ def initialize(*arguments, scope: :ractor, **options)
95
+ initialize_scoped(arguments, options, scope:)
96
+ end
97
+
98
+ private
99
+
100
+ def initialize_scoped(arguments, options, scope:, configuration: nil)
101
+ raise ArgumentError, "Invalid scope: #{scope.inspect}" unless Farce::SCOPES.include?(scope)
102
+
103
+ if is_a?(Shareable::Tracked)
104
+ guard = Internal.native_ractors? ? Internal::TransactionFreezeGuard : Internal::Flag
105
+ @farce_freeze_state = guard.new(false)
106
+ end
107
+ @scope = scope
108
+ @configuration = configuration || MANAGER.wrap([arguments.freeze, options.freeze].freeze)
109
+
110
+ Internal::Storage.scope(scope)[self] = new_scoped_value(*arguments, **options) if eager_scoped_value?
111
+
112
+ # Abstract initializers allocate instance variables. Local backing objects
113
+ # live in Storage instead, so initialization ends here.
114
+ Internal::Freeze.publish(self)
115
+ end
116
+
117
+ def marshal_configuration
118
+ value = Envelope === @configuration ? @configuration.marshal_dump[1] : @configuration
119
+ arguments, options = value
120
+ canonical = Internal::KeyNormalizer.canonical_entries?(arguments.first)
121
+ if canonical
122
+ arguments = arguments.dup
123
+ arguments[0] = arguments[0].to_a.freeze
124
+ value = [arguments.freeze, options].freeze
125
+ end
126
+ [Internal::MarshalSupport.value(value), canonical]
127
+ end
128
+
129
+ # @api private
130
+ def marshal_initialize(arguments, options, configuration)
131
+ options = options.dup
132
+ scope = options.delete(:scope)
133
+ if options.key?(:normalize_keys)
134
+ normalizer = Internal::KeyNormalizer.build(options.delete(:normalize_keys), shareable: true)
135
+ if is_a?(Abstract::ConcurrentMap)
136
+ @key_normalizer = normalizer
137
+ else
138
+ Internal::KeyNormalizer.install(self, normalizer, Internal::KeyNormalizer.operations_for(self))
139
+ end
140
+ end
141
+ original, canonical = configuration
142
+ original = Internal::MarshalSupport.restore_value(original)
143
+ if canonical
144
+ initial, settings = original
145
+ initial = initial.dup
146
+ initial[0] = Internal::KeyNormalizer.canonical_entries.concat(initial[0])
147
+ original = [initial.freeze, settings].freeze
148
+ end
149
+ configuration = MANAGER.wrap(original)
150
+ initialize_scoped(arguments, options, scope:, configuration:)
151
+ end
152
+
153
+ # Subclasses without initial contents can defer backing storage allocation.
154
+ def eager_scoped_value? = true
155
+ def freeze_scoped_value(value) = value.freeze
156
+ def scoped_configuration = MANAGER.unwrap(@configuration)
157
+
158
+ def scoped_value
159
+ Internal::Storage.store_if_absent(self, scope:) do
160
+ arguments, options = scoped_configuration
161
+ new_scoped_value(*arguments, **options)
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable set with independent mutable contents in each scope.
8
+ class Set < Farce::Abstract::Set
9
+ include Shareable::Delegated
10
+
11
+ # The scope with independent contents.
12
+ # @return [Symbol] The configured scope name.
13
+ def scope = map_backend.scope
14
+
15
+ private def new_map(...) = Farce::Local::Map.new(...)
16
+ end
17
+ end
18
+ end