farce 0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (420) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/CODE_OF_CONDUCT.md +26 -0
  4. data/CONTRIBUTING.md +71 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +1525 -0
  7. data/SECURITY.md +10 -0
  8. data/docs/benchmarks.md +185 -0
  9. data/docs/gems/dry-types.md +290 -0
  10. data/docs/gems/msgpack.md +70 -0
  11. data/docs/gems/ractor-shim.md +63 -0
  12. data/docs/modes.md +628 -0
  13. data/docs/scopes.md +649 -0
  14. data/docs/variants.md +346 -0
  15. data/ext/ext_helper.rb +20 -0
  16. data/ext/farce/README.md +21 -0
  17. data/ext/farce/atom.c +1023 -0
  18. data/ext/farce/bounded_map.c +1683 -0
  19. data/ext/farce/containers.h +77 -0
  20. data/ext/farce/counter.c +399 -0
  21. data/ext/farce/darwin.c +100 -0
  22. data/ext/farce/depend +12 -0
  23. data/ext/farce/dict.c +1523 -0
  24. data/ext/farce/dict.h +152 -0
  25. data/ext/farce/drivers.c +237 -0
  26. data/ext/farce/exchanger.c +299 -0
  27. data/ext/farce/extconf.rb +99 -0
  28. data/ext/farce/farce.c +359 -0
  29. data/ext/farce/flag.c +288 -0
  30. data/ext/farce/io.c +338 -0
  31. data/ext/farce/lock.c +510 -0
  32. data/ext/farce/map.c +2240 -0
  33. data/ext/farce/priority_queue.c +2056 -0
  34. data/ext/farce/queue.c +1059 -0
  35. data/ext/farce/reactor.c +820 -0
  36. data/ext/farce/reactor.h +103 -0
  37. data/ext/farce/shareable.h +31 -0
  38. data/ext/farce/signal.c +350 -0
  39. data/ext/farce/transaction.c +354 -0
  40. data/ext/farce/transaction.h +40 -0
  41. data/ext/farce/tree_map.c +1953 -0
  42. data/ext/farce/trie.c +2020 -0
  43. data/ext/farce/unshareable.c +155 -0
  44. data/ext/farce/unshared_io_pool.h +234 -0
  45. data/ext/farce/unshared_signal.c +263 -0
  46. data/ext/farce/unshared_wait.h +193 -0
  47. data/ext/farce/unsupported.c +6 -0
  48. data/ext/farce/vector.c +1195 -0
  49. data/ext/farce/weak_map.c +1714 -0
  50. data/ext/java/org/farce/BoundedMap.java +394 -0
  51. data/ext/java/org/farce/FiberScheduler.java +141 -0
  52. data/ext/java/org/farce/PriorityKey.java +45 -0
  53. data/ext/java/org/farce/PriorityQueue.java +373 -0
  54. data/ext/java/org/farce/QueueSignal.java +28 -0
  55. data/ext/rebind/README.md +14 -0
  56. data/ext/rebind/extconf.rb +10 -0
  57. data/ext/rebind/rebind.c +186 -0
  58. data/lib/farce/_yard/internal.rb +13 -0
  59. data/lib/farce/_yard/macros.rb +61 -0
  60. data/lib/farce/_yard/ractor.rb +46 -0
  61. data/lib/farce/abstract/atom.rb +186 -0
  62. data/lib/farce/abstract/bounded_map.rb +232 -0
  63. data/lib/farce/abstract/collection.rb +151 -0
  64. data/lib/farce/abstract/concurrent_map.rb +381 -0
  65. data/lib/farce/abstract/counter.rb +193 -0
  66. data/lib/farce/abstract/duplicable_map.rb +229 -0
  67. data/lib/farce/abstract/exchanger.rb +26 -0
  68. data/lib/farce/abstract/flag.rb +71 -0
  69. data/lib/farce/abstract/lazy.rb +115 -0
  70. data/lib/farce/abstract/lease.rb +104 -0
  71. data/lib/farce/abstract/lease_map.rb +261 -0
  72. data/lib/farce/abstract/lease_pool.rb +91 -0
  73. data/lib/farce/abstract/lfu_map.rb +20 -0
  74. data/lib/farce/abstract/lru_map.rb +25 -0
  75. data/lib/farce/abstract/map.rb +345 -0
  76. data/lib/farce/abstract/molecule.rb +245 -0
  77. data/lib/farce/abstract/port.rb +74 -0
  78. data/lib/farce/abstract/priority_queue.rb +117 -0
  79. data/lib/farce/abstract/queue.rb +269 -0
  80. data/lib/farce/abstract/scheduler.rb +111 -0
  81. data/lib/farce/abstract/set.rb +910 -0
  82. data/lib/farce/abstract/sorted_set.rb +172 -0
  83. data/lib/farce/abstract/timer_queue.rb +136 -0
  84. data/lib/farce/abstract/tree_map.rb +269 -0
  85. data/lib/farce/abstract/value.rb +68 -0
  86. data/lib/farce/abstract/vector.rb +1126 -0
  87. data/lib/farce/abstract/weak_atom.rb +32 -0
  88. data/lib/farce/abstract/weak_key_map.rb +12 -0
  89. data/lib/farce/abstract/weak_map.rb +13 -0
  90. data/lib/farce/abstract/weak_set.rb +12 -0
  91. data/lib/farce/abstract/weak_value_map.rb +12 -0
  92. data/lib/farce/abstract.rb +17 -0
  93. data/lib/farce/atom.rb +293 -0
  94. data/lib/farce/class_mirror.rb +87 -0
  95. data/lib/farce/clock.rb +121 -0
  96. data/lib/farce/config.rb +229 -0
  97. data/lib/farce/counter.rb +71 -0
  98. data/lib/farce/deduper.rb +122 -0
  99. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  101. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  102. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  103. data/lib/farce/engine/jruby/map.rb +505 -0
  104. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  105. data/lib/farce/engine/jruby/signal.rb +147 -0
  106. data/lib/farce/engine/jruby.rb +63 -0
  107. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  108. data/lib/farce/engine/jvm/counter.rb +102 -0
  109. data/lib/farce/engine/jvm/extension.rb +32 -0
  110. data/lib/farce/engine/jvm/farce.jar +0 -0
  111. data/lib/farce/engine/jvm/flag.rb +79 -0
  112. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  113. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  114. data/lib/farce/engine/jvm/types.rb +180 -0
  115. data/lib/farce/engine/jvm.rb +19 -0
  116. data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
  117. data/lib/farce/engine/ruby/3.4/port.rb +186 -0
  118. data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
  119. data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
  120. data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
  121. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  122. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  123. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  124. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  125. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  126. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  127. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  128. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  129. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  130. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  131. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  132. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  133. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  134. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  135. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  136. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  137. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  138. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  139. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  140. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  141. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  142. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  143. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  144. data/lib/farce/engine/ruby.rb +130 -0
  145. data/lib/farce/engine/shared/atom.rb +10 -0
  146. data/lib/farce/engine/shared/exchanger.rb +130 -0
  147. data/lib/farce/engine/shared/identity_key.rb +22 -0
  148. data/lib/farce/engine/shared/lease.rb +10 -0
  149. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  150. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  151. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  152. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  153. data/lib/farce/engine/shared/port.rb +44 -0
  154. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  155. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  156. data/lib/farce/engine/shared/queue.rb +233 -0
  157. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  158. data/lib/farce/engine/shared/rebindable.rb +15 -0
  159. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  160. data/lib/farce/engine/shared/strict_map.rb +117 -0
  161. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  162. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  163. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  164. data/lib/farce/engine/shared/trie.rb +317 -0
  165. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  166. data/lib/farce/engine/shared/unshareable.rb +14 -0
  167. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  168. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  169. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  170. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  171. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  172. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  173. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  174. data/lib/farce/engine/shared/vault.rb +31 -0
  175. data/lib/farce/engine/shared/vector.rb +83 -0
  176. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  177. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  178. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  179. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  180. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  181. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  182. data/lib/farce/engine/shared.rb +63 -0
  183. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  184. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  185. data/lib/farce/engine/truffleruby/map.rb +656 -0
  186. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  187. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  188. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  189. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  190. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  191. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  192. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  193. data/lib/farce/engine/truffleruby.rb +148 -0
  194. data/lib/farce/envelope.rb +335 -0
  195. data/lib/farce/error.rb +35 -0
  196. data/lib/farce/exchanger.rb +41 -0
  197. data/lib/farce/flag.rb +27 -0
  198. data/lib/farce/integrations/active_support/blank.rb +68 -0
  199. data/lib/farce/integrations/active_support/clock.rb +17 -0
  200. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  201. data/lib/farce/integrations/active_support/map.rb +175 -0
  202. data/lib/farce/integrations/active_support/set.rb +60 -0
  203. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  204. data/lib/farce/integrations/active_support/vector.rb +231 -0
  205. data/lib/farce/integrations/active_support.rb +9 -0
  206. data/lib/farce/integrations/activesupport.rb +5 -0
  207. data/lib/farce/integrations/bson.rb +108 -0
  208. data/lib/farce/integrations/cbor.rb +51 -0
  209. data/lib/farce/integrations/concurrent.rb +112 -0
  210. data/lib/farce/integrations/dry-types.rb +5 -0
  211. data/lib/farce/integrations/dry_types.rb +694 -0
  212. data/lib/farce/integrations/json.rb +7 -0
  213. data/lib/farce/integrations/msgpack.rb +112 -0
  214. data/lib/farce/integrations/oj.rb +69 -0
  215. data/lib/farce/integrations/psych.rb +191 -0
  216. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  217. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  218. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  219. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  220. data/lib/farce/integrations/shared/to_json.rb +42 -0
  221. data/lib/farce/integrations/sorted_set.rb +11 -0
  222. data/lib/farce/integrations/weakref.rb +38 -0
  223. data/lib/farce/integrations/yajl.rb +10 -0
  224. data/lib/farce/integrations.rb +156 -0
  225. data/lib/farce/internal/_frozen_config.rb +11 -0
  226. data/lib/farce/internal/autoloads.rb +47 -0
  227. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  228. data/lib/farce/internal/converter.rb +106 -0
  229. data/lib/farce/internal/copyable.rb +31 -0
  230. data/lib/farce/internal/delegation.rb +20 -0
  231. data/lib/farce/internal/external_transaction.rb +59 -0
  232. data/lib/farce/internal/fake_ractor.rb +179 -0
  233. data/lib/farce/internal/freeze.rb +118 -0
  234. data/lib/farce/internal/inspect.rb +157 -0
  235. data/lib/farce/internal/key_lock_map.rb +29 -0
  236. data/lib/farce/internal/key_normalizer.rb +289 -0
  237. data/lib/farce/internal/lease_initialization.rb +115 -0
  238. data/lib/farce/internal/lease_map.rb +441 -0
  239. data/lib/farce/internal/lease_pool_state.rb +228 -0
  240. data/lib/farce/internal/lease_state.rb +342 -0
  241. data/lib/farce/internal/lease_waiting.rb +13 -0
  242. data/lib/farce/internal/managed_queue.rb +26 -0
  243. data/lib/farce/internal/map_value_modes.rb +227 -0
  244. data/lib/farce/internal/marshal_support.rb +227 -0
  245. data/lib/farce/internal/mixin.rb +20 -0
  246. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  247. data/lib/farce/internal/noncopyable.rb +17 -0
  248. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  249. data/lib/farce/internal/pool_supervisor.rb +41 -0
  250. data/lib/farce/internal/pool_worker.rb +61 -0
  251. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  252. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  253. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  254. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  255. data/lib/farce/internal/portable_transaction.rb +164 -0
  256. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  257. data/lib/farce/internal/reservation_waiting.rb +24 -0
  258. data/lib/farce/internal/scheduled_task.rb +48 -0
  259. data/lib/farce/internal/scheduler_io.rb +171 -0
  260. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  261. data/lib/farce/internal/select_scheduler.rb +235 -0
  262. data/lib/farce/internal/storage.rb +162 -0
  263. data/lib/farce/internal/strict_lease.rb +30 -0
  264. data/lib/farce/internal/strict_lease_map.rb +20 -0
  265. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  266. data/lib/farce/internal/thread_pool.rb +166 -0
  267. data/lib/farce/internal/transaction_conflict.rb +9 -0
  268. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  269. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  270. data/lib/farce/internal/undefined.rb +22 -0
  271. data/lib/farce/internal/unshared_lease.rb +29 -0
  272. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  273. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  274. data/lib/farce/internal/value_serialization.rb +13 -0
  275. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  276. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  277. data/lib/farce/internal.rb +129 -0
  278. data/lib/farce/lazy.rb +100 -0
  279. data/lib/farce/lazy_ref.rb +40 -0
  280. data/lib/farce/lease.rb +28 -0
  281. data/lib/farce/lease_map.rb +34 -0
  282. data/lib/farce/lease_pool.rb +29 -0
  283. data/lib/farce/lfu_map.rb +53 -0
  284. data/lib/farce/local/atom.rb +23 -0
  285. data/lib/farce/local/counter.rb +88 -0
  286. data/lib/farce/local/flag.rb +65 -0
  287. data/lib/farce/local/lazy.rb +38 -0
  288. data/lib/farce/local/lazy_ref.rb +36 -0
  289. data/lib/farce/local/lease.rb +77 -0
  290. data/lib/farce/local/lease_map.rb +80 -0
  291. data/lib/farce/local/lease_pool.rb +38 -0
  292. data/lib/farce/local/lfu_map.rb +55 -0
  293. data/lib/farce/local/lru_map.rb +62 -0
  294. data/lib/farce/local/map.rb +47 -0
  295. data/lib/farce/local/molecule.rb +45 -0
  296. data/lib/farce/local/priority_queue.rb +53 -0
  297. data/lib/farce/local/queue.rb +32 -0
  298. data/lib/farce/local/scoped.rb +166 -0
  299. data/lib/farce/local/set.rb +18 -0
  300. data/lib/farce/local/sorted_set.rb +50 -0
  301. data/lib/farce/local/timer_queue.rb +41 -0
  302. data/lib/farce/local/tree_map.rb +47 -0
  303. data/lib/farce/local/vector.rb +30 -0
  304. data/lib/farce/local/weak_atom.rb +23 -0
  305. data/lib/farce/local/weak_key_map.rb +39 -0
  306. data/lib/farce/local/weak_map.rb +39 -0
  307. data/lib/farce/local/weak_set.rb +18 -0
  308. data/lib/farce/local/weak_value_map.rb +39 -0
  309. data/lib/farce/local.rb +35 -0
  310. data/lib/farce/lock.rb +54 -0
  311. data/lib/farce/lru_map.rb +63 -0
  312. data/lib/farce/map.rb +58 -0
  313. data/lib/farce/mode_manager.rb +142 -0
  314. data/lib/farce/molecule.rb +60 -0
  315. data/lib/farce/mutable.rb +171 -0
  316. data/lib/farce/pool.rb +332 -0
  317. data/lib/farce/port.rb +134 -0
  318. data/lib/farce/priority_queue.rb +70 -0
  319. data/lib/farce/proxy/register.rb +101 -0
  320. data/lib/farce/proxy/supervisor.rb +137 -0
  321. data/lib/farce/proxy/wrapper.rb +49 -0
  322. data/lib/farce/proxy.rb +291 -0
  323. data/lib/farce/queue.rb +60 -0
  324. data/lib/farce/ractor.rb +269 -0
  325. data/lib/farce/read_write_lock.rb +256 -0
  326. data/lib/farce/reference.rb +158 -0
  327. data/lib/farce/resolv/dns.rb +42 -0
  328. data/lib/farce/resolv.rb +85 -0
  329. data/lib/farce/scheduler.rb +536 -0
  330. data/lib/farce/set.rb +39 -0
  331. data/lib/farce/shareable.rb +150 -0
  332. data/lib/farce/signal.rb +97 -0
  333. data/lib/farce/sorted_set.rb +36 -0
  334. data/lib/farce/strict/atom.rb +34 -0
  335. data/lib/farce/strict/counter.rb +10 -0
  336. data/lib/farce/strict/exchanger.rb +25 -0
  337. data/lib/farce/strict/flag.rb +10 -0
  338. data/lib/farce/strict/lazy.rb +26 -0
  339. data/lib/farce/strict/lazy_ref.rb +26 -0
  340. data/lib/farce/strict/lease.rb +27 -0
  341. data/lib/farce/strict/lease_map.rb +33 -0
  342. data/lib/farce/strict/lease_pool.rb +28 -0
  343. data/lib/farce/strict/lfu_map.rb +27 -0
  344. data/lib/farce/strict/lru_map.rb +28 -0
  345. data/lib/farce/strict/map.rb +54 -0
  346. data/lib/farce/strict/molecule.rb +16 -0
  347. data/lib/farce/strict/port.rb +40 -0
  348. data/lib/farce/strict/priority_queue.rb +20 -0
  349. data/lib/farce/strict/queue.rb +17 -0
  350. data/lib/farce/strict/set.rb +14 -0
  351. data/lib/farce/strict/sorted_set.rb +19 -0
  352. data/lib/farce/strict/timer_queue.rb +13 -0
  353. data/lib/farce/strict/tree_map.rb +27 -0
  354. data/lib/farce/strict/vector.rb +24 -0
  355. data/lib/farce/strict/weak_atom.rb +31 -0
  356. data/lib/farce/strict/weak_key_map.rb +49 -0
  357. data/lib/farce/strict/weak_map.rb +49 -0
  358. data/lib/farce/strict/weak_set.rb +14 -0
  359. data/lib/farce/strict/weak_value_map.rb +48 -0
  360. data/lib/farce/strict.rb +28 -0
  361. data/lib/farce/system.rb +40 -0
  362. data/lib/farce/thread_scheduler.rb +70 -0
  363. data/lib/farce/timer_queue.rb +66 -0
  364. data/lib/farce/transaction/atom.rb +73 -0
  365. data/lib/farce/transaction/map.rb +25 -0
  366. data/lib/farce/transaction/map_operations.rb +143 -0
  367. data/lib/farce/transaction/molecule.rb +90 -0
  368. data/lib/farce/transaction/mutable.rb +61 -0
  369. data/lib/farce/transaction/set.rb +17 -0
  370. data/lib/farce/transaction/set_operations.rb +48 -0
  371. data/lib/farce/transaction/sorted_set.rb +18 -0
  372. data/lib/farce/transaction/tree_map.rb +78 -0
  373. data/lib/farce/transaction/vector.rb +139 -0
  374. data/lib/farce/transaction/wrapper.rb +202 -0
  375. data/lib/farce/transaction.rb +343 -0
  376. data/lib/farce/tree_map.rb +56 -0
  377. data/lib/farce/unsafe/lfu_map.rb +36 -0
  378. data/lib/farce/unsafe/lru_map.rb +36 -0
  379. data/lib/farce/unsafe/tree_map.rb +21 -0
  380. data/lib/farce/unsafe.rb +29 -0
  381. data/lib/farce/unshareable.rb +67 -0
  382. data/lib/farce/unshared/atom.rb +24 -0
  383. data/lib/farce/unshared/counter.rb +10 -0
  384. data/lib/farce/unshared/flag.rb +10 -0
  385. data/lib/farce/unshared/lazy.rb +38 -0
  386. data/lib/farce/unshared/lazy_ref.rb +26 -0
  387. data/lib/farce/unshared/lease.rb +27 -0
  388. data/lib/farce/unshared/lease_map.rb +26 -0
  389. data/lib/farce/unshared/lease_pool.rb +25 -0
  390. data/lib/farce/unshared/lfu_map.rb +20 -0
  391. data/lib/farce/unshared/lru_map.rb +20 -0
  392. data/lib/farce/unshared/map.rb +49 -0
  393. data/lib/farce/unshared/molecule.rb +16 -0
  394. data/lib/farce/unshared/priority_queue.rb +16 -0
  395. data/lib/farce/unshared/queue.rb +27 -0
  396. data/lib/farce/unshared/set.rb +14 -0
  397. data/lib/farce/unshared/sorted_set.rb +28 -0
  398. data/lib/farce/unshared/timer_queue.rb +16 -0
  399. data/lib/farce/unshared/tree_map.rb +21 -0
  400. data/lib/farce/unshared/vector.rb +19 -0
  401. data/lib/farce/unshared/weak_atom.rb +31 -0
  402. data/lib/farce/unshared/weak_key_map.rb +44 -0
  403. data/lib/farce/unshared/weak_map.rb +44 -0
  404. data/lib/farce/unshared/weak_set.rb +14 -0
  405. data/lib/farce/unshared/weak_value_map.rb +43 -0
  406. data/lib/farce/unshared.rb +28 -0
  407. data/lib/farce/vector.rb +228 -0
  408. data/lib/farce/version.rb +8 -0
  409. data/lib/farce/walker/definitions.rb +186 -0
  410. data/lib/farce/walker/modification.rb +355 -0
  411. data/lib/farce/walker.rb +319 -0
  412. data/lib/farce/weak_atom.rb +121 -0
  413. data/lib/farce/weak_key_map.rb +31 -0
  414. data/lib/farce/weak_map.rb +33 -0
  415. data/lib/farce/weak_ref.rb +67 -0
  416. data/lib/farce/weak_set.rb +82 -0
  417. data/lib/farce/weak_value.rb +195 -0
  418. data/lib/farce/weak_value_map.rb +32 -0
  419. data/lib/farce.rb +519 -0
  420. metadata +435 -8
@@ -0,0 +1,155 @@
1
+ #include "containers.h"
2
+
3
+ typedef struct {
4
+ unsigned char reserved;
5
+ } containers_pin_marker_t;
6
+
7
+ static ID pin_marker_id;
8
+ static ID copy_marker_id;
9
+ static ID move_marker_id;
10
+ static ID pipe_id;
11
+ static ID close_id;
12
+ static VALUE cPinMarker;
13
+ static VALUE eIsolationError;
14
+
15
+ static size_t
16
+ containers_pin_marker_size(const void *pointer)
17
+ {
18
+ return pointer == NULL ? 0 : sizeof(containers_pin_marker_t);
19
+ }
20
+
21
+ static const rb_data_type_t containers_pin_marker_type = {
22
+ .wrap_struct_name = "Ractor::Containers pin marker",
23
+ .function = {
24
+ .dmark = NULL,
25
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
26
+ .dsize = containers_pin_marker_size,
27
+ .dcompact = NULL,
28
+ },
29
+ .parent = NULL,
30
+ .data = NULL,
31
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
32
+ };
33
+
34
+ static bool
35
+ containers_pin_marker_p(VALUE value)
36
+ {
37
+ return !NIL_P(value) && rb_typeddata_is_kind_of(value, &containers_pin_marker_type);
38
+ }
39
+
40
+ static void
41
+ containers_check_guard_target(VALUE object)
42
+ {
43
+ if (RB_SPECIAL_CONST_P(object)) {
44
+ rb_raise(rb_eTypeError, "cannot guard an immediate value");
45
+ }
46
+
47
+ rb_check_frozen(object);
48
+ if (rb_ractor_shareable_p(object)) {
49
+ rb_raise(eIsolationError, "cannot guard an object that is already shareable");
50
+ }
51
+ }
52
+
53
+ static VALUE
54
+ containers_new_pin_marker(void)
55
+ {
56
+ containers_pin_marker_t *marker_data;
57
+ VALUE marker = TypedData_Make_Struct(
58
+ cPinMarker,
59
+ containers_pin_marker_t,
60
+ &containers_pin_marker_type,
61
+ marker_data
62
+ );
63
+ marker_data->reserved = 0;
64
+ rb_obj_freeze(marker);
65
+ return marker;
66
+ }
67
+
68
+ static VALUE
69
+ containers_new_copy_marker(void)
70
+ {
71
+ VALUE pair = rb_funcall(rb_cIO, pipe_id, 0);
72
+ VALUE reader = rb_ary_entry(pair, 0);
73
+ VALUE writer = rb_ary_entry(pair, 1);
74
+
75
+ rb_funcall(reader, close_id, 0);
76
+ rb_funcall(writer, close_id, 0);
77
+ return reader;
78
+ }
79
+
80
+ static VALUE
81
+ containers_unshareable_pin(VALUE module, VALUE object)
82
+ {
83
+ VALUE marker;
84
+ (void)module;
85
+
86
+ if (RB_SPECIAL_CONST_P(object)) {
87
+ rb_raise(rb_eTypeError, "cannot guard an immediate value");
88
+ }
89
+
90
+ marker = rb_ivar_get(object, pin_marker_id);
91
+ if (containers_pin_marker_p(marker)) return object;
92
+
93
+ containers_check_guard_target(object);
94
+ rb_ivar_set(object, pin_marker_id, containers_new_pin_marker());
95
+ return object;
96
+ }
97
+
98
+ static VALUE
99
+ containers_unshareable_prevent_copyable(VALUE module, VALUE object)
100
+ {
101
+ VALUE marker;
102
+ (void)module;
103
+
104
+ if (RB_SPECIAL_CONST_P(object)) {
105
+ rb_raise(rb_eTypeError, "cannot guard an immediate value");
106
+ }
107
+
108
+ if (containers_pin_marker_p(rb_ivar_get(object, pin_marker_id))) return object;
109
+ marker = rb_ivar_get(object, copy_marker_id);
110
+ if (rb_obj_is_kind_of(marker, rb_cIO)) return object;
111
+
112
+ containers_check_guard_target(object);
113
+ rb_ivar_set(object, copy_marker_id, containers_new_copy_marker());
114
+ return object;
115
+ }
116
+
117
+ static VALUE
118
+ containers_unshareable_prevent_movable(VALUE module, VALUE object)
119
+ {
120
+ VALUE marker;
121
+ (void)module;
122
+
123
+ if (RB_SPECIAL_CONST_P(object)) {
124
+ rb_raise(rb_eTypeError, "cannot guard an immediate value");
125
+ }
126
+
127
+ if (containers_pin_marker_p(rb_ivar_get(object, pin_marker_id))) return object;
128
+ marker = rb_ivar_get(object, move_marker_id);
129
+ if (rb_obj_is_kind_of(marker, rb_cTime)) return object;
130
+
131
+ containers_check_guard_target(object);
132
+ rb_ivar_set(object, move_marker_id, rb_time_new(0, 0));
133
+ return object;
134
+ }
135
+
136
+ void
137
+ containers_init_unshareable(VALUE namespace)
138
+ {
139
+ VALUE module = rb_define_module_under(namespace, "Unshareable");
140
+
141
+ pin_marker_id = rb_intern("__ractor_containers_pin__");
142
+ copy_marker_id = rb_intern("__ractor_containers_prevent_copy__");
143
+ move_marker_id = rb_intern("__ractor_containers_prevent_move__");
144
+ pipe_id = rb_intern("pipe");
145
+ close_id = rb_intern("close");
146
+ eIsolationError = rb_const_get(rb_cRactor, rb_intern("IsolationError"));
147
+
148
+ cPinMarker = rb_class_new(rb_cObject);
149
+ rb_undef_alloc_func(cPinMarker);
150
+ rb_global_variable(&cPinMarker);
151
+
152
+ rb_define_singleton_method(module, "pin_to_current_ractor", containers_unshareable_pin, 1);
153
+ rb_define_singleton_method(module, "prevent_copyable", containers_unshareable_prevent_copyable, 1);
154
+ rb_define_singleton_method(module, "prevent_movable", containers_unshareable_prevent_movable, 1);
155
+ }
@@ -0,0 +1,234 @@
1
+ #ifndef FARCE_UNSHARED_IO_POOL_H
2
+ #define FARCE_UNSHARED_IO_POOL_H
3
+
4
+ #include "unshared_wait.h"
5
+ #include "ruby/io.h"
6
+ #include <errno.h>
7
+ #include <fcntl.h>
8
+ #include <unistd.h>
9
+
10
+ /* Each pair belongs to its Ruby IO objects. Native code never closes their
11
+ * descriptors. A slot stays active until io_wait returns or unwinds, so no
12
+ * scheduler can observe a descriptor reassigned to another pending wait. */
13
+ typedef struct farce_io_slot farce_io_slot_t;
14
+ struct farce_io_slot {
15
+ VALUE reader;
16
+ VALUE writer;
17
+ int read_fd;
18
+ int write_fd;
19
+ bool notified;
20
+ bool broken;
21
+ farce_io_slot_t *all_previous;
22
+ farce_io_slot_t *all_next;
23
+ farce_io_slot_t *idle_next;
24
+ farce_io_slot_t *previous;
25
+ farce_io_slot_t *next;
26
+ };
27
+
28
+ typedef struct {
29
+ farce_io_slot_t *all;
30
+ farce_io_slot_t *idle;
31
+ farce_io_slot_t *active;
32
+ size_t idle_count;
33
+ size_t active_count;
34
+ size_t size;
35
+ } farce_io_pool_t;
36
+
37
+ #define FARCE_IO_POOL_IDLE_LIMIT 2
38
+
39
+ static void
40
+ farce_io_pool_mark(farce_io_pool_t *pool)
41
+ {
42
+ for (farce_io_slot_t *slot = pool->all; slot; slot = slot->all_next) {
43
+ rb_gc_mark(slot->reader);
44
+ rb_gc_mark(slot->writer);
45
+ }
46
+ }
47
+
48
+ static void
49
+ farce_io_pool_free(farce_io_pool_t *pool)
50
+ {
51
+ farce_io_slot_t *slot = pool->all;
52
+ while (slot) {
53
+ farce_io_slot_t *next = slot->all_next;
54
+ /* Ruby finalizes the IO objects and their owned descriptors. */
55
+ free(slot);
56
+ slot = next;
57
+ }
58
+ }
59
+
60
+ static size_t
61
+ farce_io_pool_memsize(farce_io_pool_t *pool)
62
+ {
63
+ return pool->size * sizeof(farce_io_slot_t);
64
+ }
65
+
66
+ static VALUE
67
+ farce_io_pool_descriptor(VALUE io)
68
+ {
69
+ return INT2NUM(rb_io_descriptor(io));
70
+ }
71
+
72
+ static void
73
+ farce_io_pool_discard(farce_io_pool_t *pool, farce_io_slot_t *slot)
74
+ {
75
+ if (slot->all_previous) slot->all_previous->all_next = slot->all_next;
76
+ else pool->all = slot->all_next;
77
+ if (slot->all_next) slot->all_next->all_previous = slot->all_previous;
78
+ pool->size--;
79
+ VALUE reader = slot->reader;
80
+ VALUE writer = slot->writer;
81
+ free(slot);
82
+ /* Cleanup must close both ends without masking the original exception. */
83
+ VALUE error = rb_errinfo();
84
+ int state;
85
+ rb_protect(rb_io_close, reader, &state);
86
+ rb_set_errinfo(error);
87
+ rb_protect(rb_io_close, writer, &state);
88
+ rb_set_errinfo(error);
89
+ RB_GC_GUARD(reader);
90
+ RB_GC_GUARD(writer);
91
+ RB_GC_GUARD(error);
92
+ }
93
+
94
+ static farce_io_slot_t *
95
+ farce_io_pool_acquire(farce_io_pool_t *pool)
96
+ {
97
+ if (pool->idle) {
98
+ farce_io_slot_t *slot = pool->idle;
99
+ pool->idle = slot->idle_next;
100
+ pool->idle_count--;
101
+ slot->idle_next = NULL;
102
+ return slot;
103
+ }
104
+
105
+ /* IO.pipe supplies fully owned, exception-safe Ruby handles. If setup
106
+ * raises before the slot is linked, Ruby still owns both descriptors. */
107
+ VALUE pair = rb_funcall(rb_cIO, rb_intern("pipe"), 0);
108
+ VALUE reader = rb_ary_entry(pair, 0);
109
+ VALUE writer = rb_ary_entry(pair, 1);
110
+ int read_fd = rb_io_descriptor(reader);
111
+ int write_fd = rb_io_descriptor(writer);
112
+ #ifndef _WIN32
113
+ int read_flags = fcntl(read_fd, F_GETFL, 0);
114
+ int write_flags = fcntl(write_fd, F_GETFL, 0);
115
+ if (read_flags < 0 || write_flags < 0 ||
116
+ fcntl(read_fd, F_SETFL, read_flags | O_NONBLOCK) < 0 ||
117
+ fcntl(write_fd, F_SETFL, write_flags | O_NONBLOCK) < 0) rb_sys_fail("fcntl");
118
+ #endif
119
+ farce_io_slot_t *slot = calloc(1, sizeof(*slot));
120
+ if (!slot) rb_memerror();
121
+ slot->reader = reader;
122
+ slot->writer = writer;
123
+ slot->read_fd = read_fd;
124
+ slot->write_fd = write_fd;
125
+ slot->all_next = pool->all;
126
+ if (slot->all_next) slot->all_next->all_previous = slot;
127
+ pool->all = slot;
128
+ pool->size++;
129
+ RB_GC_GUARD(pair);
130
+ return slot;
131
+ }
132
+
133
+ static void
134
+ farce_io_pool_release(farce_io_pool_t *pool, farce_io_slot_t *slot)
135
+ {
136
+ VALUE error = rb_errinfo();
137
+ int state;
138
+ VALUE descriptor = rb_protect(farce_io_pool_descriptor, slot->reader, &state);
139
+ rb_set_errinfo(error);
140
+ if (state || NUM2INT(descriptor) != slot->read_fd) slot->broken = true;
141
+ if (!slot->broken && slot->notified) {
142
+ unsigned char byte;
143
+ ssize_t count;
144
+ do { count = read(slot->read_fd, &byte, 1); } while (count < 0 && errno == EINTR);
145
+ if (count == 0 || (count < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) slot->broken = true;
146
+ }
147
+ slot->notified = false;
148
+ if (slot->broken || pool->idle_count == FARCE_IO_POOL_IDLE_LIMIT) {
149
+ farce_io_pool_discard(pool, slot);
150
+ }
151
+ else {
152
+ slot->idle_next = pool->idle;
153
+ pool->idle = slot;
154
+ pool->idle_count++;
155
+ }
156
+ RB_GC_GUARD(error);
157
+ }
158
+
159
+ static void
160
+ farce_io_pool_notify(farce_io_pool_t *pool)
161
+ {
162
+ const unsigned char byte = 1;
163
+ for (farce_io_slot_t *slot = pool->active; slot; slot = slot->next) {
164
+ if (slot->notified || slot->broken) continue;
165
+ ssize_t count;
166
+ do { count = write(slot->write_fd, &byte, 1); } while (count < 0 && errno == EINTR);
167
+ if (count == 1 || (count < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) slot->notified = true;
168
+ else slot->broken = true;
169
+ }
170
+ }
171
+
172
+ typedef struct {
173
+ farce_io_pool_t *pool;
174
+ farce_io_slot_t *slot;
175
+ bool (*ready)(void *);
176
+ void *data;
177
+ bool finite;
178
+ double deadline;
179
+ bool registered;
180
+ bool completed;
181
+ } farce_io_pool_wait_t;
182
+
183
+ static VALUE
184
+ farce_io_pool_wait_body(VALUE opaque)
185
+ {
186
+ farce_io_pool_wait_t *wait = (farce_io_pool_wait_t *)opaque;
187
+ wait->slot = farce_io_pool_acquire(wait->pool);
188
+ /* Acquiring a new pipe may run Ruby. Recheck the caller's condition before
189
+ * registering under the GVL, including notifications during allocation. */
190
+ if (wait->ready(wait->data)) { wait->completed = true; return Qtrue; }
191
+ double remaining = wait->finite ? wait->deadline - farce_unshared_wait_now() : 0;
192
+ if (wait->finite && remaining <= 0) { wait->completed = true; return Qfalse; }
193
+ farce_io_slot_t *slot = wait->slot;
194
+ /* Older CRuby versions dispatch io_wait without checking a closed IO.
195
+ * Validate before handing a cached object back to the scheduler. */
196
+ if (rb_io_descriptor(slot->reader) != slot->read_fd) rb_raise(rb_eIOError, "queue wait descriptor changed");
197
+ slot->previous = NULL;
198
+ slot->next = wait->pool->active;
199
+ if (slot->next) slot->next->previous = slot;
200
+ wait->pool->active = slot;
201
+ wait->pool->active_count++;
202
+ wait->registered = true;
203
+ VALUE result = rb_io_wait(slot->reader, INT2NUM(RUBY_IO_READABLE), wait->finite ? DBL2NUM(remaining) : Qnil);
204
+ wait->completed = true;
205
+ return result;
206
+ }
207
+
208
+ static VALUE
209
+ farce_io_pool_wait_cleanup(VALUE opaque)
210
+ {
211
+ farce_io_pool_wait_t *wait = (farce_io_pool_wait_t *)opaque;
212
+ if (!wait->slot) return Qnil;
213
+ farce_io_slot_t *slot = wait->slot;
214
+ if (wait->registered) {
215
+ if (slot->previous) slot->previous->next = slot->next;
216
+ else wait->pool->active = slot->next;
217
+ if (slot->next) slot->next->previous = slot->previous;
218
+ wait->pool->active_count--;
219
+ }
220
+ /* An interrupted scheduler may have started closing its IO. Finish that
221
+ * close after rb_io_wait unwinds, and never reuse an exceptional slot. */
222
+ if (!wait->completed) slot->broken = true;
223
+ farce_io_pool_release(wait->pool, slot);
224
+ return Qnil;
225
+ }
226
+
227
+ static bool
228
+ farce_io_pool_wait(farce_io_pool_t *pool, bool finite, double deadline, bool (*ready)(void *), void *data)
229
+ {
230
+ farce_io_pool_wait_t wait = {.pool = pool, .finite = finite, .deadline = deadline, .ready = ready, .data = data};
231
+ return RTEST(rb_ensure(farce_io_pool_wait_body, (VALUE)&wait, farce_io_pool_wait_cleanup, (VALUE)&wait));
232
+ }
233
+
234
+ #endif
@@ -0,0 +1,263 @@
1
+ #include "unshared_io_pool.h"
2
+
3
+ static VALUE cUnsharedSignal;
4
+ static VALUE cUnsharedIOSignal;
5
+ static VALUE cUnsharedBlockSignal;
6
+
7
+ struct containers_unshared_signal {
8
+ farce_unshared_wait_list_t waiters;
9
+ uint64_t generation;
10
+ farce_io_pool_t fiber_pool;
11
+ size_t fiber_waiters;
12
+ bool initialized;
13
+ bool fiber_io;
14
+ };
15
+
16
+ static void
17
+ unshared_signal_mark(void *pointer)
18
+ {
19
+ containers_unshared_signal_t *signal = pointer;
20
+ farce_unshared_wait_mark(&signal->waiters);
21
+ farce_io_pool_mark(&signal->fiber_pool);
22
+ }
23
+
24
+ static void
25
+ unshared_signal_free(void *pointer)
26
+ {
27
+ containers_unshared_signal_t *signal = pointer;
28
+ farce_io_pool_free(&signal->fiber_pool);
29
+ ruby_xfree(signal);
30
+ }
31
+
32
+ static size_t
33
+ unshared_signal_memsize(const void *pointer)
34
+ {
35
+ const containers_unshared_signal_t *signal = pointer;
36
+ return signal ? sizeof(*signal) + signal->fiber_pool.size * sizeof(farce_io_slot_t) : 0;
37
+ }
38
+
39
+ static const rb_data_type_t unshared_signal_type = {
40
+ .wrap_struct_name = "Farce::Internal::UnsharedSignal",
41
+ .function = {
42
+ .dmark = unshared_signal_mark,
43
+ .dfree = unshared_signal_free,
44
+ .dsize = unshared_signal_memsize,
45
+ },
46
+ };
47
+
48
+ static VALUE
49
+ unshared_signal_allocate_mode(VALUE klass, bool fiber_io)
50
+ {
51
+ containers_unshared_signal_t *signal;
52
+ VALUE self = TypedData_Make_Struct(klass, containers_unshared_signal_t, &unshared_signal_type, signal);
53
+ signal->fiber_io = fiber_io;
54
+ return self;
55
+ }
56
+
57
+ static VALUE unshared_signal_allocate(VALUE klass) { return unshared_signal_allocate_mode(klass, FARCE_UNSHARED_FIBER_IO); }
58
+ static VALUE unshared_io_signal_allocate(VALUE klass) { return unshared_signal_allocate_mode(klass, true); }
59
+ static VALUE unshared_block_signal_allocate(VALUE klass) { return unshared_signal_allocate_mode(klass, false); }
60
+
61
+ static containers_unshared_signal_t *
62
+ unshared_signal_get(VALUE self)
63
+ {
64
+ containers_unshared_signal_t *signal;
65
+ TypedData_Get_Struct(self, containers_unshared_signal_t, &unshared_signal_type, signal);
66
+ if (!signal->initialized) rb_raise(rb_eRuntimeError, "uninitialized Signal");
67
+ return signal;
68
+ }
69
+
70
+ /* Only the built-in classes bypass Ruby dispatch. Further subclasses retain
71
+ * ordinary protocol dispatch, including overrides. */
72
+ containers_unshared_signal_t *
73
+ containers_unshared_signal_get_if_exact(VALUE self)
74
+ {
75
+ VALUE klass = CLASS_OF(self);
76
+ return klass == cUnsharedSignal || klass == cUnsharedIOSignal || klass == cUnsharedBlockSignal ?
77
+ unshared_signal_get(self) : NULL;
78
+ }
79
+
80
+ bool
81
+ containers_unshared_fiber_io(VALUE mode)
82
+ {
83
+ if (mode == Qundef || mode == ID2SYM(rb_intern("auto"))) return FARCE_UNSHARED_FIBER_IO;
84
+ if (mode == ID2SYM(rb_intern("io"))) return true;
85
+ if (mode == ID2SYM(rb_intern("block"))) return false;
86
+ rb_raise(rb_eArgError, "fiber_wait must be :auto, :io, or :block");
87
+ }
88
+
89
+ /* Comparison-lock contention follows even a user subclass's native waiting
90
+ * policy. Inspecting it here never calls Ruby during queue publication. */
91
+ bool
92
+ containers_unshared_signal_uses_fiber_io(VALUE self)
93
+ {
94
+ if (!rb_typeddata_is_kind_of(self, &unshared_signal_type)) return FARCE_UNSHARED_FIBER_IO;
95
+ return unshared_signal_get(self)->fiber_io;
96
+ }
97
+
98
+ static VALUE
99
+ unshared_signal_for(VALUE klass, VALUE mode)
100
+ {
101
+ (void)klass;
102
+ bool fiber_io = containers_unshared_fiber_io(mode);
103
+ VALUE selected = mode == ID2SYM(rb_intern("auto")) ? cUnsharedSignal :
104
+ (fiber_io ? cUnsharedIOSignal : cUnsharedBlockSignal);
105
+ return rb_class_new_instance(0, NULL, selected);
106
+ }
107
+
108
+ static VALUE
109
+ unshared_signal_fiber_wait(VALUE self)
110
+ {
111
+ return ID2SYM(rb_intern(unshared_signal_get(self)->fiber_io ? "io" : "block"));
112
+ }
113
+
114
+ bool
115
+ containers_unshared_signal_has_waiters(containers_unshared_signal_t *signal)
116
+ {
117
+ return signal->waiters.count != 0 || signal->fiber_waiters != 0;
118
+ }
119
+
120
+ void
121
+ containers_unshared_signal_notify(containers_unshared_signal_t *signal)
122
+ {
123
+ signal->generation++;
124
+ /* Pipe notification does not call Ruby. Notify fibers before direct
125
+ * scheduler callbacks, which may yield or raise. */
126
+ if (signal->fiber_waiters) farce_io_pool_notify(&signal->fiber_pool);
127
+ farce_unshared_wait_notify_all(&signal->waiters);
128
+ }
129
+
130
+ static VALUE
131
+ unshared_signal_initialize(VALUE self)
132
+ {
133
+ containers_unshared_signal_t *signal;
134
+ TypedData_Get_Struct(self, containers_unshared_signal_t, &unshared_signal_type, signal);
135
+ if (signal->initialized) rb_raise(rb_eRuntimeError, "Signal is already initialized");
136
+ rb_check_frozen(self);
137
+ signal->initialized = true;
138
+ rb_obj_freeze(self);
139
+ return self;
140
+ }
141
+
142
+ static VALUE
143
+ unshared_signal_initialize_copy(VALUE self, VALUE other)
144
+ {
145
+ (void)self;
146
+ (void)other;
147
+ rb_raise(rb_eTypeError, "cannot copy Signal");
148
+ }
149
+
150
+ static VALUE
151
+ unshared_signal_generation(VALUE self)
152
+ {
153
+ return ULL2NUM(unshared_signal_get(self)->generation);
154
+ }
155
+
156
+ static VALUE
157
+ unshared_signal_num_waiting(VALUE self)
158
+ {
159
+ containers_unshared_signal_t *signal = unshared_signal_get(self);
160
+ return SIZET2NUM(signal->waiters.count + signal->fiber_waiters);
161
+ }
162
+
163
+ static VALUE
164
+ unshared_signal_broadcast(VALUE self)
165
+ {
166
+ containers_unshared_signal_t *signal = unshared_signal_get(self);
167
+ uint64_t generation = signal->generation + 1;
168
+ containers_unshared_signal_notify(signal);
169
+ return ULL2NUM(generation);
170
+ }
171
+
172
+ typedef struct {
173
+ containers_unshared_signal_t *signal;
174
+ uint64_t observed;
175
+ double deadline;
176
+ bool finite;
177
+ } unshared_signal_fiber_wait_t;
178
+
179
+ static bool
180
+ unshared_signal_fiber_ready(void *opaque)
181
+ {
182
+ unshared_signal_fiber_wait_t *wait = opaque;
183
+ return wait->signal->generation != wait->observed;
184
+ }
185
+
186
+ static VALUE
187
+ unshared_signal_fiber_wait_body(VALUE opaque)
188
+ {
189
+ unshared_signal_fiber_wait_t *wait = (unshared_signal_fiber_wait_t *)opaque;
190
+ return farce_io_pool_wait(&wait->signal->fiber_pool, wait->finite, wait->deadline,
191
+ unshared_signal_fiber_ready, wait) ? Qtrue : Qfalse;
192
+ }
193
+
194
+ static VALUE
195
+ unshared_signal_fiber_wait_cleanup(VALUE opaque)
196
+ {
197
+ unshared_signal_fiber_wait_t *wait = (unshared_signal_fiber_wait_t *)opaque;
198
+ wait->signal->fiber_waiters--;
199
+ return Qnil;
200
+ }
201
+
202
+ static VALUE
203
+ unshared_signal_wait(int argc, VALUE *argv, VALUE self)
204
+ {
205
+ VALUE observed_value = Qnil;
206
+ VALUE keywords = Qnil;
207
+ VALUE keyword_values[1] = {Qundef};
208
+ ID keyword_ids[] = {rb_intern("timeout")};
209
+ rb_scan_args(argc, argv, "01:", &observed_value, &keywords);
210
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
211
+
212
+ bool snapshot_at_entry = NIL_P(observed_value);
213
+ uint64_t observed = snapshot_at_entry ? 0 : NUM2ULL(observed_value);
214
+ VALUE timeout = keyword_values[0] == Qundef ? Qnil : keyword_values[0];
215
+ bool finite = !NIL_P(timeout);
216
+ double deadline = 0;
217
+ if (finite) {
218
+ double seconds = NUM2DBL(timeout);
219
+ if (!isfinite(seconds) || seconds < 0) {
220
+ rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
221
+ }
222
+ deadline = farce_unshared_wait_now() + seconds;
223
+ }
224
+ containers_unshared_signal_t *signal = unshared_signal_get(self);
225
+ if (snapshot_at_entry) observed = signal->generation;
226
+
227
+ /* Coercion above may run Ruby. The generation check and registration below
228
+ * run together under the GVL, so a broadcast cannot slip between them. */
229
+ while (signal->generation == observed) {
230
+ if (finite && farce_unshared_wait_now() >= deadline) {
231
+ return rb_block_given_p() ? rb_yield_values(0) : Qnil;
232
+ }
233
+ if (signal->fiber_io && !NIL_P(rb_fiber_scheduler_current())) {
234
+ /* Reuse owned pipe objects after each scheduler wait unwinds. */
235
+ unshared_signal_fiber_wait_t wait = {signal, observed, deadline, finite};
236
+ signal->fiber_waiters++;
237
+ rb_ensure(unshared_signal_fiber_wait_body, (VALUE)&wait,
238
+ unshared_signal_fiber_wait_cleanup, (VALUE)&wait);
239
+ }
240
+ else farce_unshared_wait(&signal->waiters, self, finite, deadline);
241
+ }
242
+ return ULL2NUM(signal->generation);
243
+ }
244
+
245
+ void
246
+ containers_init_unshared_signal(VALUE namespace)
247
+ {
248
+ rb_define_const(namespace, "UNSHARED_FIBER_IO", FARCE_UNSHARED_FIBER_IO ? Qtrue : Qfalse);
249
+ cUnsharedSignal = rb_define_class_under(namespace, "UnsharedSignal", rb_cObject);
250
+ rb_define_alloc_func(cUnsharedSignal, unshared_signal_allocate);
251
+ cUnsharedIOSignal = rb_define_class_under(namespace, "UnsharedIOSignal", cUnsharedSignal);
252
+ rb_define_alloc_func(cUnsharedIOSignal, unshared_io_signal_allocate);
253
+ cUnsharedBlockSignal = rb_define_class_under(namespace, "UnsharedBlockSignal", cUnsharedSignal);
254
+ rb_define_alloc_func(cUnsharedBlockSignal, unshared_block_signal_allocate);
255
+ rb_define_singleton_method(cUnsharedSignal, "for", unshared_signal_for, 1);
256
+ rb_define_method(cUnsharedSignal, "fiber_wait", unshared_signal_fiber_wait, 0);
257
+ rb_define_private_method(cUnsharedSignal, "initialize", unshared_signal_initialize, 0);
258
+ rb_define_private_method(cUnsharedSignal, "initialize_copy", unshared_signal_initialize_copy, 1);
259
+ rb_define_method(cUnsharedSignal, "generation", unshared_signal_generation, 0);
260
+ rb_define_method(cUnsharedSignal, "num_waiting", unshared_signal_num_waiting, 0);
261
+ rb_define_method(cUnsharedSignal, "broadcast", unshared_signal_broadcast, 0);
262
+ rb_define_method(cUnsharedSignal, "wait", unshared_signal_wait, -1);
263
+ }