farce 0.0.1.alpha2-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +26 -0
  3. data/CONTRIBUTING.md +71 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +1524 -0
  6. data/SECURITY.md +10 -0
  7. data/docs/benchmarks.md +185 -0
  8. data/docs/gems/dry-types.md +290 -0
  9. data/docs/gems/msgpack.md +70 -0
  10. data/docs/gems/ractor-shim.md +63 -0
  11. data/docs/modes.md +628 -0
  12. data/docs/scopes.md +649 -0
  13. data/docs/variants.md +346 -0
  14. data/ext/ext_helper.rb +20 -0
  15. data/ext/farce/README.md +21 -0
  16. data/ext/farce/atom.c +1023 -0
  17. data/ext/farce/bounded_map.c +1683 -0
  18. data/ext/farce/containers.h +77 -0
  19. data/ext/farce/counter.c +399 -0
  20. data/ext/farce/darwin.c +100 -0
  21. data/ext/farce/depend +12 -0
  22. data/ext/farce/dict.c +1523 -0
  23. data/ext/farce/dict.h +152 -0
  24. data/ext/farce/drivers.c +237 -0
  25. data/ext/farce/exchanger.c +299 -0
  26. data/ext/farce/extconf.rb +99 -0
  27. data/ext/farce/farce.c +359 -0
  28. data/ext/farce/flag.c +288 -0
  29. data/ext/farce/io.c +338 -0
  30. data/ext/farce/lock.c +510 -0
  31. data/ext/farce/map.c +2240 -0
  32. data/ext/farce/priority_queue.c +2056 -0
  33. data/ext/farce/queue.c +1059 -0
  34. data/ext/farce/reactor.c +820 -0
  35. data/ext/farce/reactor.h +103 -0
  36. data/ext/farce/shareable.h +31 -0
  37. data/ext/farce/signal.c +350 -0
  38. data/ext/farce/transaction.c +354 -0
  39. data/ext/farce/transaction.h +40 -0
  40. data/ext/farce/tree_map.c +1953 -0
  41. data/ext/farce/trie.c +2020 -0
  42. data/ext/farce/unshareable.c +155 -0
  43. data/ext/farce/unshared_io_pool.h +234 -0
  44. data/ext/farce/unshared_signal.c +263 -0
  45. data/ext/farce/unshared_wait.h +193 -0
  46. data/ext/farce/unsupported.c +6 -0
  47. data/ext/farce/vector.c +1195 -0
  48. data/ext/farce/weak_map.c +1714 -0
  49. data/ext/java/org/farce/BoundedMap.java +394 -0
  50. data/ext/java/org/farce/FiberScheduler.java +141 -0
  51. data/ext/java/org/farce/PriorityKey.java +45 -0
  52. data/ext/java/org/farce/PriorityQueue.java +373 -0
  53. data/ext/java/org/farce/QueueSignal.java +28 -0
  54. data/ext/rebind/README.md +14 -0
  55. data/ext/rebind/extconf.rb +10 -0
  56. data/ext/rebind/rebind.c +186 -0
  57. data/lib/farce/_yard/internal.rb +13 -0
  58. data/lib/farce/_yard/macros.rb +61 -0
  59. data/lib/farce/_yard/ractor.rb +46 -0
  60. data/lib/farce/abstract/atom.rb +186 -0
  61. data/lib/farce/abstract/bounded_map.rb +232 -0
  62. data/lib/farce/abstract/collection.rb +151 -0
  63. data/lib/farce/abstract/concurrent_map.rb +381 -0
  64. data/lib/farce/abstract/counter.rb +193 -0
  65. data/lib/farce/abstract/duplicable_map.rb +229 -0
  66. data/lib/farce/abstract/exchanger.rb +26 -0
  67. data/lib/farce/abstract/flag.rb +71 -0
  68. data/lib/farce/abstract/lazy.rb +115 -0
  69. data/lib/farce/abstract/lease.rb +104 -0
  70. data/lib/farce/abstract/lease_map.rb +261 -0
  71. data/lib/farce/abstract/lease_pool.rb +91 -0
  72. data/lib/farce/abstract/lfu_map.rb +20 -0
  73. data/lib/farce/abstract/lru_map.rb +25 -0
  74. data/lib/farce/abstract/map.rb +345 -0
  75. data/lib/farce/abstract/molecule.rb +245 -0
  76. data/lib/farce/abstract/port.rb +74 -0
  77. data/lib/farce/abstract/priority_queue.rb +117 -0
  78. data/lib/farce/abstract/queue.rb +269 -0
  79. data/lib/farce/abstract/scheduler.rb +111 -0
  80. data/lib/farce/abstract/set.rb +910 -0
  81. data/lib/farce/abstract/sorted_set.rb +172 -0
  82. data/lib/farce/abstract/timer_queue.rb +136 -0
  83. data/lib/farce/abstract/tree_map.rb +269 -0
  84. data/lib/farce/abstract/value.rb +68 -0
  85. data/lib/farce/abstract/vector.rb +1126 -0
  86. data/lib/farce/abstract/weak_atom.rb +32 -0
  87. data/lib/farce/abstract/weak_key_map.rb +12 -0
  88. data/lib/farce/abstract/weak_map.rb +13 -0
  89. data/lib/farce/abstract/weak_set.rb +12 -0
  90. data/lib/farce/abstract/weak_value_map.rb +12 -0
  91. data/lib/farce/abstract.rb +17 -0
  92. data/lib/farce/atom.rb +293 -0
  93. data/lib/farce/class_mirror.rb +87 -0
  94. data/lib/farce/clock.rb +121 -0
  95. data/lib/farce/config.rb +229 -0
  96. data/lib/farce/counter.rb +71 -0
  97. data/lib/farce/deduper.rb +122 -0
  98. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  99. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  101. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  102. data/lib/farce/engine/jruby/map.rb +505 -0
  103. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  104. data/lib/farce/engine/jruby/signal.rb +147 -0
  105. data/lib/farce/engine/jruby.rb +63 -0
  106. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  107. data/lib/farce/engine/jvm/counter.rb +102 -0
  108. data/lib/farce/engine/jvm/extension.rb +32 -0
  109. data/lib/farce/engine/jvm/farce.jar +0 -0
  110. data/lib/farce/engine/jvm/flag.rb +79 -0
  111. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  112. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  113. data/lib/farce/engine/jvm/types.rb +180 -0
  114. data/lib/farce/engine/jvm.rb +19 -0
  115. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  116. data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
  117. data/lib/farce/engine/ruby/3.4/port.rb +186 -0
  118. data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
  119. data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
  120. data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
  121. data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
  122. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  123. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  124. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  125. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  126. data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
  127. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  128. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  129. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  130. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  131. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  132. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  133. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  134. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  135. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  136. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  137. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  138. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  139. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  140. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  141. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  142. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  143. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  144. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  145. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  146. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  147. data/lib/farce/engine/ruby.rb +130 -0
  148. data/lib/farce/engine/shared/atom.rb +10 -0
  149. data/lib/farce/engine/shared/exchanger.rb +130 -0
  150. data/lib/farce/engine/shared/identity_key.rb +22 -0
  151. data/lib/farce/engine/shared/lease.rb +10 -0
  152. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  153. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  154. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  155. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  156. data/lib/farce/engine/shared/port.rb +44 -0
  157. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  158. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  159. data/lib/farce/engine/shared/queue.rb +233 -0
  160. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  161. data/lib/farce/engine/shared/rebindable.rb +15 -0
  162. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  163. data/lib/farce/engine/shared/strict_map.rb +117 -0
  164. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  165. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  166. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  167. data/lib/farce/engine/shared/trie.rb +317 -0
  168. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  169. data/lib/farce/engine/shared/unshareable.rb +14 -0
  170. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  171. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  172. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  173. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  174. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  175. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  176. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  177. data/lib/farce/engine/shared/vault.rb +31 -0
  178. data/lib/farce/engine/shared/vector.rb +83 -0
  179. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  180. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  181. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  182. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  183. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  184. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  185. data/lib/farce/engine/shared.rb +63 -0
  186. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  187. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  188. data/lib/farce/engine/truffleruby/map.rb +656 -0
  189. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  190. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  191. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  192. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  193. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  194. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  195. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  196. data/lib/farce/engine/truffleruby.rb +148 -0
  197. data/lib/farce/envelope.rb +335 -0
  198. data/lib/farce/error.rb +35 -0
  199. data/lib/farce/exchanger.rb +41 -0
  200. data/lib/farce/flag.rb +27 -0
  201. data/lib/farce/integrations/active_support/blank.rb +68 -0
  202. data/lib/farce/integrations/active_support/clock.rb +17 -0
  203. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  204. data/lib/farce/integrations/active_support/map.rb +175 -0
  205. data/lib/farce/integrations/active_support/set.rb +60 -0
  206. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  207. data/lib/farce/integrations/active_support/vector.rb +231 -0
  208. data/lib/farce/integrations/active_support.rb +9 -0
  209. data/lib/farce/integrations/activesupport.rb +5 -0
  210. data/lib/farce/integrations/bson.rb +108 -0
  211. data/lib/farce/integrations/cbor.rb +51 -0
  212. data/lib/farce/integrations/concurrent.rb +112 -0
  213. data/lib/farce/integrations/dry-types.rb +5 -0
  214. data/lib/farce/integrations/dry_types.rb +694 -0
  215. data/lib/farce/integrations/json.rb +7 -0
  216. data/lib/farce/integrations/msgpack.rb +112 -0
  217. data/lib/farce/integrations/oj.rb +69 -0
  218. data/lib/farce/integrations/psych.rb +191 -0
  219. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  220. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  221. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  222. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  223. data/lib/farce/integrations/shared/to_json.rb +42 -0
  224. data/lib/farce/integrations/sorted_set.rb +11 -0
  225. data/lib/farce/integrations/weakref.rb +38 -0
  226. data/lib/farce/integrations/yajl.rb +10 -0
  227. data/lib/farce/integrations.rb +156 -0
  228. data/lib/farce/internal/_frozen_config.rb +11 -0
  229. data/lib/farce/internal/autoloads.rb +47 -0
  230. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  231. data/lib/farce/internal/converter.rb +106 -0
  232. data/lib/farce/internal/copyable.rb +31 -0
  233. data/lib/farce/internal/delegation.rb +20 -0
  234. data/lib/farce/internal/external_transaction.rb +59 -0
  235. data/lib/farce/internal/fake_ractor.rb +179 -0
  236. data/lib/farce/internal/freeze.rb +118 -0
  237. data/lib/farce/internal/inspect.rb +157 -0
  238. data/lib/farce/internal/key_lock_map.rb +29 -0
  239. data/lib/farce/internal/key_normalizer.rb +289 -0
  240. data/lib/farce/internal/lease_initialization.rb +115 -0
  241. data/lib/farce/internal/lease_map.rb +441 -0
  242. data/lib/farce/internal/lease_pool_state.rb +228 -0
  243. data/lib/farce/internal/lease_state.rb +342 -0
  244. data/lib/farce/internal/lease_waiting.rb +13 -0
  245. data/lib/farce/internal/managed_queue.rb +26 -0
  246. data/lib/farce/internal/map_value_modes.rb +227 -0
  247. data/lib/farce/internal/marshal_support.rb +227 -0
  248. data/lib/farce/internal/mixin.rb +20 -0
  249. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  250. data/lib/farce/internal/noncopyable.rb +17 -0
  251. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  252. data/lib/farce/internal/pool_supervisor.rb +41 -0
  253. data/lib/farce/internal/pool_worker.rb +61 -0
  254. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  255. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  256. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  257. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  258. data/lib/farce/internal/portable_transaction.rb +164 -0
  259. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  260. data/lib/farce/internal/reservation_waiting.rb +24 -0
  261. data/lib/farce/internal/scheduled_task.rb +48 -0
  262. data/lib/farce/internal/scheduler_io.rb +171 -0
  263. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  264. data/lib/farce/internal/select_scheduler.rb +235 -0
  265. data/lib/farce/internal/storage.rb +162 -0
  266. data/lib/farce/internal/strict_lease.rb +30 -0
  267. data/lib/farce/internal/strict_lease_map.rb +20 -0
  268. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  269. data/lib/farce/internal/thread_pool.rb +166 -0
  270. data/lib/farce/internal/transaction_conflict.rb +9 -0
  271. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  272. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  273. data/lib/farce/internal/undefined.rb +22 -0
  274. data/lib/farce/internal/unshared_lease.rb +29 -0
  275. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  276. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  277. data/lib/farce/internal/value_serialization.rb +13 -0
  278. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  279. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  280. data/lib/farce/internal.rb +129 -0
  281. data/lib/farce/lazy.rb +100 -0
  282. data/lib/farce/lazy_ref.rb +40 -0
  283. data/lib/farce/lease.rb +28 -0
  284. data/lib/farce/lease_map.rb +34 -0
  285. data/lib/farce/lease_pool.rb +29 -0
  286. data/lib/farce/lfu_map.rb +53 -0
  287. data/lib/farce/local/atom.rb +23 -0
  288. data/lib/farce/local/counter.rb +88 -0
  289. data/lib/farce/local/flag.rb +65 -0
  290. data/lib/farce/local/lazy.rb +38 -0
  291. data/lib/farce/local/lazy_ref.rb +36 -0
  292. data/lib/farce/local/lease.rb +77 -0
  293. data/lib/farce/local/lease_map.rb +80 -0
  294. data/lib/farce/local/lease_pool.rb +38 -0
  295. data/lib/farce/local/lfu_map.rb +55 -0
  296. data/lib/farce/local/lru_map.rb +62 -0
  297. data/lib/farce/local/map.rb +47 -0
  298. data/lib/farce/local/molecule.rb +45 -0
  299. data/lib/farce/local/priority_queue.rb +53 -0
  300. data/lib/farce/local/queue.rb +32 -0
  301. data/lib/farce/local/scoped.rb +166 -0
  302. data/lib/farce/local/set.rb +18 -0
  303. data/lib/farce/local/sorted_set.rb +50 -0
  304. data/lib/farce/local/timer_queue.rb +41 -0
  305. data/lib/farce/local/tree_map.rb +47 -0
  306. data/lib/farce/local/vector.rb +30 -0
  307. data/lib/farce/local/weak_atom.rb +23 -0
  308. data/lib/farce/local/weak_key_map.rb +39 -0
  309. data/lib/farce/local/weak_map.rb +39 -0
  310. data/lib/farce/local/weak_set.rb +18 -0
  311. data/lib/farce/local/weak_value_map.rb +39 -0
  312. data/lib/farce/local.rb +35 -0
  313. data/lib/farce/lock.rb +54 -0
  314. data/lib/farce/lru_map.rb +63 -0
  315. data/lib/farce/map.rb +58 -0
  316. data/lib/farce/mode_manager.rb +142 -0
  317. data/lib/farce/molecule.rb +60 -0
  318. data/lib/farce/mutable.rb +171 -0
  319. data/lib/farce/pool.rb +332 -0
  320. data/lib/farce/port.rb +134 -0
  321. data/lib/farce/priority_queue.rb +70 -0
  322. data/lib/farce/proxy/register.rb +101 -0
  323. data/lib/farce/proxy/supervisor.rb +137 -0
  324. data/lib/farce/proxy/wrapper.rb +49 -0
  325. data/lib/farce/proxy.rb +291 -0
  326. data/lib/farce/queue.rb +60 -0
  327. data/lib/farce/ractor.rb +269 -0
  328. data/lib/farce/read_write_lock.rb +256 -0
  329. data/lib/farce/reference.rb +158 -0
  330. data/lib/farce/resolv/dns.rb +42 -0
  331. data/lib/farce/resolv.rb +85 -0
  332. data/lib/farce/scheduler.rb +536 -0
  333. data/lib/farce/set.rb +39 -0
  334. data/lib/farce/shareable.rb +150 -0
  335. data/lib/farce/signal.rb +97 -0
  336. data/lib/farce/sorted_set.rb +36 -0
  337. data/lib/farce/strict/atom.rb +34 -0
  338. data/lib/farce/strict/counter.rb +10 -0
  339. data/lib/farce/strict/exchanger.rb +25 -0
  340. data/lib/farce/strict/flag.rb +10 -0
  341. data/lib/farce/strict/lazy.rb +26 -0
  342. data/lib/farce/strict/lazy_ref.rb +26 -0
  343. data/lib/farce/strict/lease.rb +27 -0
  344. data/lib/farce/strict/lease_map.rb +33 -0
  345. data/lib/farce/strict/lease_pool.rb +28 -0
  346. data/lib/farce/strict/lfu_map.rb +27 -0
  347. data/lib/farce/strict/lru_map.rb +28 -0
  348. data/lib/farce/strict/map.rb +54 -0
  349. data/lib/farce/strict/molecule.rb +16 -0
  350. data/lib/farce/strict/port.rb +40 -0
  351. data/lib/farce/strict/priority_queue.rb +20 -0
  352. data/lib/farce/strict/queue.rb +17 -0
  353. data/lib/farce/strict/set.rb +14 -0
  354. data/lib/farce/strict/sorted_set.rb +19 -0
  355. data/lib/farce/strict/timer_queue.rb +13 -0
  356. data/lib/farce/strict/tree_map.rb +27 -0
  357. data/lib/farce/strict/vector.rb +24 -0
  358. data/lib/farce/strict/weak_atom.rb +31 -0
  359. data/lib/farce/strict/weak_key_map.rb +49 -0
  360. data/lib/farce/strict/weak_map.rb +49 -0
  361. data/lib/farce/strict/weak_set.rb +14 -0
  362. data/lib/farce/strict/weak_value_map.rb +48 -0
  363. data/lib/farce/strict.rb +28 -0
  364. data/lib/farce/system.rb +40 -0
  365. data/lib/farce/thread_scheduler.rb +70 -0
  366. data/lib/farce/timer_queue.rb +66 -0
  367. data/lib/farce/transaction/atom.rb +73 -0
  368. data/lib/farce/transaction/map.rb +25 -0
  369. data/lib/farce/transaction/map_operations.rb +143 -0
  370. data/lib/farce/transaction/molecule.rb +90 -0
  371. data/lib/farce/transaction/mutable.rb +61 -0
  372. data/lib/farce/transaction/set.rb +17 -0
  373. data/lib/farce/transaction/set_operations.rb +48 -0
  374. data/lib/farce/transaction/sorted_set.rb +18 -0
  375. data/lib/farce/transaction/tree_map.rb +78 -0
  376. data/lib/farce/transaction/vector.rb +139 -0
  377. data/lib/farce/transaction/wrapper.rb +202 -0
  378. data/lib/farce/transaction.rb +343 -0
  379. data/lib/farce/tree_map.rb +56 -0
  380. data/lib/farce/unsafe/lfu_map.rb +36 -0
  381. data/lib/farce/unsafe/lru_map.rb +36 -0
  382. data/lib/farce/unsafe/tree_map.rb +21 -0
  383. data/lib/farce/unsafe.rb +29 -0
  384. data/lib/farce/unshareable.rb +67 -0
  385. data/lib/farce/unshared/atom.rb +24 -0
  386. data/lib/farce/unshared/counter.rb +10 -0
  387. data/lib/farce/unshared/flag.rb +10 -0
  388. data/lib/farce/unshared/lazy.rb +38 -0
  389. data/lib/farce/unshared/lazy_ref.rb +26 -0
  390. data/lib/farce/unshared/lease.rb +27 -0
  391. data/lib/farce/unshared/lease_map.rb +26 -0
  392. data/lib/farce/unshared/lease_pool.rb +25 -0
  393. data/lib/farce/unshared/lfu_map.rb +20 -0
  394. data/lib/farce/unshared/lru_map.rb +20 -0
  395. data/lib/farce/unshared/map.rb +49 -0
  396. data/lib/farce/unshared/molecule.rb +16 -0
  397. data/lib/farce/unshared/priority_queue.rb +16 -0
  398. data/lib/farce/unshared/queue.rb +27 -0
  399. data/lib/farce/unshared/set.rb +14 -0
  400. data/lib/farce/unshared/sorted_set.rb +28 -0
  401. data/lib/farce/unshared/timer_queue.rb +16 -0
  402. data/lib/farce/unshared/tree_map.rb +21 -0
  403. data/lib/farce/unshared/vector.rb +19 -0
  404. data/lib/farce/unshared/weak_atom.rb +31 -0
  405. data/lib/farce/unshared/weak_key_map.rb +44 -0
  406. data/lib/farce/unshared/weak_map.rb +44 -0
  407. data/lib/farce/unshared/weak_set.rb +14 -0
  408. data/lib/farce/unshared/weak_value_map.rb +43 -0
  409. data/lib/farce/unshared.rb +28 -0
  410. data/lib/farce/vector.rb +228 -0
  411. data/lib/farce/version.rb +8 -0
  412. data/lib/farce/walker/definitions.rb +186 -0
  413. data/lib/farce/walker/modification.rb +355 -0
  414. data/lib/farce/walker.rb +319 -0
  415. data/lib/farce/weak_atom.rb +121 -0
  416. data/lib/farce/weak_key_map.rb +31 -0
  417. data/lib/farce/weak_map.rb +33 -0
  418. data/lib/farce/weak_ref.rb +67 -0
  419. data/lib/farce/weak_set.rb +82 -0
  420. data/lib/farce/weak_value.rb +195 -0
  421. data/lib/farce/weak_value_map.rb +32 -0
  422. data/lib/farce.rb +519 -0
  423. metadata +468 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A shareable priority queue with transfer modes for unshareable values.
7
+ class PriorityQueue < Farce::Abstract::PriorityQueue
8
+ include Internal::ManagedQueue
9
+ include Shareable::Unfreezable
10
+
11
+ # (see Farce::Abstract::PriorityQueue#initialize)
12
+ # @!macro modes
13
+ # @param mode [Symbol] the default mode for sending unshareable values
14
+ def initialize(capacity: nil, default_priority: 0, order: :ascending, mode: :copy, track_age: false)
15
+ @manager = ModeManager.new(mode:)
16
+ super(capacity:, default_priority:, order:, track_age:)
17
+ end
18
+
19
+ # (see Farce::Abstract::PriorityQueue#push)
20
+ # @!macro modes
21
+ # @param mode [Symbol] the default mode for sending unshareable values
22
+ def push(value, non_block = false, priority: default_priority, timeout: nil, mode: nil) # rubocop:disable Style/OptionalBooleanParameter
23
+ push_to_storage(priority, non_block, @manager.wrap(value, mode:), timeout:)
24
+ end
25
+
26
+ # (see Farce::Abstract::PriorityQueue#try_push)
27
+ # @!macro modes
28
+ # @param mode [Symbol] the default mode for sending unshareable values
29
+ def try_push(value, priority: default_priority, mode: nil)
30
+ return true if @queue.push(priority, @manager.wrap(value, mode:))
31
+
32
+ block_given? ? yield : false
33
+ end
34
+
35
+ # (see Farce::Abstract::PriorityQueue#pop)
36
+ def pop(non_block = false, timeout: nil) # rubocop:disable Style/OptionalBooleanParameter
37
+ return try_pop { raise ThreadError, "queue empty" } if non_block
38
+ deadline = timeout_at(timeout) unless timeout.nil?
39
+
40
+ while true
41
+ empty = false
42
+ result = @reverse_order ? @queue.pop_last { empty = true } : @queue.pop { empty = true }
43
+
44
+ return @manager.unwrap(result) unless empty
45
+ remaining = remaining_timeout(deadline)
46
+ return block_given? ? yield : nil unless wait_pop(timeout: remaining)
47
+ end
48
+ end
49
+
50
+ # (see Farce::Abstract::PriorityQueue#try_pop)
51
+ def try_pop
52
+ empty = false
53
+ result = @reverse_order ? @queue.pop_last { empty = true } : @queue.pop { empty = true }
54
+ return @manager.unwrap(result) unless empty
55
+ yield if block_given?
56
+ end
57
+
58
+ # (see Farce::Abstract::PriorityQueue#peek)
59
+ # @note Peeking at a moved value claims it for this Ractor while leaving it queued.
60
+ def peek
61
+ empty = false
62
+ result = if @reverse_order
63
+ @queue.peek_last { empty = true }
64
+ else
65
+ @queue.peek { empty = true }
66
+ end
67
+ empty ? (block_given? ? yield : nil) : @manager.unwrap(result)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ class Proxy < BasicObject
7
+ # Creates a new register for defining wrapper methods for proxy classes.
8
+ # Helpful to isolate proxy definitions that shouldn't be activated globally.
9
+ #
10
+ # @example
11
+ # register = Farce::Proxy::Register.new
12
+ #
13
+ # register.define(Hash) do
14
+ # # Don't send the fetch block to the proxied object
15
+ # # Allows blocks that aren't ractor-safe
16
+ # def fetch(*args)
17
+ # return super unless block_given?
18
+ # undefined = Object.new.freeze
19
+ # result = super(args.first, undefined)
20
+ # undefined.equal?(result) ? yield(args.first) : result
21
+ # end
22
+ # end
23
+ #
24
+ # default_proxy = Farce::Proxy.new({})
25
+ # custom_proxy = Farce::Proxy.new({}, register:)
26
+ #
27
+ # Ractor.new(default_proxy, custom_proxy) do |default_proxy, custom_proxy|
28
+ # default_proxy.fetch(:key) { Ractor.main? } # => true
29
+ # custom_proxy.fetch(:key) { Ractor.main? } # => false
30
+ # end
31
+ class Register
32
+ include Internal::MarshalSupport::Reject
33
+
34
+ # Mixin extending the modules created by {Register#define}
35
+ module Definition
36
+ # Same as `Module#define_method`, but makes Proc definitions shareable across Ractors.
37
+ #
38
+ # @overload define_method(name)
39
+ # @param name [Symbol, String] the name of the method to define
40
+ # @yield block to define the method body
41
+ #
42
+ # @overload define_method(name, definition)
43
+ # @param name [Symbol, String] the name of the method to define
44
+ # @param definition [Proc, Method, UnboundMethod] the method body
45
+ def define_method(name, definition = UNDEFINED, &block)
46
+ if UNDEFINED.equal?(definition)
47
+ return super(name) unless block
48
+ definition = block
49
+ end
50
+
51
+ definition = Ractor.shareable_lambda(&definition) if definition.is_a?(Proc) && !Ractor.shareable?(definition)
52
+
53
+ super(name, definition)
54
+ end
55
+ end
56
+
57
+ include Farce::Shareable::Tracked
58
+
59
+ # @api private
60
+ attr_reader :inner_mirror, :outer_mirror
61
+
62
+ def initialize
63
+ @inner_mirror = ClassMirror.new(Wrapper::Inner)
64
+ @outer_mirror = ClassMirror.new(Wrapper::Outer)
65
+ super
66
+ end
67
+
68
+ # Created a new module that will be included in the proxy for the specified class and layer.
69
+ # You can define methods using `def` within the block.
70
+ # These methods may call `super` (which will ultimately delegate to the proxied object).
71
+ #
72
+ # The inner layer is invoked inside the Ractor the proxy resides in, while the outer layer is
73
+ # invoked in the caller's Ractor.
74
+ #
75
+ # The wrapper objects do not have to be Ractor-shareable, so you may use instance variables for memoization.
76
+ #
77
+ # @param klass [Class] the class to define wrapper methods for
78
+ # @param layer [Symbol] the layer of the proxy to define methods for (:inner or :outer)
79
+ # @yield block to define wrapper methods for the proxied object
80
+ # @yieldreceiver [Module] the module representing the wrapper layer, extended by {Definition}
81
+ def define(klass, layer: :outer, &)
82
+ check_frozen!
83
+ mirror_for(layer).define(klass) do
84
+ extend Definition
85
+
86
+ module_eval(&)
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def mirror_for(layer)
93
+ case layer
94
+ when :inner then @inner_mirror
95
+ when :outer then @outer_mirror
96
+ else raise ArgumentError, "Unknown layer: #{layer}"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ require "farce/envelope"
6
+
7
+ module Farce
8
+ class Proxy < BasicObject
9
+ # Creates a new register for defining wrapper methods for proxy classes.
10
+ # @api private
11
+ class Supervisor # :nodoc: all
12
+ NilValue = Object.new.freeze
13
+ Request = Data.define(:success, :result, :args, :kwargs, :block)
14
+
15
+ attr_reader :mode_manager, :register
16
+
17
+ def initialize(register, scheduler, reference)
18
+ @mode_manager = ModeManager.new(register:)
19
+ @queue = Strict::Queue.new(capacity: 1)
20
+ @pending = Strict::Map.new(compare_by_identity: true)
21
+ @register = register
22
+ @scheduler = scheduler
23
+ @reference = reference
24
+ end
25
+
26
+ def run(object)
27
+ @owner = Internal::ProxyOwner.current
28
+ @wrapper_class = @register.outer_mirror[::Kernel.instance_method(:class).bind_call(object)]
29
+ Ractor.make_shareable(self)
30
+ @owner.register(self)
31
+ @scheduler.schedule(mode: :local) { run!(object) }
32
+ end
33
+
34
+ # Sends a method to the outer wrapper, which then will most likely trigger {#dispatch},
35
+ # which will send it over the queue to the inner wrapper.
36
+ def send(...)
37
+ check_owner!
38
+ wrapper.__send__(...)
39
+ end
40
+
41
+ # Dispatches the method call to the inner wrapper via the queue and waits for the result.
42
+ # Called by Wrapper::Outer#method_missing
43
+ def dispatch(*args, **kwargs, &)
44
+ args.map! { @mode_manager.wrap(it) }
45
+ kwargs.transform_values! { @mode_manager.wrap(it) }
46
+
47
+ Ractor.make_shareable(kwargs)
48
+ Ractor.make_shareable(args)
49
+
50
+ block = Ractor.shareable_proc(&) if block_given?
51
+ request = Request.new(Internal::Flag.new, Strict::Queue.new(capacity: 1), args, kwargs, block)
52
+
53
+ begin
54
+ @pending[request.result] = true
55
+ # Register before checking closure so shutdown cannot miss this reply.
56
+ check_owner!
57
+ @queue.push(request)
58
+ result = request.result.pop
59
+ rescue ClosedQueueError
60
+ raise Ractor::RemoteError, "proxy owner has stopped"
61
+ ensure
62
+ @pending.delete(request.result)
63
+ end
64
+
65
+ value = @mode_manager.unwrap(result)
66
+ value = nil if NilValue.equal?(value)
67
+
68
+ return value if request.success.value
69
+ value = Ractor::RemoteError.new(value) if value.is_a?(String)
70
+ raise value
71
+ end
72
+
73
+ def method_defined?(...)
74
+ check_owner!
75
+ wrapper.respond_to?(...)
76
+ end
77
+
78
+ def stop
79
+ @queue.close
80
+ @pending.each_key(&:close)
81
+ @owner.unregister(self)
82
+ end
83
+
84
+ private
85
+
86
+ def check_owner!
87
+ return if @owner.alive? && !@queue.closed?
88
+ raise Ractor::RemoteError, "proxy owner has stopped"
89
+ end
90
+
91
+ def wrapper
92
+ token = @reference.value
93
+ Internal::Storage.store_if_absent(token) { @wrapper_class.new(self) }
94
+ end
95
+
96
+ def deliver(request, result)
97
+ request.result.push(result)
98
+ rescue ClosedQueueError
99
+ # The owner may have stopped while the target method was running.
100
+ nil
101
+ end
102
+
103
+ def run!(object)
104
+ inner_wrapper = @register.inner_mirror[::Kernel.instance_method(:class).bind_call(object)].new(object, self)
105
+ until @queue.closed?
106
+ @queue.seal unless @reference.alive?
107
+ begin
108
+ request = @queue.pop(timeout: 0.1)
109
+ rescue ClosedQueueError
110
+ break
111
+ end
112
+ next unless request
113
+ begin
114
+ args = request.args.map { @mode_manager.unwrap(it) }
115
+ kwargs = request.kwargs.transform_values { @mode_manager.unwrap(it) }
116
+ result = inner_wrapper.__send__(*args, **kwargs, &request.block)
117
+ request.success.value = true
118
+ result = SELF if object.equal?(result)
119
+ result = nil.equal?(result) ? NilValue : @mode_manager.wrap(result)
120
+ rescue Exception => e # rubocop:disable Lint/RescueException
121
+ request.success.value = false
122
+ begin
123
+ result = @mode_manager.wrap(e)
124
+ rescue StandardError
125
+ result = -"#{e.class}: #{e.message}"
126
+ end
127
+ end
128
+ deliver(request, result)
129
+ end
130
+ ensure
131
+ stop
132
+ end
133
+ end
134
+
135
+ private_constant :Supervisor
136
+ end
137
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ class Proxy < BasicObject
7
+ # @api private
8
+ class Wrapper < ::BasicObject # :nodoc: all
9
+ include ::Farce.const_get(:Internal)::Delegation
10
+
11
+ class Inner < self
12
+ def initialize(object, ...)
13
+ @__object__ = object
14
+ super(...)
15
+ end
16
+
17
+ private
18
+
19
+ # Dispatches method calls to @__object__
20
+ def method_missing(...) = @__object__.__send__(...)
21
+ def respond_to_missing?(...) = @__object__.respond_to?(...)
22
+ end
23
+
24
+ class Outer < self
25
+ private
26
+
27
+ # Dispatches method calls to be processed by the Ractor the proxied object resides in.
28
+ def method_missing(...) = @__supervisor__.dispatch(...)
29
+ def respond_to_missing?(...) = @__supervisor__.dispatch(:respond_to?, ...)
30
+ end
31
+
32
+ define_method(:respond_to?, ::Kernel.instance_method(:respond_to?))
33
+ def initialize(supervisor) = @__supervisor__ = supervisor
34
+
35
+ private
36
+
37
+ def __wrap__(...) = @__supervisor__.mode_manager.wrap(...)
38
+ def __unwrap__(...) = @__supervisor__.mode_manager.unwrap(...)
39
+
40
+ ::Farce::MODES.each do |mode|
41
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
42
+ private def __#{mode}__(object) = __wrap__(object, mode: :#{mode})
43
+ RUBY
44
+ end
45
+ end
46
+
47
+ private_constant :Wrapper
48
+ end
49
+ end
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A proxy mimics the API of another object, executing method calls within the Ractor that created it.
7
+ # This is an easy way to have drop-in replacements for objects that cannot be shared across Ractors.
8
+ # Delegated calls raise Ractor::RemoteError after the owner exits.
9
+ #
10
+ # ```ruby
11
+ # # Mutable arrays aren't Ractor-shareable
12
+ # array = []
13
+ # proxied = []
14
+ # proxy = Farce::Proxy.new(proxied)
15
+ #
16
+ # Farce::Ractor.new(array, proxy) do |*list|
17
+ # list.each { it << 42 }
18
+ # end.join
19
+ #
20
+ # # The array got copied instead of being modified in place
21
+ # array # => []
22
+ #
23
+ # # The proxy didn't get copied
24
+ # proxied # => [42]
25
+ # ```
26
+ #
27
+ # ## Customizing Proxy Behavior
28
+ #
29
+ # Imagine we have an unshareable class:
30
+ #
31
+ # ```ruby
32
+ # class MyClass
33
+ # include Farce::Unshareable
34
+ # attr_reader :record
35
+ #
36
+ # def initialize = @record = []
37
+ # def <<(value) = @record << value
38
+ # end
39
+ # ```
40
+ #
41
+ # We can use a proxy to still allow cross-ractor access:
42
+ #
43
+ # ```ruby
44
+ # my_instance = MyClass.new
45
+ # my_proxy = Farce::Proxy.create(my_instance)
46
+ #
47
+ # Ractor.new(my_proxy) { it << 42 }.join
48
+ #
49
+ # my_instance.record # => [42]
50
+ # ```
51
+ #
52
+ # The above example has one issue:
53
+ # We cannot chain the calls, because `<<` returns an array, which actually gets copied.
54
+ #
55
+ # ```ruby
56
+ # my_instance = MyClass.new
57
+ # my_proxy = Farce::Proxy.create(my_instance)
58
+ #
59
+ # Ractor.new(my_proxy) { it << 42 << 43 }.join
60
+ #
61
+ # my_instance.record # => [42]
62
+ # ```
63
+ #
64
+ # We could easily fix this inside `MyClass` by returning `self` from `<<`, but maybe we don't actually control
65
+ # `MyClass`? In that case, we can add a proxy definition:
66
+ #
67
+ # ```ruby
68
+ # # The "inner" layer runs in the same ractor that the proxied object lives in.
69
+ # Farce::Proxy.define(MyClass, layer: :inner) do
70
+ # # Also proxy the value returned by `<<`
71
+ # def <<(...) = __proxy__(super)
72
+ # end
73
+ #
74
+ # my_instance = MyClass.new
75
+ # my_proxy = Farce::Proxy.create(my_instance)
76
+ #
77
+ # Ractor.new(my_proxy) { it << 42 << 43 }.join
78
+ #
79
+ # my_instance.record # => [42, 43]
80
+ # ```
81
+ #
82
+ # ### Advanced customization
83
+ #
84
+ # The proxy wrappers define methods for all supported {file:docs/modes.md modes}. Use the outer wrapper for argument
85
+ # handling and memoization, use the inner wrapper for handling return values:
86
+ #
87
+ # ```ruby
88
+ # # This class cannot easily be shared across Ractors, as it uses a Mutex and a mutable array.
89
+ # class MyClass
90
+ # attr_reader :capacity
91
+ #
92
+ # def initialize(capacity = 100)
93
+ # @capacity = Integer(capacity)
94
+ # @record = []
95
+ # @mutex = Mutex.new
96
+ # end
97
+ #
98
+ # def add(value)
99
+ # @mutex.synchronize do
100
+ # return false if @record.size >= @capacity
101
+ # @record << value
102
+ # true
103
+ # end
104
+ # end
105
+ #
106
+ # def to_a = @record.dup
107
+ # end
108
+ #
109
+ # # Lets define some rules
110
+ #
111
+ # Farce::Proxy.define(MyClass, layer: :outer) do
112
+ # # Capacity doesn't change, so we can memoize it.
113
+ # def capacity = @capacity ||= super
114
+ #
115
+ # # Maybe a common use-case is adding MyClass instances to MyClass instances?
116
+ # # If so, maybe we want to proxy these as well? Make everything else shareable.
117
+ # def add(value)
118
+ # value = __proxy__(value) if value.is_a? MyClass
119
+ # super __make_shareable__(value)
120
+ # end
121
+ # end
122
+ #
123
+ # Farce::Proxy.define(MyClass, layer: :inner) do
124
+ # # to_a already creates a new array every time, we don't have to copy it again
125
+ # def to_a = __move__(super)
126
+ # end
127
+ # ```
128
+ #
129
+ # Proxies should mimic the original object's interface as closely as possible, but nothing keeps you from adding
130
+ # custom methods:
131
+ #
132
+ # ```ruby
133
+ # class MyClass
134
+ # def is_proxy? = false
135
+ # end
136
+ #
137
+ # Farce::Proxy.define(MyClass) do
138
+ # def is_proxy? = true
139
+ # def proxy_method = "Hi from the proxy!!!"
140
+ # end
141
+ #
142
+ # my_instance = MyClass.new
143
+ # my_proxy = Farce::Proxy.create(my_instance)
144
+ #
145
+ # my_instance.is_proxy? # => false
146
+ # my_proxy.is_proxy? # => true
147
+ #
148
+ # my_proxy.proxy_method if my_proxy.is_proxy? # => "Hi from the proxy!!!"
149
+ # ```
150
+ class Proxy < BasicObject
151
+ include ::Farce.const_get(:Internal)::Autoloads
152
+ include ::Farce.const_get(:Internal)::Delegation
153
+
154
+ REGISTER = Register.new
155
+ SELF = ::Object.new.freeze
156
+ UNDEFINED = ::Farce.const_get(:UNDEFINED)
157
+ ProxyOwner = ::Farce.const_get(:Internal)::ProxyOwner
158
+
159
+ private_constant :Supervisor, :Wrapper, :REGISTER, :SELF, :UNDEFINED, :ProxyOwner
160
+
161
+ define_method(:respond_to?, ::Kernel.instance_method(:respond_to?))
162
+ define_method(:__freeze__, ::Kernel.instance_method(:freeze))
163
+ private :__freeze__
164
+
165
+ # @return [Farce::Proxy::Register] the default register used by the proxy class
166
+ def self.default_register = REGISTER
167
+
168
+ # @overload define(klass, layer: :outer)
169
+ # (see Farce::Proxy::Register#define)
170
+ # @see .default_register
171
+ # @see Farce::Proxy::Register#define
172
+ def self.define(...) = REGISTER.define(...)
173
+
174
+ # @overload create(object, register: nil, scheduler: nil)
175
+ # Creates a new proxy for the given object if it is not Ractor-shareable.
176
+ # Returns the given object otherwise.
177
+ # @param (see Farce::Proxy#initialize)
178
+ #
179
+ # @overload create(register: nil)
180
+ # Creates a new Ractor, runs the given block within it, and returns the result.
181
+ # If the result is not Ractor-shareable, it will be proxied.
182
+ # The new Ractor stops after its proxies are collected.
183
+ #
184
+ # @yield The block to be executed within the new Ractor.
185
+ # @yieldreceiver [nil]
186
+ #
187
+ # @overload create(scheduler:, register: nil)
188
+ # Schedules the given block to be executed by the specified scheduler.
189
+ # Returns its result. If the result is not Ractor-shareable, it will be proxied.
190
+ #
191
+ # @yield The block to be executed by the scheduler.
192
+ # @yieldreceiver [nil]
193
+ #
194
+ # @return [BasicObject, Farce::Proxy] the proxied object or the original object if it is Ractor-shareable
195
+ def self.create(object = UNDEFINED, register: nil, scheduler: nil, &)
196
+ if UNDEFINED.equal?(object)
197
+ initializer = ::Farce::Strict::Atom.new(::Farce::Ractor.shareable_proc(&))
198
+ success = ::Farce::Strict::Atom.new
199
+ result = ::Farce::Strict::Atom.new
200
+ owned = scheduler.nil?
201
+ scheduler ||= owner_scheduler
202
+
203
+ schedule_initializer(scheduler, initializer, register, success, result, owned)
204
+
205
+ success.wait_until_non_nil
206
+ return result.swap(nil) if success.value
207
+ raise ::Farce::Ractor::RemoteError, result.swap(nil)
208
+ end
209
+
210
+ return object if Ractor.shareable?(object)
211
+ new(object, register:, scheduler:)
212
+ end
213
+
214
+ def self.owner_scheduler = ::Farce::ThreadScheduler.new { |*args, &block| ::Farce::Ractor.new(*args, &block) }
215
+ private_class_method :owner_scheduler
216
+
217
+ def self.schedule_initializer(scheduler, *)
218
+ scheduler.schedule(*) do |initializer, register, success, result, owned|
219
+ local_scheduler = ::Farce::Proxy.__send__(:initialize_proxy, initializer, register, success, result, owned)
220
+ result = nil
221
+ ProxyOwner.drain_scheduler(local_scheduler) if local_scheduler
222
+ rescue ::StandardError => e
223
+ raise unless result
224
+ result.value = -"#{e.class}: #{e.message}"
225
+ success.value = false
226
+ ensure
227
+ local_scheduler&.close
228
+ end
229
+ end
230
+ private_class_method :schedule_initializer
231
+
232
+ # Keep initializer and proxy temporaries out of the frame that drains the owner.
233
+ def self.initialize_proxy(initializer, register, success, result, owned)
234
+ initialized = initializer.swap(nil).call
235
+ local_scheduler = nil
236
+ local_scheduler = ProxyOwner.create_scheduler if owned && !::Farce::Ractor.shareable?(initialized)
237
+ created = ::Farce::Proxy.create(initialized, register:, scheduler: local_scheduler)
238
+ # Resolve shareability before the atom setter. Its native traversal scratch
239
+ # state can otherwise retain the proxy while the owner drains its scheduler.
240
+ ::Farce::Ractor.make_shareable(created)
241
+ result.value = created
242
+ success.value = true
243
+ local_scheduler
244
+ rescue ::Exception # rubocop:disable Lint/RescueException -- Close the owned scheduler before propagating initialization failures.
245
+ local_scheduler&.close
246
+ raise
247
+ end
248
+ private_class_method :initialize_proxy
249
+
250
+ # @param object [BasicObject] the object to be managed by the proxy
251
+ # @param register [Farce::Proxy::Register, nil] the register to use
252
+ # @param scheduler [Farce::Abstract::Scheduler, nil] the scheduler to use
253
+ def initialize(object, register: nil, scheduler: nil)
254
+ @token = ::Object.new.freeze # cross-ractor finalizers don't work reliably across Ruby versions
255
+ @supervisor = Supervisor.new(register || REGISTER, scheduler || ::Farce, ::Farce::WeakValue.new(@token))
256
+ @proxy_class = ::Kernel.instance_method(:class).bind_call(self)
257
+ @object_class = ::Kernel.instance_method(:class).bind_call(object)
258
+ @object_id = ::BasicObject.instance_method(:__id__).bind_call(object)
259
+ __freeze__
260
+ @supervisor.run(object)
261
+ end
262
+
263
+ # @return [true] proxies can be shared even when their targets cannot
264
+ def ractor_shareable? = true
265
+
266
+ # @note Does not call `#inspect` on the proxied object itself, to avoid Ractor round-trips
267
+ # @return [String] a string representation of the proxy object
268
+ def inspect = "#<#{@proxy_class.inspect} object=#<#{@object_class.inspect}:0x#{@object_id.to_s(16)}>>"
269
+
270
+ # @api private
271
+ def pretty_print(pp)
272
+ pp.group(1, "#<#{@proxy_class.inspect} ", ">") do
273
+ pp.text "object="
274
+ pp.text "#<#{@object_class.inspect}:0x#{@object_id.to_s(16)}>"
275
+ end
276
+ end
277
+
278
+ private
279
+
280
+ # @api private
281
+ def marshal_dump = ::Kernel.raise(::TypeError, "Farce::Proxy cannot be marshaled")
282
+
283
+ # Dispatches method calls to be processed by the Ractor the proxied object resides in.
284
+ def method_missing(...)
285
+ result = @supervisor.send(...)
286
+ SELF.equal?(result) ? self : result
287
+ end
288
+
289
+ def respond_to_missing?(...) = @supervisor.method_defined?(...)
290
+ end
291
+ end