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,50 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A scoped set maintained in ascending order.
8
+ class SortedSet < Farce::Abstract::SortedSet
9
+ include Shareable::Delegated
10
+
11
+ class MutableTreeMap < Farce::Local::TreeMap # :nodoc:
12
+ BackingState = Data.define(:map, :key_locks)
13
+ private_constant :BackingState
14
+
15
+ private
16
+
17
+ def prepare_key(key) = key
18
+
19
+ def new_copied_scoped_value(map)
20
+ BackingState.new(map, Internal::MutableOrderedKeyLockMap.new)
21
+ end
22
+
23
+ def new_scoped_value(entries = nil, **)
24
+ unless Internal::KeyNormalizer.canonical_entries?(entries)
25
+ return BackingState.new(
26
+ Internal::MutableTreeMap.new(entries, **),
27
+ Internal::MutableOrderedKeyLockMap.new,
28
+ )
29
+ end
30
+
31
+ map = Internal::MutableTreeMap.new(nil, **)
32
+ entries.each { map[_1] = _2 }
33
+ BackingState.new(map, Internal::MutableOrderedKeyLockMap.new)
34
+ end
35
+ end
36
+ private_constant :MutableTreeMap
37
+
38
+ # The scope with independent contents.
39
+ # @return [Symbol] The configured scope name.
40
+ def scope = map_backend.scope
41
+
42
+ private
43
+
44
+ def new_map(entries = nil, compare_keys_by_identity: false, scope: :ractor, **)
45
+ raise ArgumentError, "sorted sets do not support identity comparison" if compare_keys_by_identity
46
+ MutableTreeMap.new(entries, scope:)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable timer queue with independent mutable storage in each scope.
8
+ class TimerQueue < Abstract::TimerQueue
9
+ include Shareable::Unfreezable
10
+ include Scoped
11
+
12
+ # @overload initialize(capacity: nil, track_age: false, scope: :ractor)
13
+ # @!macro scopes
14
+ # @param capacity [Integer, nil] the maximum number of values, or nil for an unbounded queue
15
+ # @param track_age [Boolean] whether to track enqueue age and queue generations
16
+ # @param scope [Symbol] the scope of the queue
17
+ # @param fiber_wait [:auto, :io, :block] how scheduled fibers wait on CRuby
18
+ # @return [TimerQueue]
19
+ def initialize(capacity: nil, track_age: false, scope: :ractor, fiber_wait: :auto)
20
+ super
21
+ end
22
+
23
+ # @return [Symbol] always returns `:local`
24
+ def mode = :local
25
+
26
+ # @api private
27
+ def fiber_wait = internal_signal.fiber_wait
28
+
29
+ private
30
+
31
+ def eager_scoped_value? = false
32
+ def internal_queue = scoped_value.first
33
+ def internal_signal = scoped_value.last
34
+
35
+ def new_scoped_value(capacity: nil, fiber_wait: :auto, track_age: false)
36
+ signal = Internal::UnsharedSignal.for(fiber_wait)
37
+ [Internal::UnsharedPriorityQueue.new(capacity:, signal:, track_age:), signal]
38
+ end
39
+ end
40
+ end
41
+ 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 tree map with independent mutable storage in each scope.
8
+ #
9
+ # @!method initialize(entries = nil, scope: :ractor)
10
+ # @!macro scopes
11
+ # @param entries [Hash, Farce::Abstract::Map, #each, #to_hash, nil] the entries to store initially
12
+ # @param scope [Symbol] the scope of the tree map
13
+ # @return [TreeMap]
14
+ class TreeMap < Abstract::TreeMap
15
+ include Shareable::Tracked
16
+ include Scoped::Tracked
17
+
18
+ State = Data.define(:map, :key_locks)
19
+ private_constant :State
20
+
21
+ protected
22
+
23
+ def internal_map = scoped_value.map
24
+ def transaction_key_locks = scoped_value.key_locks
25
+
26
+ private
27
+
28
+ def freeze_scoped_value(state) = state.map.freeze
29
+
30
+ def new_copied_scoped_value(map)
31
+ State.new(map, Internal::OrderedKeyLockMap.new)
32
+ end
33
+
34
+ def with_key_lock(key, &) = scoped_value.key_locks.synchronize(key, &)
35
+
36
+ def new_scoped_value(entries = nil, **)
37
+ unless Internal::KeyNormalizer.canonical_entries?(entries)
38
+ return State.new(Internal::TreeMap.new(entries, **), Internal::OrderedKeyLockMap.new)
39
+ end
40
+
41
+ map = Internal::TreeMap.new(nil, **)
42
+ entries.each { map[_1] = _2 }
43
+ State.new(map, Internal::OrderedKeyLockMap.new)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable vector with independent mutable storage in each scope.
8
+ #
9
+ # @!method initialize(source = nil, compare_by_identity: false, scope: :ractor)
10
+ # @!macro scopes
11
+ # @param source [Array, nil] the initial values
12
+ # @param compare_by_identity [Boolean] whether values are compared by identity
13
+ # @param scope [Symbol] the scope of the vector
14
+ # @return [Vector]
15
+ class Vector < Abstract::Vector
16
+ include Shareable::Tracked
17
+ include Scoped::Tracked
18
+
19
+ protected
20
+
21
+ def internal_vector = scoped_value
22
+
23
+ def build_derived_vector(values)
24
+ self.class.new(values, compare_by_identity: compare_by_identity?, scope:)
25
+ end
26
+
27
+ private def new_scoped_value(...) = Internal::UnsharedVector.new(...)
28
+ end
29
+ end
30
+ 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 weak atom with independent mutable storage 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 weak atomic reference
14
+ # @return [WeakAtom]
15
+ class WeakAtom < Abstract::WeakAtom
16
+ include Shareable::Tracked
17
+ include Scoped::Tracked
18
+
19
+ protected def internal_atom = scoped_value
20
+ private def new_scoped_value(...) = Internal::UnsharedWeakAtom.new(...)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable weak key map with independent mutable storage in each scope.
8
+ #
9
+ # @!method initialize(initial_mapping = nil, scope: :ractor, **options)
10
+ # @!macro scopes
11
+ # @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] the entries to store initially
12
+ # @param scope [Symbol] the scope of the weak key map
13
+ # @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
14
+ # @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
15
+ # @option options [Boolean] compare_values_by_identity (compare_by_identity)
16
+ # whether values are compared by identity
17
+ # @return [WeakKeyMap]
18
+ class WeakKeyMap < Abstract::WeakKeyMap
19
+ include Shareable::Tracked
20
+ include Scoped::Tracked
21
+
22
+ protected
23
+
24
+ def internal_map = scoped_value
25
+
26
+ private
27
+
28
+ def new_scoped_value(entries = nil, **)
29
+ unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
30
+ return Internal::UnsharedWeakKeyMap.new(entries, **)
31
+ end
32
+
33
+ map = Internal::UnsharedWeakKeyMap.new(nil, **)
34
+ entries&.each { map[_1] = _2 }
35
+ Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable weak map with independent mutable storage in each scope.
8
+ #
9
+ # @!method initialize(initial_mapping = nil, scope: :ractor, **options)
10
+ # @!macro scopes
11
+ # @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] the entries to store initially
12
+ # @param scope [Symbol] the scope of the weak map
13
+ # @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
14
+ # @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
15
+ # @option options [Boolean] compare_values_by_identity (compare_by_identity)
16
+ # whether values are compared by identity
17
+ # @return [WeakMap]
18
+ class WeakMap < Abstract::WeakMap
19
+ include Shareable::Tracked
20
+ include Scoped::Tracked
21
+
22
+ protected
23
+
24
+ def internal_map = scoped_value
25
+
26
+ private
27
+
28
+ def new_scoped_value(entries = nil, **)
29
+ unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
30
+ return Internal::UnsharedWeakMap.new(entries, **)
31
+ end
32
+
33
+ map = Internal::UnsharedWeakMap.new(nil, **)
34
+ entries&.each { map[_1] = _2 }
35
+ Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
36
+ end
37
+ end
38
+ end
39
+ 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 weak set with independent mutable contents in each scope.
8
+ class WeakSet < Farce::Abstract::WeakSet
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::WeakMap.new(...)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Local
7
+ # A shareable weak value map with independent mutable storage in each scope.
8
+ #
9
+ # @!method initialize(initial_mapping = nil, scope: :ractor, **options)
10
+ # @!macro scopes
11
+ # @param initial_mapping [Hash, Farce::Abstract::Map, #each, nil] the entries to store initially
12
+ # @param scope [Symbol] the scope of the weak value map
13
+ # @option options [Boolean] compare_by_identity (false) whether keys and values are compared by identity
14
+ # @option options [Boolean] compare_keys_by_identity (compare_by_identity) whether keys are compared by identity
15
+ # @option options [Boolean] compare_values_by_identity (compare_by_identity)
16
+ # whether values are compared by identity
17
+ # @return [WeakValueMap]
18
+ class WeakValueMap < Abstract::WeakValueMap
19
+ include Shareable::Tracked
20
+ include Scoped::Tracked
21
+
22
+ protected
23
+
24
+ def internal_map = scoped_value
25
+
26
+ private
27
+
28
+ def new_scoped_value(entries = nil, **)
29
+ unless @key_normalizer || Internal::KeyNormalizer.canonical_entries?(entries)
30
+ return Internal::UnsharedWeakValueMap.new(entries, **)
31
+ end
32
+
33
+ map = Internal::UnsharedWeakValueMap.new(nil, **)
34
+ entries&.each { map[_1] = _2 }
35
+ Internal::KeyNormalizer.wrap_concurrent(map, @key_normalizer)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # Shareable containers whose mutable contents belong to the current scope.
7
+ # Scopes may be :ractor, :thread_group, :thread, :fiber_storage, or :fiber.
8
+ # Initial contents are copied between Ractors. Within a Ractor, each scope has
9
+ # its own container, but objects supplied as initial contents may be shared.
10
+ module Local
11
+ include Internal::Autoloads
12
+
13
+ # @overload enfarce(object, freeze: nil, scope: :ractor)
14
+ # Converts vanilla Ruby objects into their {Farce::Local} equivalents.
15
+ #
16
+ # @!macro scopes
17
+ #
18
+ # @example
19
+ # Farce::Local.enfarce({ foo: ["bar"] })
20
+ # # => #<Farce::Local::Map {foo: #<Farce::Local::Vector ["bar"]>}>
21
+ #
22
+ #
23
+ # @yield [object] Optional block, called with any object that doesn't have a specific conversion defined.
24
+ # @yieldparam object [BasicObject] the object to convert
25
+ # @yieldreturn [BasicObject] the converted object
26
+ # @param object [BasicObject] The root object to convert
27
+ # @param freeze [Boolean, nil]
28
+ # Whether to freeze the converted objects.
29
+ # If set to `nil` (default), the freezing behavior will depend on the original object's frozen state.
30
+ # @param scope [Symbol] The scope for the local container, e.g., `:ractor`.
31
+ # @return [BasicObject] the converted object
32
+ # @see Farce.enfarce
33
+ def self.enfarce(object, **, &) = Internal::Converter.new(self, **, &).convert(object)
34
+ end
35
+ end
data/lib/farce/lock.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # @note
7
+ # On JRuby and TruffleRuby, this will be a subclass of Mutex. On CRuby, it will be a custom implementation,
8
+ # as Mutex instances are not Ractor-shareable.
9
+ #
10
+ # A Ractor-shareable mutual-exclusion lock with the same interface as Ruby's Mutex.
11
+ #
12
+ # Lock ownership belongs to the current Fiber. Attempting to lock the same lock recursively raises
13
+ # ThreadError. Waiting for a lock cooperates with an installed Fiber scheduler.
14
+ #
15
+ # @!method lock
16
+ # Acquire the lock, waiting until it is available.
17
+ # @return [self]
18
+ # @raise [ThreadError] if the current Fiber already owns the lock
19
+ #
20
+ # @!method locked?
21
+ # @return [Boolean] whether any Fiber owns the lock
22
+ #
23
+ # @!method owned?
24
+ # @return [Boolean] whether the current Fiber owns the lock
25
+ #
26
+ # @!method sleep(timeout = nil)
27
+ # Release the lock while sleeping, then reacquire it before returning.
28
+ # @param timeout [Numeric, nil] the maximum number of seconds to sleep, or nil to sleep indefinitely
29
+ # @return [nil]
30
+ # @raise [ThreadError] if the current Fiber does not own the lock
31
+ #
32
+ # @!method synchronize
33
+ # Acquire the lock for the duration of the block.
34
+ # @yield the block to run while holding the lock
35
+ # @return [Object] the block result
36
+ # @raise [ThreadError] if no block is given or the current Fiber already owns the lock
37
+ #
38
+ # @!method try_lock
39
+ # Attempt to acquire the lock without waiting.
40
+ # @return [Boolean] whether the lock was acquired
41
+ #
42
+ # @!method unlock
43
+ # Release the lock.
44
+ # @return [self]
45
+ # @raise [ThreadError] if the current Fiber does not own the lock
46
+ class Lock < Internal::Lock
47
+ include Internal::MarshalSupport::Reject
48
+ include Internal::Copyable
49
+ include Shareable::Native
50
+ include Shareable::Unfreezable
51
+
52
+ private def instance_variables_to_inspect = Internal::EMPTY_ARRAY
53
+ end
54
+ end
@@ -0,0 +1,63 @@
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 recently used entry.
7
+ # Transfer modes apply to values. Equality-mode mutable string keys become immutable.
8
+ # Individual reads and writes update recency. Iteration and key observation do not.
9
+ #
10
+ # @example Keeping copied values in a small cache
11
+ # cache = Farce::LRUMap.new(max_size: 2, mode: :copy)
12
+ #
13
+ # cache[:first] = [1]
14
+ # cache[:second] = [2]
15
+ # cache[:first] # makes sure :first was accessed more recently than :second
16
+ # cache[:third] = [3]
17
+ #
18
+ # cache.key?(:second) # => false
19
+ class LRUMap < Farce::Abstract::LRUMap
20
+ include Shareable::Delegated
21
+
22
+ # @!macro modes
23
+ # @param entries [Hash, Array<Array(BasicObject, BasicObject)>, Abstract::Map, #each, nil]
24
+ # Optional initial entries. Entries are stored sequentially and may be evicted.
25
+ # @param mode [Symbol] The default value transfer mode.
26
+ def initialize(entries = nil, mode: :copy, **)
27
+ @manager = marshal_mode_manager(mode)
28
+ super(entries, **)
29
+ end
30
+
31
+ # The default transfer mode for values.
32
+ # @return [Symbol]
33
+ def mode = @manager.mode
34
+
35
+ # Keys must be shareable, except equality-mode strings that can be snapshotted.
36
+ # @return [true]
37
+ def shareable_keys? = true
38
+
39
+ # Values are stored in Ractor-shareable representations.
40
+ # @return [true]
41
+ def shareable_values? = true
42
+
43
+ protected
44
+
45
+ def unwrap_value(value) = @manager.unwrap(value)
46
+
47
+ def wrap_value(value)
48
+ check_frozen!
49
+ @manager.wrap(value)
50
+ end
51
+
52
+ private
53
+
54
+ def each_for_inspect(&) = internal_map.each(&)
55
+ def inspect_value(inspector, value) = super(inspector, value, @manager)
56
+ def new_bounded_map(...) = Internal::StrictLRUMap.new(...)
57
+ def freeze_backend = internal_map
58
+
59
+ def new_key_locks(compare_keys_by_identity:)
60
+ Internal::KeyLockMap.new(registry_class: Farce::Strict::Map, compare_keys_by_identity:)
61
+ end
62
+ end
63
+ end
data/lib/farce/map.rb ADDED
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A concurrent map with direct shareable keys and strongly retained values.
7
+ # Transfer modes apply only to values. String keys use their frozen unary-minus form,
8
+ # except when comparing keys by identity. Other keys must already be shareable.
9
+ # Values stored through this map's mode manager are automatically unwrapped.
10
+ # Updates on different keys can run concurrently.
11
+ #
12
+ # Clearing the map invalidates unfinished updates so they cannot restore removed entries.
13
+ #
14
+ # `dup` and `clone` copy entries into independent storage while sharing stored values.
15
+ # Move-mode envelopes keep their ownership across copies. `dup` and ordinary `clone`
16
+ # remain Ractor-shareable.
17
+ #
18
+ # @example Atomically updating a value
19
+ # map = Farce::Map.new({ count: 0 })
20
+ # map.update(:count) { |count| count + 1 } # => 1
21
+ #
22
+ # @example Automatically making values shareable
23
+ # map = Farce::Map.new(mode: :make_shareable)
24
+ # map[:jobs] = []
25
+ # map.update(:jobs) { |items| items + [:job] } # => [:job]
26
+ # map[:jobs] # => [:job]
27
+ # Ractor.shareable?(map[:jobs]) # => true
28
+ #
29
+ # @!parse
30
+ # class Map
31
+ # # @!macro map_initialization
32
+ # # @!macro modes
33
+ # # @param mode [Symbol] The default transfer mode for values. Defaults to `:copy`.
34
+ # # @return [Map]
35
+ # def initialize(
36
+ # initial_mapping = nil,
37
+ # mode: :copy,
38
+ # normalize_keys: nil,
39
+ # compare_by_identity: false,
40
+ # compare_keys_by_identity: compare_by_identity,
41
+ # compare_values_by_identity: compare_by_identity
42
+ # )
43
+ # end
44
+ # end
45
+ class Map < Abstract::ConcurrentMap
46
+ include Internal::MapValueModes
47
+ include Shareable::Delegated
48
+
49
+ private
50
+
51
+ def new_map(**) = Internal::StrictMap.new(**)
52
+ def freeze_backend = @map
53
+
54
+ Internal.prepare_map_access(self, :modes)
55
+ end
56
+
57
+ Internal::KeyNormalizer.prepare_concurrent_class(Internal::StrictMap)
58
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A mode manager encapsulates logic for dealing with Ruby objects that aren't Ractor-shareable.
7
+ # It is a perfect companion piece for APIs that require Ractor-shareable objects.
8
+ #
9
+ # For instance, [Ratomic::Queue](https://mperham.github.io/ratomic/Ratomic/Queue.html) expects Ractor-shareable
10
+ # objects without checking for them. Creating a thin wrapper with a mode manager allows you to use it safely and with
11
+ # non-shareable objects.
12
+ #
13
+ # ```ruby
14
+ # require "ratomic"
15
+ # require "farce"
16
+ #
17
+ # class MyQueue
18
+ # include Farce::Shareable
19
+ #
20
+ # def initialize(capacity: 1024, mode: :copy)
21
+ # @queue = Ratomic::Queue.new(capacity)
22
+ # @manager = Farce::ModeManager.new(mode:)
23
+ # end
24
+ #
25
+ # def pop = @manager.unwrap(@queue.pop)
26
+ #
27
+ # def push(value, mode: nil)
28
+ # @queue.push(@manager.wrap(value, mode:))
29
+ # self
30
+ # end
31
+ # end
32
+ #
33
+ # queue = MyQueue.new(capacity: 100, mode: :move)
34
+ #
35
+ # # consume the queue on a different Ractor
36
+ # Ractor.new(queue) { |q| p Ractor.shareable?(q.pop) }
37
+ #
38
+ # object = Object.new
39
+ # q.push(object)
40
+ #
41
+ # # push moved the object into the queue, so it is no longer accessible on the current Ractor
42
+ # Ractor::MovedObject === object # => true
43
+ # ```
44
+ #
45
+ # It is recommended to use a different mode manager for each instance of a queue, port, or whatever else you're
46
+ # wrapping, as {#wrap} may create an {Envelope} and {#unwrap} will only unwrap envelopes created by the same mode
47
+ # manager. This way users can still safely send envelopes they created between Ractors without them getting
48
+ # unexpectedly claimed.
49
+ #
50
+ # @!macro modes
51
+ class ModeManager
52
+ include Internal::Copyable
53
+ include Shareable::Immutable
54
+
55
+ # @return [Symbol] The default mode to use when wrapping values.
56
+ attr_reader :mode
57
+
58
+ # @!macro modes
59
+ # @param mode [Symbol] The default mode to use when wrapping values. Must be one of the valid modes.
60
+ # @param register [Proxy::Register, nil] The register to use for generating Farce::Proxy instances.
61
+ def initialize(mode: :copy, register: nil)
62
+ raise ArgumentError, "invalid mode: #{mode.inspect}" unless Farce::MODES.include?(mode)
63
+ @mode = mode
64
+ @register = register
65
+ super()
66
+ end
67
+
68
+ # @api private
69
+ def marshal_dump
70
+ raise TypeError, "proxy registers cannot be marshaled" if @register
71
+ [1, mode]
72
+ end
73
+
74
+ # @api private
75
+ def marshal_load(data)
76
+ mode, = Internal::MarshalSupport.payload(data, 1)
77
+ initialize(mode:)
78
+ end
79
+
80
+ # Wraps a value in an envelope if it is not Ractor-shareable.
81
+ #
82
+ # @!macro modes
83
+ # @param value [BasicObject] The value to wrap.
84
+ # @param mode [Symbol, nil] The mode to use when wrapping the value. If `nil`, the default mode will be used.
85
+ # @return [BasicObject, Envelope]
86
+ # The prepared value, or a managed envelope containing it.
87
+ def wrap(value, mode: nil)
88
+ return value if Ractor.shareable?(value)
89
+
90
+ case mode || self.mode
91
+ when :copy then Envelope::Copy.new(value, self)
92
+ when :dedup then Ractor.make_shareable(Farce.dedup(value))
93
+ when :local then Envelope::Local.new(value, self)
94
+ when :make_shareable then Ractor.make_shareable(value)
95
+ when :move then Envelope::Move.new(value, self)
96
+ when :mutable then Mutable.new(value)
97
+ when :raise then raise Ractor::IsolationError, "value is not Ractor-shareable: #{value.inspect}"
98
+ when :shareable_copy then Ractor.make_shareable(value, copy: true)
99
+ when :proxy then Proxy.new(value, register: @register)
100
+ else raise ArgumentError, "invalid mode: #{mode.inspect}"
101
+ end
102
+ end
103
+
104
+ # Unwraps any envelope created by this mode manager, returning the value inside.
105
+ # Other options, including other envelopes, will be returned as-is.
106
+ # @param value [BasicObject, Envelope] The value to unwrap.
107
+ # @return [BasicObject]
108
+ # The value inside the envelope if it was created by this mode manager, or the value itself if it was not.
109
+ def unwrap(value)
110
+ return value unless managed_envelope?(value)
111
+ value.value
112
+ end
113
+
114
+ # Compares two values managed by this instance. Compatible envelopes can be
115
+ # compared without claiming or opening them.
116
+ # @param left [BasicObject, Envelope] The stored value.
117
+ # @param right [BasicObject, Envelope] The comparison value.
118
+ # @param identity [Boolean] Whether to compare by identity instead of equality.
119
+ # @return [Boolean] Whether the values match.
120
+ def same_value?(left, right, identity: false)
121
+ left_managed = managed_envelope?(left)
122
+ right_managed = managed_envelope?(right)
123
+
124
+ if left_managed && right_managed
125
+ return false if identity && Envelope::Move === left && Envelope::Local === right && !left.owned?
126
+ return left.same_value?(right, identity:)
127
+ end
128
+
129
+ return false if identity && left_managed
130
+ return left.same_value?(right) if left_managed
131
+
132
+ left = unwrap(left)
133
+ right = unwrap(right)
134
+ return BasicObject.instance_method(:equal?).bind_call(left, right) if identity
135
+
136
+ left == right
137
+ end
138
+
139
+ # @return [Boolean] Whether the value is an envelope managed by this mode manager.
140
+ def managed_envelope?(value) = Envelope === value && value.auto_unwrap.equal?(self)
141
+ end
142
+ end