farce 0.0.1.alpha2-x86-linux-musl

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,536 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable task scheduler.
7
+ #
8
+ # Tasks run as fibers using a fiber scheduler, or as threads when the Ruby implementation
9
+ # doesn't support Fiber schedulers (ie TruffleRuby).
10
+ #
11
+ # If supports any existing Fiber scheduler, including Async and CarbonFiber, but
12
+ # can also be used as a Fiber scheduler itself.
13
+ #
14
+ # Use {.create} to start a worker, or construct an instance and install it with
15
+ # `Fiber.set_scheduler` on the current thread. A scheduler can only be launched
16
+ # or installed once.
17
+ #
18
+ # It will keep the fiber scheduler alive until it is explicitly closed.
19
+ # Closing a scheduler stops new submissions through {#schedule}. The underlying
20
+ # fiber scheduler has its own lifecycle: draining fibers may still spawn child
21
+ # fibers with `Fiber.schedule` while it is closing.
22
+ #
23
+ # @example Submit arguments to a worker
24
+ # # Runs a scheduler in a background Ractor/Thread
25
+ # scheduler = Farce::Scheduler.create
26
+ # scheduler.schedule("hello") { |message| puts message }
27
+ #
28
+ # @example Using Farce::Scheduler as a Fiber scheduler
29
+ # scheduler = Farce::Scheduler.new
30
+ # Fiber.set_scheduler(scheduler)
31
+ # Fiber.schedule { puts "Hello from the scheduler!" }
32
+ class Scheduler < Farce::Abstract::Scheduler
33
+ include Shareable::Unfreezable
34
+ include Internal::Inspect
35
+
36
+ CLOSED_STATES = ::Set[:closed, :closing, :error].freeze
37
+ private_constant :CLOSED_STATES
38
+
39
+ # Returns a Farce scheduler for the current thread's installed fiber scheduler.
40
+ # Returns `nil` when no fiber scheduler is installed.
41
+ #
42
+ # Creates and installs a wrapper when necessary, starting its dispatcher on
43
+ # that scheduler. Returns `nil` when no fiber scheduler is installed.
44
+ #
45
+ # Additional keyword arguments are forwarded to {#initialize} when wrapping.
46
+ #
47
+ # @example Create a scheduler for Async, and send it to a different Ractor
48
+ # require "async"
49
+ # require "farce"
50
+ #
51
+ # # No fiber scheduler set here
52
+ # Farce::Scheduler.current # => nil
53
+ #
54
+ # Async do
55
+ # # A scheduler that will hand tasks to Async::Scheduler
56
+ # scheduler = Farce::Scheduler.current # => #<Farce::Scheduler>
57
+ #
58
+ # # This looks like doing the same as nested Async { ... } calls or
59
+ # # Fiber.schedule { ... }, …
60
+ # scheduler.schedule { puts "Scheduled from the main Ractor" }
61
+ #
62
+ # # … but it can be called from other threads and ractors
63
+ # Ractor.new(scheduler) do |scheduler|
64
+ # scheduler.schedule { puts "Scheduled from a background Ractor" }
65
+ # end
66
+ # end
67
+ #
68
+ # @example Full circle
69
+ # scheduler = Farce::Scheduler.new
70
+ # Farce::Scheduler.current # => nil
71
+ # Fiber.set_scheduler(scheduler)
72
+ # Farce::Scheduler.current == scheduler # => true
73
+ # @return [Scheduler, nil]
74
+ def self.current(**)
75
+ return unless scheduler = Fiber.scheduler
76
+ register = Internal::Storage.store_if_absent(self) { Internal::Storage.new }
77
+ register.store_if_absent(scheduler) do
78
+ wrapper = new(**, external: true)
79
+ wrapper.__send__(:set_scheduler!, scheduler:)
80
+ wrapper
81
+ end
82
+ end
83
+
84
+ # Creates a new scheduler and starts it in a background ractor, thread, or custom executor.
85
+ #
86
+ # @overload create(executor = nil, name: nil, priority: nil, capacity: 1024, backend: Farce.config.io_backend)
87
+ # Runs tasks on the configured fiber scheduler. The backend option selects
88
+ # the IO driver when using Farce's built-in scheduler.
89
+ #
90
+ # @example
91
+ # scheduler = Farce::Scheduler.create
92
+ # scheduler.schedule { puts "Running on the built-in scheduler" }
93
+ #
94
+ # @param executor [Class, nil] Ractor or Thread, nil selects the runtime default.
95
+ # @param name [String, nil] Optional worker name.
96
+ # @param priority [Integer, nil] Optional worker thread priority.
97
+ # @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
98
+ # @param backend [Symbol] IO backend for the built-in scheduler, defaults to Farce configuration.
99
+ # @return [Scheduler]
100
+ #
101
+ # @overload create(executor = nil, name: nil, priority: nil, capacity: 1024, &block)
102
+ # Runs tasks on a fiber scheduler supplied by your block. Use this to send
103
+ # work through Farce to another library's event loop, such as Async.
104
+ #
105
+ # Construct and return the scheduler inside the block: Farce calls it once
106
+ # in the worker, where that event loop will run, and installs the result.
107
+ # The block configures the scheduler. Submit actual tasks with {#schedule}.
108
+ # Configure any IO backend on the returned scheduler itself. Farce's backend
109
+ # option is ignored when a block is supplied.
110
+ #
111
+ # The block must be Ractor-shareable. Require the library before calling
112
+ # create and avoid capturing a scheduler created on another thread or Ractor.
113
+ #
114
+ # @example Run tasks on Async in a worker thread
115
+ # require "async"
116
+ # require "farce"
117
+ #
118
+ # # Using Thread as an executor as at the moment Async cannot run on a non-main Ractor
119
+ # scheduler = Farce::Scheduler.create(Thread) { Async::Scheduler.new }
120
+ # scheduler.schedule { puts "Running on Async" }
121
+ #
122
+ # @example Run tasks on CarbonFiber in a worker ractor
123
+ # require "carbon_fiber"
124
+ # require "farce"
125
+ #
126
+ # scheduler = Farce::Scheduler.create(Ractor) { CarbonFiber::Scheduler.new }
127
+ # scheduler.schedule { puts "Running on CarbonFiber" }
128
+ #
129
+ # @param executor [Class, nil]
130
+ # Ractor or Thread. `nil` selects the runtime default.
131
+ # Choose an executor supported by the supplied scheduler and its dependencies.
132
+ # @param name [String, nil] Optional worker name.
133
+ # @param priority [Integer, nil] Optional worker thread priority.
134
+ # @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
135
+ # @yieldreturn [Object] The fiber scheduler to install in the worker.
136
+ # @return [Scheduler]
137
+ # @see #launch
138
+ def self.create(executor = nil, name: nil, priority: nil, **, &)
139
+ new(**, &).tap { it.launch(executor, name:, priority:) }
140
+ end
141
+
142
+ # Creates a scheduler without starting a worker. Use this instead of {.create}
143
+ # when you want to launch it later with {#launch}, or install it on the current
144
+ # thread with `Fiber.set_scheduler(scheduler)`.
145
+ #
146
+ # @overload initialize(capacity: 1024, backend: Farce.config.io_backend)
147
+ # Uses the configured fiber scheduler when launched or installed.
148
+ #
149
+ # @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
150
+ # @param backend [Symbol] IO backend for the built-in scheduler, defaults to Farce configuration.
151
+ #
152
+ # @overload initialize(capacity: 1024, &block)
153
+ # Uses another library's fiber scheduler while retaining Farce's ability to
154
+ # accept tasks from other threads and Ractors.
155
+ #
156
+ # Supply a block that constructs and returns that scheduler. The block runs
157
+ # once when this handle is launched or installed, in the thread that will
158
+ # run the tasks, rather than during new. It must be Ractor-shareable.
159
+ # Farce installs its result. Submit tasks separately with {#schedule}.
160
+ # Configure the supplied scheduler in the block. Farce's backend option is
161
+ # ignored for this overload.
162
+ #
163
+ # @example Choose Async, then launch the worker later
164
+ # require "async"
165
+ # require "farce"
166
+ #
167
+ # scheduler = Farce::Scheduler.new { Async::Scheduler.new }
168
+ # worker = scheduler.launch_thread
169
+ # scheduler.schedule { puts "Running on Async" }
170
+ #
171
+ # @param capacity [Integer, nil] Pending-task capacity, nil creates an unbounded queue.
172
+ # @yieldreturn [Object] The fiber scheduler to install when launched or installed.
173
+ def initialize(capacity: 1024, backend: Internal::FROZEN_CONFIG.io_backend, queue: nil, external: false,
174
+ pool_worker: nil, queue_owner: true, &constructor)
175
+ constructor ||= Internal::FROZEN_CONFIG.fiber_scheduler_constructor
176
+ @capacity = capacity ? Integer(capacity) : nil
177
+ @backend = backend
178
+ @constructor = Ractor.shareable?(constructor) ? constructor : Ractor.shareable_proc(&constructor) if constructor
179
+ @external = external || !constructor.nil?
180
+ @owner = Internal::Atom.new(nil)
181
+ @state = Internal::Atom.new(:initialized)
182
+ @error = Internal::Atom.new
183
+ @queue = queue || Internal::Queue.new(capacity: @capacity)
184
+ @pool_worker = pool_worker
185
+ @queue_owner = queue_owner
186
+ super()
187
+ end
188
+
189
+ # @return [Exception, nil] The error encountered by the scheduler, if any.
190
+ def error
191
+ value = @error.value
192
+ value.is_a?(Envelope) ? value.value : value
193
+ end
194
+
195
+ # @overload launch_ractor(name: nil, priority: nil)
196
+ # Launches a Ractor worker. Accepts the keyword arguments of {#launch}.
197
+ # @param name [String, nil] Optional worker name.
198
+ # @param priority [Integer, nil] Optional worker thread priority.
199
+ # @return [Ractor] The worker.
200
+ def launch_ractor(...) = launch(Ractor, ...)
201
+
202
+ # @overload launch_thread(name: nil, priority: nil)
203
+ # Launches a Thread worker. Accepts the keyword arguments of {#launch}.
204
+ # @param name [String, nil] Optional worker name.
205
+ # @param priority [Integer, nil] Optional worker thread priority.
206
+ # @return [Thread] The worker.
207
+ def launch_thread(...) = launch(Thread, ...)
208
+
209
+ # Starts dispatching tasks in a new worker and waits for setup to leave its
210
+ # launching/setup states before returning. Returns the created worker instance.
211
+ #
212
+ # @example Using the return value to assign a ThreadGroup
213
+ # scheduler = Farce::Scheduler.new
214
+ # worker = scheduler.launch_thread
215
+ # group = ThreadGroup.new
216
+ #
217
+ # group.add(worker)
218
+ # group.enclose
219
+ #
220
+ # @example Using a custom worker
221
+ # class MyWorker
222
+ # def initialize(...)
223
+ # # flip a coin whether we're using Ractor or Thread
224
+ # @worker = [Ractor, Thread].sample.new(...)
225
+ # end
226
+ #
227
+ # def join = @worker.join
228
+ # end
229
+ #
230
+ # scheduler = Farce::Scheduler.new
231
+ # scheduler.launch(MyWorker)
232
+ #
233
+ # @param executor [Class, nil]
234
+ # Ractor or Thread. Defaults to Ractor when native Ractors are available, otherwise Thread.
235
+ # @param name [String, nil] Optional worker name.
236
+ # @param priority [Integer, nil] Optional worker thread priority.
237
+ # @return [Ractor, Thread, Object] The worker.
238
+ # @raise [RuntimeError] If this handle has already been launched, installed, or closed.
239
+ def launch(executor = nil, name: nil, priority: nil)
240
+ raise "Scheduler may not be reused" unless @state.compare_and_set(:initialized, :launching)
241
+
242
+ executor ||= Internal.native_ractors? ? Ractor : Thread
243
+ priority &&= Integer(priority)
244
+ name &&= -String(name)
245
+ callback = Ractor.shareable_proc(self: self) { _1.__send__(:launch!, _3, _2) }
246
+
247
+ return executor.new(self, priority, name, &callback) unless executor <= Ractor
248
+ # Requiring an autoloaded implementation from a new Ractor can deadlock
249
+ # while its launcher waits for setup to finish.
250
+ Internal.const_get(:FiberScheduler, false)
251
+ Internal.const_get(:Storage, false)
252
+ Internal.const_get(:ThreadPool, false)
253
+ executor.new(self, priority, name, name:, &callback)
254
+ rescue Exception => e # rubocop:disable Lint/RescueException
255
+ @state.value = :error
256
+ @error.value = Envelope.new(e)
257
+ raise
258
+ ensure
259
+ # if we return the thread too early, someone could kill it while another threads blocks on a schedule call
260
+ if System.windows? && executor <= Ractor
261
+ sleep 0.001 while @state.value == :launching
262
+ sleep 0.001 while @state.value == :setup
263
+ else
264
+ @state.wait_until_changed(:launching)
265
+ @state.wait_until_changed(:setup)
266
+ end
267
+ end
268
+
269
+ # @return [Ractor, nil] The owning Ractor, or nil before ownership is assigned.
270
+ def owner = @owner.value
271
+
272
+ # Assigns ownership during setup. An existing owner cannot be changed.
273
+ # This is used by schedule to determine if a task is scheduled locally or remotely.
274
+ #
275
+ # If {#schedule} is called without setting `auto_local` to false, it will block until the owner can be determined.
276
+ # Setting the owner explicitly avoids blocking on ownership determination.
277
+ #
278
+ # @example
279
+ # scheduler = Farce::Scheduler.new
280
+ #
281
+ # # Scheduler hasn't been launched, so it could block.
282
+ # scheduler.owner = Ractor.current
283
+ #
284
+ # # Now this doesn't block
285
+ # scheduler.schedule { some_task }
286
+ #
287
+ # # Can use #launch_thread, but #launch_ractor would fail now that the owner is set.
288
+ # scheduler.launch_thread
289
+ #
290
+ # @param value [Ractor, nil]
291
+ # @raise [ArgumentError] If value is not a Ractor/nil or changes an assigned owner.
292
+ def owner=(value)
293
+ raise ArgumentError, "owner must be a Ractor" unless value.nil? || value.is_a?(Ractor)
294
+ return if @owner.compare_and_set(nil, value)
295
+ return if owner == value
296
+ raise ArgumentError, "owner has already been set"
297
+ end
298
+
299
+ # @return [Boolean] Whether a fiber scheduler constructor was supplied.
300
+ def wraps_external? = @external
301
+
302
+ # Retrieves the constructed external scheduler from its owning Ractor.
303
+ # @return [Object, nil]
304
+ # The external scheduler, or nil before setup or when using the built-in scheduler.
305
+ # @raise [Ractor::IsolationError]
306
+ # If called outside the owning Ractor for an external scheduler.
307
+ def external_scheduler
308
+ return unless wraps_external?
309
+ return Internal::Storage[self] if owner.nil? || owner == Ractor.current
310
+ raise Ractor::IsolationError, "external_scheduler is owned by a different Ractor"
311
+ end
312
+
313
+ # Enqueues a task, waiting for queue space if necessary. Returns after
314
+ # submission. It does not wait for task completion or return the task's value.
315
+ #
316
+ # With `auto_local` enabled, waits for ownership to be assigned and uses `:local`
317
+ # mode whenever the caller and owner are in the same Ractor, including different
318
+ # threads in that Ractor.
319
+ #
320
+ # Local tasks retain their block and arguments.
321
+ #
322
+ # Otherwise, the block must be convertible to a Ractor-shareable proc.
323
+ # Pass task data as arguments so the selected transfer mode can be applied to it.
324
+ #
325
+ # @!macro modes
326
+ #
327
+ # @param args [Array<Object>] Positional arguments passed to the task block.
328
+ # @param mode [Symbol]
329
+ # Argument transfer mode: `:copy`, `:move`, `:local`, `:make_shareable`, `:shareable_copy`, or `:raise`.
330
+ # See {ModeManager#wrap}.
331
+ # @param auto_local [Boolean] Whether to override mode with `:local` in the owning Ractor.
332
+ # @yield [*args] The task to execute.
333
+ # @return [Scheduler] self.
334
+ # @raise [SchedulerClosedError] If the handle is closing, closed, or in an error state.
335
+ # @see Ractor.shareable_proc
336
+ def schedule(*args, mode: :copy, auto_local: true, &block)
337
+ raise SchedulerClosedError, "cannot schedule task on a closed scheduler" if closed?
338
+
339
+ if auto_local
340
+ owner = @owner.wait_until_non_nil
341
+ raise SchedulerClosedError, "cannot schedule task on a closed scheduler" if closed?
342
+ mode = :local if owner == Ractor.current
343
+ elsif mode == :local && (owner = @owner.value) && owner != Ractor.current
344
+ raise Ractor::IsolationError, "cannot schedule local task outside the owning Ractor"
345
+ end
346
+
347
+ task = mode == :local ?
348
+ Envelope::Local.new(args.any? ? -> { block.call(*args) } : block) :
349
+ Internal::ScheduledTask.new(args, block, mode)
350
+
351
+ @queue.push(task)
352
+ self
353
+ rescue ClosedQueueError
354
+ raise unless closed?
355
+ raise SchedulerClosedError, "cannot schedule task on a closed scheduler"
356
+ end
357
+
358
+ # (see Farce::Abstract::Scheduler#local?)
359
+ def local?(wait: true)
360
+ owner = wait ? @owner.wait_until_non_nil : @owner.value
361
+ owner == Ractor.current
362
+ end
363
+
364
+ # Requests shutdown and rejects further submissions. Repeated calls are harmless.
365
+ # The dispatcher drains queued tasks before closing the queue. This method
366
+ # does not wait for dispatch or running tasks to finish.
367
+ #
368
+ # This wakes a dispatcher blocked on an empty queue. It does not directly
369
+ # close the underlying fiber scheduler.
370
+ # @return [Scheduler] self.
371
+ def close
372
+ until closed?
373
+ state = @state.value
374
+ @state.compare_and_set(state, :closing) unless CLOSED_STATES.include?(state)
375
+ end
376
+ # if the scheduler never acquired an owner, #schedule might be blocked
377
+ @owner.compare_and_set(nil, Ractor.current)
378
+ wake_dispatcher
379
+ self
380
+ end
381
+
382
+ # @return [Symbol]
383
+ # The current state of the scheduler. One of:
384
+ # * `:initialized`: The scheduler has been created but not yet launched.
385
+ # * `:launching`: The scheduler is in the process of being {#launch launched} on a background worker.
386
+ # * `:setup`: The scheduler is in the process of being setting up a fiber scheduler.
387
+ # * `:running`: The scheduler is actively dispatching tasks.
388
+ # * `:closing`: The scheduler has been requested to shut down and is draining its queue.
389
+ # * `:closed`: The scheduler has finished shutting down.
390
+ # * `:error`: The scheduler encountered an error during dispatch.
391
+ def state = @state.value
392
+
393
+ # @return [Boolean]
394
+ # Whether new submissions are rejected, including while
395
+ # closing or after a dispatch error.
396
+ # Does not imply all tasks have finished.
397
+ # @see #state
398
+ def closed? = CLOSED_STATES.include?(@state.value)
399
+
400
+ # @return [Boolean] Whether the dispatcher is in its `:running` state.
401
+ # @see #state
402
+ def alive? = @state.value == :running
403
+
404
+ # @api private
405
+ def inspect_with(inspector)
406
+ super do
407
+ external = Internal::Storage[self]
408
+ inspector.attribute(:state, state)
409
+ inspector.attribute(:backend, @backend)
410
+ inspector.attribute(:external_scheduler, external) if external && !external.is_a?(Internal::FiberScheduler)
411
+ inspector.attribute(:local, local?) if @owner.value
412
+ yield if block_given?
413
+ end
414
+ end
415
+
416
+ private
417
+
418
+ def set_scheduler!(expected_state = :initialized, scheduler: nil)
419
+ setup_owner(expected_state)
420
+ scheduler ||= @constructor&.call || Internal::FiberScheduler.new(backend: @backend)
421
+ register = Internal::Storage.store_if_absent(self.class) { Internal::Storage.new }
422
+ register[scheduler] = self
423
+ Internal::Storage[self] = scheduler
424
+ Fiber.set_scheduler(scheduler) unless Fiber.scheduler.equal?(scheduler)
425
+ runnable_count = :farce_runnable_count if @pool_worker && scheduler.respond_to?(:farce_runnable_count)
426
+ scheduler.fiber do
427
+ dispatch(scheduler, runnable_count) do |task|
428
+ if @pool_worker
429
+ scheduler.fiber(&task.to_proc(@pool_worker))
430
+ else
431
+ scheduler.fiber(&task)
432
+ end
433
+ end
434
+ end
435
+ end
436
+
437
+ def launch!(name, priority)
438
+ Thread.current.name = name if name
439
+ Thread.current.priority = priority if priority
440
+
441
+ if Fiber.respond_to?(:set_scheduler)
442
+ begin
443
+ set_scheduler!(:launching)
444
+ ensure
445
+ Fiber.set_scheduler(nil) if Fiber.scheduler
446
+ end
447
+ else
448
+ setup_owner(:launching)
449
+ group = ThreadGroup.new
450
+ group.add(Thread.current)
451
+
452
+ dispatch do |task|
453
+ callback = @pool_worker ? task.to_proc(@pool_worker) : task.to_proc
454
+ group.add(Thread.new(&callback))
455
+ end
456
+ group.list.each { it.join unless it.equal?(Thread.current) }
457
+ end
458
+ end
459
+
460
+ def setup_owner(expected_state)
461
+ raise "Scheduler may not be reused" unless @state.compare_and_set(expected_state, :setup)
462
+ self.owner = Ractor.current
463
+ end
464
+
465
+ def wake_dispatcher
466
+ # A full queue already guarantees that the dispatcher will wake and observe the closing state.
467
+ @queue.try_push(nil)
468
+ rescue ClosedQueueError
469
+ nil
470
+ end
471
+
472
+ def dispatch(scheduler = nil, runnable_count = nil)
473
+ failure = nil
474
+ @state.compare_and_set(:setup, :running)
475
+ while keep_processing?
476
+ break if @pool_worker && !@pool_worker.wait_for_admission(scheduler, runnable_count)
477
+ timed_out = false
478
+ timeout = @pool_worker&.idle_timeout
479
+ task = @queue.pop(timeout:) { timed_out = true }
480
+ if timed_out
481
+ break if @pool_worker&.retire?
482
+ next
483
+ end
484
+ next unless task
485
+ task = task.value if task.is_a?(Envelope)
486
+ if task
487
+ @pool_worker&.task_started
488
+ yield task
489
+ end
490
+ end
491
+ @queue.close if @queue_owner
492
+ rescue Internal::SchedulerLifecycle::Cancelled => e
493
+ # Shutdown cancellation must not create envelope ports in a terminating Ractor.
494
+ failure = e
495
+ raise
496
+ rescue ClosedQueueError
497
+ raise unless @pool_worker
498
+ rescue Exception => e # rubocop:disable Lint/RescueException
499
+ failure = e
500
+ @state.value = :error
501
+ @error.value = Envelope.new(e)
502
+ raise
503
+ ensure
504
+ @pool_worker&.stopped(failure)
505
+ # make sure we're in a closed state
506
+ unless @state.compare_and_set(:closing, :closed)
507
+ state = @state.value
508
+ @state.compare_and_set(state, :closed) unless CLOSED_STATES.include?(state)
509
+ end
510
+ end
511
+
512
+ def next_task
513
+ task = @queue.pop
514
+ return task unless task.is_a?(Envelope)
515
+ task.value
516
+ end
517
+
518
+ def keep_processing?
519
+ case @state.value
520
+ when :running, :setup then true
521
+ when :closing then @queue.size.positive?
522
+ else false
523
+ end
524
+ end
525
+ end
526
+
527
+ module FiberExtension
528
+ Fiber.singleton_class.prepend(self) if Fiber.respond_to?(:set_scheduler)
529
+ def set_scheduler(scheduler) # rubocop:disable Naming/AccessorMethodName
530
+ return super unless scheduler.is_a?(Farce::Scheduler)
531
+ scheduler.__send__(:set_scheduler!)
532
+ end
533
+ end
534
+
535
+ private_constant :FiberExtension
536
+ end
data/lib/farce/set.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable concurrent set whose elements support transfer modes.
7
+ # Membership uses a stable insertion-time hash and eql? snapshot. Later
8
+ # mutation of a local element does not change its membership key. Elements
9
+ # whose equality cannot be preserved by a shareable snapshot are rejected.
10
+ # Identity comparison supports shareable elements and the `:local`, `:move`,
11
+ # `:make_shareable`, and `:shareable_copy` modes. Copying a non-shareable
12
+ # identity element is unsupported. On runtimes without native Ractors,
13
+ # elements follow ordinary hash-key mutation rules.
14
+ #
15
+ # @example Track unique job identifiers
16
+ # jobs = Farce::Set[:compile, :test]
17
+ # jobs.add?(:publish) # => jobs
18
+ # jobs.add?(:test) # => nil
19
+ class Set < Farce::Abstract::Set
20
+ include Shareable::Delegated
21
+
22
+ # The default transfer mode for elements.
23
+ # @return [Symbol] The configured transfer mode.
24
+ def mode = @manager.mode
25
+
26
+ protected
27
+
28
+ def value_modes? = true
29
+
30
+ private
31
+
32
+ def initialize_value_mode(mode)
33
+ mode = :copy if UNDEFINED.equal?(mode)
34
+ @manager = ModeManager.new(mode:)
35
+ end
36
+
37
+ def new_map(...) = Farce::Strict::Map.new(...)
38
+ end
39
+ end