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
data/lib/farce.rb ADDED
@@ -0,0 +1,519 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ begin
6
+ # We don't actually use Zeitwerk, but this works around a Zeitwerk bug.
7
+ # Zeitwerk might break if a Ractor starts before it is loaded.
8
+ #
9
+ # Ractor.new {}
10
+ # require "zeitwerk"
11
+ # require "zeitwerk" # NoMethodError: super: no superclass method 'require' for main
12
+ #
13
+ # This has absolutely nothing to do with Farce, except that Farce might create a Ractor.
14
+ require "zeitwerk"
15
+ rescue LoadError => e
16
+ raise unless e.path == "zeitwerk"
17
+ end
18
+
19
+ require "farce/config"
20
+ require "farce/internal"
21
+ require "farce/engine/#{RUBY_ENGINE}"
22
+ require "farce/version"
23
+ require "farce/error"
24
+ require "farce/system"
25
+
26
+ # Namespace for everything provided by Farce.
27
+ #
28
+ # ## Including Farce
29
+ #
30
+ # Including Farce in a class or module will include **all public camel-case constants** defined under the Farce
31
+ # namespace.
32
+ #
33
+ # ```ruby
34
+ # require "farce"
35
+ #
36
+ # # This could also be done under a class or module, to avoid polluting the global namespace.
37
+ # include Farce
38
+ #
39
+ # # Now Ractor is available, even on JRuby or TruffleRuby!
40
+ # Ractor.new { puts "Hello from a Ractor!" }
41
+ #
42
+ # # Other constants are also available.
43
+ # Clock.parse(1.minute.from_now)
44
+ #
45
+ # # VERSION is not exposed. This is to avoid polluting other libraries with it.
46
+ # defined?(VERSION) # => false
47
+ # ```
48
+ module Farce
49
+ include Internal::Autoloads
50
+
51
+ # The valid storage scopes for local values and collections.
52
+ # @return [::Set<Symbol>]
53
+ SCOPES = ::Set[:ractor, :thread_group, :thread, :fiber_storage, :fiber].freeze
54
+
55
+ # The valid transfer modes for values that are not Ractor-shareable.
56
+ # @return [::Set<Symbol>]
57
+ MODES = ::Set[:copy, :move, :local, :make_shareable, :mutable, :raise, :shareable_copy, :dedup, :proxy].freeze
58
+
59
+ UNDEFINED = Internal::Undefined.new("UNDEFINED")
60
+
61
+ autoload :DEDUPER, "farce/deduper"
62
+ private_constant :Internal, :UNDEFINED, :DEDUPER
63
+
64
+ # @overload transaction(*objects, retries: nil, backoff_after: 10, max_backoff: 1.0)
65
+ # Creates and runs a new transaction attempt.
66
+ #
67
+ # Automatically retries failed attempts indefinitely unless a retry limit is specified.
68
+ # Starts backing off after the specified number of attempts, up to the maximum delay.
69
+ #
70
+ # @example Modifying multiple entries in a map
71
+ # accounts = Farce::Map.new({a: 100, b: 200})
72
+ #
73
+ # # transfer 80 from :a to :b, but only if both succeed
74
+ # success = Farce.transaction(accounts) do |tx, accounts|
75
+ # tx.abort! if accounts[:b] < 80
76
+ # accounts[:a] += 80
77
+ # accounts[:b] -= 80
78
+ # end
79
+ #
80
+ # if success
81
+ # puts "Transaction succeeded"
82
+ # else
83
+ # puts "Transaction failed"
84
+ # end
85
+ #
86
+ # @example Programmatically registering objects for transactions
87
+ # map = Farce::Map.new({a: 1, b: 2})
88
+ # summary = Farce::Atom.new("size not calculated")
89
+ #
90
+ # # make sure map[:size], map.size, and the summary all match
91
+ # Farce.transaction do |tx|
92
+ # tx_map = tx[map]
93
+ # tx_map[:size] = size = tx_map.size
94
+ # tx[summary].value = "size: #{size}"
95
+ # end
96
+ #
97
+ # @param objects [Array] list of objects to enroll in the transaction
98
+ # @param retries [Integer, nil] maximum additional attempts, or nil for unlimited retries
99
+ # @param backoff_after [Integer] number of attempts before starting to back off
100
+ # @param max_backoff [Numeric] maximum backoff delay in seconds
101
+ # @yield [transaction, *objects] the current transaction and the enrolled objects
102
+ # @yieldparam transaction [Farce::Transaction] the current transaction
103
+ # @yieldparam objects [Array] the enrolled objects
104
+ # @return [Boolean] whether the transaction committed successfully
105
+ def self.transaction(...) = Transaction.run(...)
106
+
107
+ # @overload clock
108
+ # The current clock time
109
+ #
110
+ # @overload clock(value)
111
+ # Parses value into a monotonic clock time.
112
+ # @param value [nil, Numeric, Time, ActiveSupport::Duration, Hash] the value to convert to clock time
113
+ # @see Clock.parse
114
+ #
115
+ # @overload clock(at:)
116
+ # Gives the clock time for a fixed point in time. Independent of the current time.
117
+ # @param at [Numeric, Time] the value to convert to clock time
118
+ #
119
+ # @overload clock(time:)
120
+ # Gives the clock time for a fixed point in time. Independent of the current time.
121
+ # @param time [Numeric, Time] the value to convert to clock time
122
+ #
123
+ # @overload clock(timeout_at:)
124
+ # Gives the clock time for a fixed point in time. Independent of the current time.
125
+ # @param timeout_at [Numeric, Time] the value to convert to clock time
126
+ #
127
+ # @overload clock(delay:)
128
+ # Gives the clock time for a relative offset from the current time.
129
+ # @param delay [Numeric] the value to convert to clock time
130
+ #
131
+ # @overload clock(offset:)
132
+ # Gives the clock time for a relative offset from the current time.
133
+ # @param offset [Numeric] the value to convert to clock time
134
+ #
135
+ # @overload clock(timeout:)
136
+ # Gives the clock time for a relative offset from the current time.
137
+ # @param timeout [Numeric] the value to convert to clock time
138
+ #
139
+ # @overload clock(wait:)
140
+ # Gives the clock time for a relative offset from the current time.
141
+ # @param wait [Numeric] the value to convert to clock time
142
+ #
143
+ # @return [Float] monotonic clock time in seconds, from when clock was called the first time
144
+ def self.clock(...) = Clock.parse(...)
145
+
146
+ # Deduplicate values using the default {Deduper}.
147
+ # Cached values are held weakly, so retaining only an object_id does not keep
148
+ # its canonical object alive. Keep the returned object to preserve its identity.
149
+ #
150
+ # @overload dedup(object, copy: false, skip: nil)
151
+ # Reuse equal strings and frozen containers throughout an object graph.
152
+ # @example Preserve the input
153
+ # first = Farce.dedup(["foo"], copy: true)
154
+ # Farce.dedup(["foo"]).equal?(first) # => true
155
+ # @param object [Object] the root object
156
+ # @param copy [Boolean, Symbol] false to update the input, true to copy it,
157
+ # or a copy method such as :clone
158
+ # @param skip [Module, Array<Module>, nil] additional classes or modules to skip
159
+ # @return [Object] the deduplicated result
160
+ #
161
+ # @overload dedup
162
+ # Return the default deduper to configure subsequent calls.
163
+ # @example Exclude a class and its children
164
+ # Farce.dedup.skip(SomeClass)
165
+ # @example Cache another value class
166
+ # Farce.dedup.store(MyValue)
167
+ # @return [Deduper]
168
+ # @see Deduper#dedup
169
+ def self.dedup(object = UNDEFINED, **)
170
+ return DEDUPER if UNDEFINED.equal?(object)
171
+ DEDUPER.dedup(object, **)
172
+ end
173
+
174
+ # @overload enfarce(object, freeze: nil, mode: :copy)
175
+ # Converts vanilla Ruby objects into their {Farce} equivalents.
176
+ #
177
+ # @!macro modes
178
+ #
179
+ # @example
180
+ # Farce.enfarce({ foo: ["bar"] }) # => #<Farce::Map {foo: #<Farce::Vector ["bar"]>}>
181
+ #
182
+ # @yield [object] Optional block, called with any object that doesn't have a specific conversion defined.
183
+ # @yieldparam object [BasicObject] the object to convert
184
+ # @yieldreturn [BasicObject] the converted object
185
+ # @param object [BasicObject] The root object to convert
186
+ # @param freeze [Boolean, nil]
187
+ # Whether to freeze the converted objects.
188
+ # If set to `nil` (default), the freezing behavior will depend on the original object's frozen state.
189
+ # @param mode [Symbol] The conversion mode, e.g., `:copy`.
190
+ # @return [BasicObject] the converted object
191
+ # @see Farce::Local.enfarce
192
+ # @see Farce::Strict.enfarce
193
+ # @see Farce::Unsafe.enfarce
194
+ # @see Farce::Unshared.enfarce
195
+ def self.enfarce(object, **, &) = Internal::Converter.new(self, **, &).convert(object)
196
+
197
+ # Recursively freeze an object graph using {Walker} traversal.
198
+ #
199
+ # Visits container elements, hash keys and values, and instance variables.
200
+ # Shared children and cycles are preserved.
201
+ #
202
+ # Classes and modules are skipped by default. If it is enabled, then traversal visits
203
+ # instance variables, directly defined public constants, and directly defined class variables.
204
+ # Autoloads are skipped.
205
+ #
206
+ # Freezes objects in place and returns the original root. Already frozen
207
+ # objects are still traversed so their mutable children are frozen too.
208
+ #
209
+ # @example Freeze nested values in place
210
+ # values = { tags: [+"ruby"] }
211
+ # Farce.freeze_graph(values).equal?(values) # => true
212
+ # values[:tags].frozen? # => true
213
+ # values[:tags].first.frozen? # => true
214
+ #
215
+ # @example Freeze children of an already frozen container
216
+ # values = [+"ruby"].freeze
217
+ # Farce.freeze_graph(values).equal?(values) # => true
218
+ # values.first.frozen? # => true
219
+ #
220
+ # @example Freeze module state while allowing new methods
221
+ # mod = Module.new
222
+ # mod.instance_variable_set(:@tags, [+"ruby"])
223
+ # Farce.freeze_graph(mod, traverse_modules: true)
224
+ # mod.instance_variable_get(:@tags).first.frozen? # => true
225
+ # mod.frozen? # => false
226
+ #
227
+ # @param object [Object] the root object
228
+ # @param freeze_modules [Boolean] whether to freeze classes and modules
229
+ # @param traverse_modules [Boolean] whether to visit class and module instance
230
+ # variables. Defaults to `freeze_modules`, but can be set independently.
231
+ # @return [Object] the original root. Skipped modules retain their frozen state.
232
+ # @see Walker
233
+ def self.freeze_graph(object, freeze_modules: false, traverse_modules: freeze_modules)
234
+ Walker.visit(object) do |node, walker|
235
+ if Module === node
236
+ traverse = traverse_modules
237
+ freeze = freeze_modules
238
+ else
239
+ traverse = true
240
+ freeze = true
241
+ end
242
+
243
+ walker.traverse if traverse
244
+ node.freeze if freeze
245
+ node
246
+ end
247
+ end
248
+
249
+ # Binds a proc, lambda, block, bound or unbound method to a new self.
250
+ #
251
+ # This is similar to the following common approaches:
252
+ #
253
+ # 1. **`Ractor`**: using `Ractor.shareable_proc`/`Ractor.shareable_lambda`
254
+ # 2. **`BasicObject`**: using `#instance_exec`/`#instance_eval` (used by many DSLs, like Hanami or the dry-rb gems)
255
+ # 3. **`Module`**: using `#define_method` and method binding (used by many DSLs, like Sinatra or the money gem)
256
+ #
257
+ # Approach 2 and 3 are very popular for DSLs, but the resulting objects cannot be shared across ractors.
258
+ # The first approach fixes that, but only works on a limited number of procs and receivers, and is not available on
259
+ # all Ruby implementations.
260
+ #
261
+ # Property | `Ractor` | `BasicObject` | `Module` | `Farce`
262
+ # ------------------------------------|------------|---------------|---------------|----------
263
+ # Works with every proc | 🚫 **no** | ✅ yes | ✅ yes | ✅ yes
264
+ # Results can be made shareable | ✅ yes | 🚫 **no** | 🚫 **no** | ✅ yes
265
+ # Preserves parameters | ✅ yes | 🚫 **no** | ✅ yes | ✅ yes
266
+ # Procs can accept blocks | ✅ yes | 🚫 **no** | ✅ yes | ✅ yes
267
+ # Preserves lambda-ness | ⚠️ manually | 🚫 **no** | 🚫 **no** | ✅ yes
268
+ # Works on JRuby and TruffleRuby | 🚫 **no** | ✅ yes | ✅ yes | ✅ yes
269
+ # Performance overhead | ✅ none | ⚠️ up to 200% | ⚠️ up to 80% | ✅ none
270
+ #
271
+ # If you want the proc to also be shareable, use {Ractor.shareable_proc} instead.
272
+ #
273
+ # The performance overhead of `BasicObject#instance_exec`/`BasicObject#instance_eval` is the most significant on the
274
+ # official Ruby implementation, but is still present on JRuby, which does not exhibit a performance penalty for
275
+ # `Module#define_method`. On TruffleRuby, all approaches have similar performance. The more work is done inside
276
+ # the proc, the less significant the overhead becomes.
277
+ #
278
+ # @example
279
+ # # Rebinding a block to a new self
280
+ # callback = Farce.rebind(self: 42) { self * 10 }
281
+ # callback.call # => 420
282
+ #
283
+ # # Rebound procs preserve parameters and can accept blocks
284
+ # block = ->(key, &fallback) { fetch(key, &fallback) }
285
+ # rebound = Farce.rebind(block, self: { a: 1, b: 2 })
286
+ # rebound.call(:a) { 0 } # => 1
287
+ # rebound.call(:c) { 0 } # => 0
288
+ # rebound.parameters == block.parameters # => true
289
+ #
290
+ # # Rebinding an unbound method to a new self returns a bound method
291
+ # method = Object.instance_method(:inspect) # => #<UnboundMethod>
292
+ # method = Farce.rebind(method, self: 42) # => #<Method>
293
+ # method.receiver # => 42
294
+ # method.call # => "42"
295
+ #
296
+ # # lambda-ness is preserved
297
+ # Farce.rebind(proc {}).lambda? # => false
298
+ # Farce.rebind(lambda {}).lambda? # => true
299
+ #
300
+ # # If there is no binding change, identity is preserved
301
+ # Farce.rebind(&:to_s) == :to_s.to_proc # => true
302
+ #
303
+ # @overload rebind(self: nil, lambda: nil)
304
+ # @yield The block to bind to the new self.
305
+ # @yieldreceiver [BasicObject] The object passed as `self` (or `nil`)
306
+ # @param self [BasicObject] The new self to bind to.
307
+ # @return [Proc] The bound proc.
308
+ #
309
+ # @overload rebind(bindable, self: nil, lambda: nil)
310
+ # @param bindable [Proc, Method, UnboundMethod] The proc or method to bind to the new self.
311
+ # @param self [Object] The new self to bind to.
312
+ # @return [Proc, Method]
313
+ # The bound proc or method.
314
+ # A method is returned if the argument was a Method or UnboundMethod, otherwise a Proc is returned.
315
+ #
316
+ # @!macro rebind_lambda
317
+ # @param lambda [Boolean, nil]
318
+ # If true, a block-based proc will be converted to a lambda. If false to a non-lambda proc.
319
+ # If nil, the original lambda-ness will be preserved.
320
+ # Ignored for methods or procs not based on blocks (like `Symbol#to_proc`).
321
+ #
322
+ # @return [Proc, Method] The bound proc or method.
323
+ def self.rebind(bindable = nil, lambda: nil, **self_option, &block)
324
+ raise ArgumentError, "more than one block given" if bindable && block
325
+ raise ArgumentError, "tried to create Proc object without a block" unless bindable ||= block
326
+ new_self = Internal.self_option(self_option)
327
+
328
+ if bindable.is_a? Proc
329
+ return bindable if !Internal.rebindable?(bindable) || bindable.binding.receiver.equal?(new_self)
330
+ rebound = Internal.rebind(bindable, new_self, lambda)
331
+ rebound.freeze if bindable.frozen?
332
+ return rebound
333
+ end
334
+
335
+ if bindable.is_a? Method
336
+ return bindable if bindable.receiver.equal?(new_self)
337
+ bindable = bindable.unbind
338
+ end
339
+
340
+ return bindable.bind(new_self) if bindable.is_a? UnboundMethod
341
+ raise ArgumentError, "invalid bindable: #{bindable.inspect}"
342
+ end
343
+
344
+ # Allows executing code on the main ractor from other ractors.
345
+ # This allows modifying objects and calling methods only accessible from the main ractor.
346
+ #
347
+ # If a block is given, executes it on the main ractor, passing any given arguments to it.
348
+ # Blocks the current thread until the block has finished executing.
349
+ #
350
+ # Arguments are transferred based on the given mode.
351
+ #
352
+ # @!macro modes
353
+ #
354
+ # Calls from the main ractor execute directly, preserving the block and arguments.
355
+ #
356
+ # If no block is given, it returns a scheduler to execute tasks on the main ractor.
357
+ # This allows scheduling without blocking the current thread.
358
+ #
359
+ # On JRuby and TruffleRuby, blocks run inline because there is no native Ractor isolation.
360
+ # The returned {ThreadScheduler} starts a new thread for each scheduled task.
361
+ #
362
+ # @example
363
+ # $results = []
364
+ #
365
+ # Farce::Ractor.new do
366
+ # # maybe computing this string on the main ractor is too expensive?
367
+ # my_string = "foo bar baz"
368
+ #
369
+ # # can't access $results on the current ractor directly, as it isn't shareable
370
+ # Farce.on_main(my_string) { $results << it }
371
+ # end.join
372
+ #
373
+ # $results # => ["foo bar baz"]
374
+ #
375
+ # @example Blocking vs non-blocking
376
+ # Farce::Ractor.new do
377
+ # # This blocks the current thread until the block has finished executing.
378
+ # Farce.on_main do
379
+ # sleep 1
380
+ # puts "Hi from the main ractor!"
381
+ # end
382
+ #
383
+ # # This does not block the current thread.
384
+ # Farce.on_main.schedule do
385
+ # sleep 1
386
+ # puts "Hi again from the main ractor!"
387
+ # end
388
+ #
389
+ # puts "Hi from the current ractor!"
390
+ # end
391
+ #
392
+ # sleep 3 # Wait for all scheduled tasks to complete.
393
+ #
394
+ # # Expected output:
395
+ # # Hi from the main ractor!
396
+ # # Hi from the current ractor!
397
+ # # Hi again from the main ractor!
398
+ #
399
+ # @overload on_main(*args, mode: :copy)
400
+ # @param args [Array] The arguments to be passed to the block.
401
+ # @param mode [Symbol] The argument transfer mode when called from another ractor.
402
+ # @yield [*args] The block to be executed on the main ractor.
403
+ # @yieldparam [*args] The arguments passed to the block.
404
+ # @return [nil]
405
+ #
406
+ # @overload on_main
407
+ # Returns a scheduler that executes tasks on the main ractor.
408
+ # @return [Abstract::Scheduler]
409
+ #
410
+ # @return [Abstract::Scheduler, nil]
411
+ # @see .in_parallel
412
+ def self.on_main(*args, mode: UNDEFINED, &)
413
+ unless block_given?
414
+ raise LocalJumpError, "no block given" unless args.empty? && UNDEFINED.equal?(mode)
415
+ return Internal::MainScheduler
416
+ end
417
+
418
+ if Ractor.main?
419
+ yield(*args)
420
+ else
421
+ mode = :copy if UNDEFINED.equal?(mode)
422
+ Internal::MainScheduler.execute(*args, mode:, auto_local: false, &)
423
+ end
424
+
425
+ nil
426
+ end
427
+
428
+ # Schedules work without waiting for the block to finish.
429
+ # On CRuby, uses a shared Ractor pool with at most {System.cpu_count} workers.
430
+ # On JRuby and TruffleRuby, starts a new thread for each task.
431
+ #
432
+ # Without a block, returns the shared scheduler. The CRuby pool starts workers
433
+ # when tasks arrive. Pass mutable task data as arguments so it can be transferred based on the given mode.
434
+ #
435
+ # @!macro modes
436
+ #
437
+ # @example
438
+ # Farce.in_parallel("hello") { |message| puts message.upcase }
439
+ # Farce.in_parallel.schedule { puts "another task" }
440
+ #
441
+ # @overload in_parallel(*args, mode: :copy)
442
+ # @param args [Array<Object>] Arguments passed to the block.
443
+ # @param mode [Symbol] Argument transfer mode. Ignored on JRuby and TruffleRuby.
444
+ # @yield [*args] The task to schedule.
445
+ # @return [nil]
446
+ #
447
+ # @overload in_parallel
448
+ # @return [Abstract::Scheduler] The shared scheduler.
449
+ #
450
+ # @return [Abstract::Scheduler, nil]
451
+ # @see .on_main
452
+ def self.in_parallel(*args, mode: UNDEFINED, &)
453
+ unless block_given?
454
+ raise LocalJumpError, "no block given" unless args.empty? && UNDEFINED.equal?(mode)
455
+ return Internal::ParallelScheduler
456
+ end
457
+
458
+ mode = :copy if UNDEFINED.equal?(mode)
459
+ Internal::ParallelScheduler.schedule(*args, mode:, &)
460
+ nil
461
+ end
462
+
463
+ # @overload schedule(*args, mode: :copy, auto_local: true, **kwargs)
464
+ # Schedules work to be executed out of band.
465
+ #
466
+ # If the `mode` is set to `:local`, it will use the current thread's fiber scheduler to schedule the task.
467
+ # If no fiber scheduler is available, it will create or reuse a Ractor-local scheduler (on the main Ractor,
468
+ # this is the same scheduler as {.on_main} uses).
469
+ #
470
+ # If `auto_local` is set to `true` (but with a different `mode`), the same logic is used as in local mode, except
471
+ # it will not create a new ractor-local scheduler if one is not already available.
472
+ #
473
+ # @example Scheduling work
474
+ # # just run this asynchronously, don't care how
475
+ # Farce.schedule("hello") { |message| puts message.upcase }
476
+ #
477
+ # @example Using a fiber scheduler
478
+ # Async do
479
+ # # this is basically the same as calling Async { do_something }
480
+ # Farce.schedule { do_something }
481
+ # end
482
+ #
483
+ # @param args [Array<Object>] Arguments passed to the block.
484
+ # @param mode [Symbol] Argument transfer mode. Ignored on JRuby and TruffleRuby.
485
+ # @param auto_local [Boolean] Whether to automatically use the local scheduler if available.
486
+ # @param kwargs [Hash] Additional keyword arguments passed to the scheduler.
487
+ # @yield [*args] The task to schedule.
488
+ # @return [nil]
489
+ def self.schedule(*, mode: :copy, auto_local: true, **, &)
490
+ raise LocalJumpError, "no block given" unless block_given?
491
+
492
+ if auto_local || mode == :local
493
+ if Fiber.respond_to?(:scheduler) && fiber_scheduler = Fiber.scheduler
494
+ fiber_scheduler.fiber(**) { yield(*) }
495
+ return
496
+ end
497
+ scheduler = Ractor.main? ? Internal::MainScheduler : Internal::Storage[:local_scheduler]
498
+ end
499
+
500
+ scheduler ||=
501
+ if mode == :local
502
+ Internal::Storage.store_if_absent(:local_scheduler) do
503
+ Internal.native_ractors? ? Scheduler.create(Thread) : ThreadScheduler.new
504
+ end
505
+ else
506
+ Internal::ParallelScheduler
507
+ end
508
+
509
+ scheduler.schedule(*, mode:, auto_local:, **, &)
510
+ nil
511
+ end
512
+
513
+ def self.append_features(mod) = Internal::Mixin.__send__(:append_features, mod)
514
+ def self.included(mod) = Internal::Mixin.__send__(:included, mod)
515
+ private_class_method :append_features, :included
516
+
517
+ Integrations.setup
518
+ Internal.finalize_engine
519
+ end