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
data/ext/farce/dict.h ADDED
@@ -0,0 +1,152 @@
1
+ /*
2
+ * Dictionary Abstract Data Type
3
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
4
+ *
5
+ * Free Software License:
6
+ *
7
+ * All rights are reserved by the author, with the following exceptions:
8
+ * Permission is granted to freely reproduce and distribute this software,
9
+ * possibly in exchange for a fee, provided that this copyright notice appears
10
+ * intact. Permission is also granted to adapt this software to produce
11
+ * derivative works, as long as the modified versions carry this copyright
12
+ * notice and additional notices stating that the work has been modified.
13
+ * This source code may be translated into executable form and incorporated
14
+ * into proprietary software; there is no requirement for such software to
15
+ * contain a copyright notice related to this source.
16
+ *
17
+ * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $
18
+ * $Name: kazlib_1_20 $
19
+ */
20
+
21
+ /*
22
+ * Modified for Ruby/RBTree.
23
+ * Modified by Farce to expose reusable unique-key insertion positions.
24
+ */
25
+
26
+ #ifndef DICT_H
27
+ #define DICT_H
28
+
29
+ #include <limits.h>
30
+ #ifdef KAZLIB_SIDEEFFECT_DEBUG
31
+ #include "sfx.h"
32
+ #endif
33
+
34
+ /*
35
+ * Blurb for inclusion into C++ translation units
36
+ */
37
+
38
+ #ifdef __cplusplus
39
+ extern "C" {
40
+ #endif
41
+
42
+ typedef unsigned long dictcount_t;
43
+ #define DICTCOUNT_T_MAX ULONG_MAX
44
+
45
+ /*
46
+ * The dictionary is implemented as a red-black tree
47
+ */
48
+
49
+ typedef enum { dnode_red, dnode_black } dnode_color_t;
50
+
51
+ typedef struct dnode_t {
52
+ #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
53
+ struct dnode_t *dict_left;
54
+ struct dnode_t *dict_right;
55
+ struct dnode_t *dict_parent;
56
+ dnode_color_t dict_color;
57
+ const void *dict_key;
58
+ void *dict_data;
59
+ #else
60
+ int dict_dummy;
61
+ #endif
62
+ } dnode_t;
63
+
64
+ typedef int (*dict_comp_t)(const void *, const void *, void *);
65
+ typedef dnode_t *(*dnode_alloc_t)(void *);
66
+ typedef void (*dnode_free_t)(dnode_t *, void *);
67
+
68
+ typedef struct dict_t {
69
+ #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
70
+ dnode_t dict_nilnode;
71
+ dictcount_t dict_nodecount;
72
+ dict_comp_t dict_compare;
73
+ dnode_alloc_t dict_allocnode;
74
+ dnode_free_t dict_freenode;
75
+ void *dict_context;
76
+ int dict_dupes;
77
+ #else
78
+ int dict_dummmy;
79
+ #endif
80
+ } dict_t;
81
+
82
+ typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
83
+
84
+ typedef struct dict_load_t {
85
+ #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
86
+ dict_t *dict_dictptr;
87
+ dnode_t dict_nilnode;
88
+ #else
89
+ int dict_dummmy;
90
+ #endif
91
+ } dict_load_t;
92
+
93
+ extern dict_t *dict_create(dict_comp_t);
94
+ extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
95
+ extern void dict_destroy(dict_t *);
96
+ extern void dict_free_nodes(dict_t *);
97
+ extern void dict_free(dict_t *);
98
+ extern dict_t *dict_init(dict_t *, dict_comp_t);
99
+ extern void dict_init_like(dict_t *, const dict_t *);
100
+ extern int dict_verify(dict_t *);
101
+ extern int dict_similar(const dict_t *, const dict_t *);
102
+ extern dnode_t *dict_lookup(dict_t *, const void *);
103
+ extern dnode_t *dict_lower_bound(dict_t *, const void *);
104
+ extern dnode_t *dict_upper_bound(dict_t *, const void *);
105
+ extern int dict_insert(dict_t *, dnode_t *, const void *);
106
+ /* Farce extension: find a unique key or retain its insertion position. The
107
+ * caller must keep the tree and key ordering unchanged until insert_at. */
108
+ extern dnode_t *dict_lookup_position(dict_t *, const void *, dnode_t **, int *);
109
+ extern int dict_insert_at(dict_t *, dnode_t *, const void *, dnode_t *, int);
110
+ extern dnode_t *dict_delete(dict_t *, dnode_t *);
111
+ extern int dict_alloc_insert(dict_t *, const void *, void *);
112
+ extern void dict_delete_free(dict_t *, dnode_t *);
113
+ extern dnode_t *dict_first(dict_t *);
114
+ extern dnode_t *dict_last(dict_t *);
115
+ extern dnode_t *dict_next(dict_t *, dnode_t *);
116
+ extern dnode_t *dict_prev(dict_t *, dnode_t *);
117
+ extern dictcount_t dict_count(dict_t *);
118
+ extern int dict_isempty(dict_t *);
119
+ extern int dict_isfull(dict_t *);
120
+ extern int dict_contains(dict_t *, dnode_t *);
121
+ extern void dict_allow_dupes(dict_t *);
122
+ extern int dnode_is_in_a_dict(dnode_t *);
123
+ extern dnode_t *dnode_create(void *);
124
+ extern dnode_t *dnode_init(dnode_t *, void *);
125
+ extern void dnode_destroy(dnode_t *);
126
+ extern void *dnode_get(dnode_t *);
127
+ extern const void *dnode_getkey(dnode_t *);
128
+ extern void dnode_put(dnode_t *, void *);
129
+ extern void dict_process(dict_t *, void *, dnode_process_t);
130
+ extern void dict_load_begin(dict_load_t *, dict_t *);
131
+ extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
132
+ extern void dict_load_end(dict_load_t *);
133
+ extern void dict_merge(dict_t *, dict_t *);
134
+
135
+ #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
136
+ #ifdef KAZLIB_SIDEEFFECT_DEBUG
137
+ #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
138
+ #else
139
+ #define dict_isfull(D) ((D)->dict_nodecount == DICTCOUNT_T_MAX)
140
+ #endif
141
+ #define dict_count(D) ((D)->dict_nodecount)
142
+ #define dict_isempty(D) ((D)->dict_nodecount == 0)
143
+ #define dnode_get(N) ((N)->dict_data)
144
+ #define dnode_getkey(N) ((N)->dict_key)
145
+ #define dnode_put(N, X) ((N)->dict_data = (X))
146
+ #endif
147
+
148
+ #ifdef __cplusplus
149
+ }
150
+ #endif
151
+
152
+ #endif
@@ -0,0 +1,237 @@
1
+ #include "reactor.h"
2
+ #include <unistd.h>
3
+ #include <poll.h>
4
+ #include <errno.h>
5
+ #include <limits.h>
6
+ #include <fcntl.h>
7
+ #include <math.h>
8
+ #ifdef HAVE_SYS_EPOLL_H
9
+ #include <sys/epoll.h>
10
+ #endif
11
+ #ifdef HAVE_SYS_EVENT_H
12
+ #include <sys/event.h>
13
+ #endif
14
+
15
+ static struct timespec relative_time(double seconds) {
16
+ struct timespec t;
17
+ t.tv_sec = (time_t)seconds;
18
+ t.tv_nsec = (long)((seconds - (double)t.tv_sec) * 1e9);
19
+ return t;
20
+ }
21
+
22
+ int fs_driver_init(fs_state *s, int driver) {
23
+ s->driver = driver;
24
+ #ifdef HAVE_SYS_EPOLL_H
25
+ if (driver == FS_EPOLL) {
26
+ s->descriptor = epoll_create1(EPOLL_CLOEXEC);
27
+ return s->descriptor < 0 ? errno : 0;
28
+ }
29
+ #endif
30
+ #ifdef HAVE_SYS_EVENT_H
31
+ if (driver == FS_KQUEUE) {
32
+ s->descriptor = kqueue();
33
+ return s->descriptor < 0 ? errno : 0;
34
+ }
35
+ #endif
36
+ #ifdef FARCE_URING
37
+ if (driver == FS_URING_DRIVER) {
38
+ int result = io_uring_queue_init(1024, &s->ring, 0);
39
+ if (result < 0) return -result;
40
+ s->ring_live = 1;
41
+ return 0;
42
+ }
43
+ #endif
44
+ return ENOSYS;
45
+ }
46
+
47
+ #ifdef FARCE_URING
48
+ static struct io_uring_sqe *sqe(fs_state *s) {
49
+ struct io_uring_sqe *entry = io_uring_get_sqe(&s->ring);
50
+ if (!entry) {
51
+ int result = io_uring_submit(&s->ring);
52
+ if (result < 0) { errno = -result; return NULL; }
53
+ entry = io_uring_get_sqe(&s->ring);
54
+ }
55
+ if (!entry) errno = EBUSY;
56
+ return entry;
57
+ }
58
+ #endif
59
+
60
+ int fs_driver_receive(fs_state *s, fs_wait *w, int fd, void *buffer, size_t size) {
61
+ #ifdef FARCE_URING
62
+ struct io_uring_sqe *entry = sqe(s);
63
+ if (!entry) return errno;
64
+ io_uring_prep_recv(entry, fd, buffer, size > INT_MAX ? INT_MAX : (unsigned)size, 0);
65
+ #ifdef IORING_RECVSEND_POLL_FIRST
66
+ entry->ioprio |= IORING_RECVSEND_POLL_FIRST;
67
+ #endif
68
+ entry->user_data = FS_RECEIVE | NUM2ULL(w->token);
69
+ w->receive_pending = 1;
70
+ // Consume every queued SQE while holding the GVL, before Ruby can close or
71
+ // reopen this fd. SQPOLL is not enabled. The kernel then owns its file ref.
72
+ while (io_uring_sq_ready(&s->ring)) {
73
+ int result = io_uring_submit(&s->ring);
74
+ if (result < 0 && result != -EINTR) return -result;
75
+ }
76
+ return 0;
77
+ #else
78
+ return ENOSYS;
79
+ #endif
80
+ }
81
+
82
+ int fs_driver_update(fs_state *s, fs_reg *reg, int events) {
83
+ uint64_t previous = reg->generation;
84
+ uint64_t generation = ++s->generation;
85
+ int old = reg->events;
86
+ #ifdef HAVE_SYS_EPOLL_H
87
+ if (s->driver == FS_EPOLL) {
88
+ struct epoll_event ev = {.data.u64 = generation};
89
+ if (events & FS_READ) ev.events |= EPOLLIN | EPOLLRDHUP;
90
+ if (events & FS_WRITE) ev.events |= EPOLLOUT;
91
+ if (events & FS_PRI) ev.events |= EPOLLPRI;
92
+ int action = !events ? EPOLL_CTL_DEL : old ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
93
+ if (epoll_ctl(s->descriptor, action, reg->fd, &ev) < 0 &&
94
+ !(events == 0 && (errno == ENOENT || errno == EBADF))) return errno;
95
+ }
96
+ #endif
97
+ #ifdef HAVE_SYS_EVENT_H
98
+ if (s->driver == FS_KQUEUE) {
99
+ struct timespec zero = {0, 0};
100
+ for (int bit = FS_READ; bit <= FS_WRITE; bit <<= 1) {
101
+ if (!(old & bit) && !(events & bit)) continue;
102
+ if (reg->poll_events & bit) continue;
103
+ if (bit != FS_PRI) {
104
+ if (s->change_count == s->change_capacity) {
105
+ s->change_capacity = s->change_capacity ? s->change_capacity * 2 : 256;
106
+ REALLOC_N(s->changes, struct kevent, s->change_capacity);
107
+ }
108
+ struct kevent *change = &s->changes[s->change_count++];
109
+ EV_SET(change, reg->fd, bit == FS_READ ? EVFILT_READ : EVFILT_WRITE,
110
+ events & bit ? EV_ADD | EV_ENABLE : EV_DELETE, 0, 0, (void *)(uintptr_t)generation);
111
+ continue;
112
+ }
113
+ struct kevent change, result;
114
+ EV_SET(&change, reg->fd, bit == FS_READ ? EVFILT_READ : bit == FS_WRITE ? EVFILT_WRITE : EVFILT_EXCEPT,
115
+ (events & bit ? EV_ADD | EV_ENABLE : EV_DELETE) | EV_RECEIPT, bit == FS_PRI ? NOTE_OOB : 0, 0,
116
+ (void *)(uintptr_t)generation);
117
+ int count = kevent(s->descriptor, &change, 1, &result, 1, &zero);
118
+ if (count < 0) return errno;
119
+ // EVFILT_EXCEPT is not supported by every descriptor (e.g. pipes).
120
+ // Retain the logical interest and snapshot it with poll instead.
121
+ if (count && bit == FS_PRI && (events & bit) &&
122
+ (result.data == EINVAL || result.data == ENOTSUP)) {
123
+ reg->poll_events |= bit;
124
+ s->polling_regs++;
125
+ continue;
126
+ }
127
+ if (count && result.data && !(!(events & bit) &&
128
+ (result.data == ENOENT || result.data == EBADF))) return (int)result.data;
129
+ }
130
+ }
131
+ #endif
132
+ #ifdef FARCE_URING
133
+ if (s->driver == FS_URING_DRIVER) {
134
+ if (old) {
135
+ struct io_uring_sqe *entry = sqe(s);
136
+ if (!entry) return errno;
137
+ io_uring_prep_poll_remove(entry, previous);
138
+ entry->user_data = 0; // cancellation acknowledgement has no storage pointer
139
+ }
140
+ if (events) {
141
+ struct io_uring_sqe *entry = sqe(s);
142
+ if (!entry) return errno;
143
+ unsigned mask = 0;
144
+ if (events & FS_READ) mask |= POLLIN;
145
+ if (events & FS_WRITE) mask |= POLLOUT;
146
+ if (events & FS_PRI) mask |= POLLPRI;
147
+ io_uring_prep_poll_add(entry, reg->fd, mask);
148
+ entry->user_data = generation;
149
+ }
150
+ }
151
+ #endif
152
+ if (previous) { st_data_t key = previous; st_delete(s->generations, &key, NULL); }
153
+ reg->generation = generation;
154
+ reg->events = events;
155
+ if (events) st_insert(s->generations, generation, (st_data_t)reg);
156
+ return 0;
157
+ }
158
+
159
+ void *fs_driver_poll(void *data) {
160
+ fs_state *s = data;
161
+ s->event_count = s->poll_error = 0;
162
+ struct timespec t = relative_time(s->timeout < 0 ? 0 : s->timeout);
163
+ #ifdef HAVE_SYS_EPOLL_H
164
+ if (s->driver == FS_EPOLL) {
165
+ struct epoll_event events[FS_EVENTS];
166
+ int n;
167
+ #ifdef HAVE_EPOLL_PWAIT2
168
+ n = epoll_pwait2(s->descriptor, events, FS_EVENTS, s->timeout < 0 ? NULL : &t, NULL);
169
+ if (n < 0 && errno == ENOSYS)
170
+ #endif
171
+ n = epoll_wait(s->descriptor, events, FS_EVENTS,
172
+ s->timeout < 0 ? -1 : (int)fmin(ceil(s->timeout * 1000), INT_MAX));
173
+ if (n < 0) { s->poll_error = errno; return NULL; }
174
+ for (int i = 0; i < n; i++) {
175
+ uint32_t e = events[i].events;
176
+ int mask = 0;
177
+ if (e & (EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR)) mask |= FS_READ;
178
+ if (e & (EPOLLOUT | EPOLLHUP | EPOLLERR)) mask |= FS_WRITE;
179
+ if (e & EPOLLPRI) mask |= FS_PRI;
180
+ s->events[s->event_count++] = (fs_event){events[i].data.u64, mask, 0};
181
+ }
182
+ }
183
+ #endif
184
+ #ifdef HAVE_SYS_EVENT_H
185
+ if (s->driver == FS_KQUEUE) {
186
+ struct kevent events[FS_EVENTS];
187
+ int n = kevent(s->descriptor, s->changes, s->change_count, events, FS_EVENTS, s->timeout < 0 ? NULL : &t);
188
+ if (n < 0) { s->poll_error = errno; return NULL; }
189
+ s->change_count = 0;
190
+ for (int i = 0; i < n; i++)
191
+ s->events[s->event_count++] = (fs_event){(uint64_t)(uintptr_t)events[i].udata,
192
+ events[i].filter == EVFILT_READ ? FS_READ : events[i].filter == EVFILT_WRITE ? FS_WRITE : FS_PRI,
193
+ events[i].flags & EV_ERROR ? (int)events[i].data : 0};
194
+ }
195
+ #endif
196
+ #ifdef FARCE_URING
197
+ if (s->driver == FS_URING_DRIVER) {
198
+ int result = io_uring_submit(&s->ring);
199
+ if (result < 0) { s->poll_error = -result; return NULL; }
200
+ struct io_uring_cqe *cqe;
201
+ if (s->timeout != 0 && io_uring_peek_cqe(&s->ring, &cqe) == -EAGAIN) {
202
+ struct __kernel_timespec kt = {t.tv_sec, t.tv_nsec};
203
+ result = s->timeout < 0 ? io_uring_wait_cqe(&s->ring, &cqe) :
204
+ io_uring_wait_cqe_timeout(&s->ring, &cqe, &kt);
205
+ if (result < 0 && result != -ETIME && result != -EINTR) s->poll_error = -result;
206
+ }
207
+ unsigned head, consumed = 0;
208
+ io_uring_for_each_cqe(&s->ring, head, cqe) {
209
+ if (s->event_count == FS_EVENTS) break;
210
+ consumed++;
211
+ if (cqe->user_data && cqe->user_data != LIBURING_UDATA_TIMEOUT) {
212
+ int e = cqe->res, mask = 0;
213
+ if (!(cqe->user_data & FS_RECEIVE) && e >= 0) {
214
+ if (e & (POLLIN | POLLHUP | POLLERR)) mask |= FS_READ;
215
+ if (e & (POLLOUT | POLLHUP | POLLERR)) mask |= FS_WRITE;
216
+ if (e & POLLPRI) mask |= FS_PRI;
217
+ }
218
+ s->events[s->event_count++] = (fs_event){cqe->user_data, mask,
219
+ cqe->user_data & FS_RECEIVE ? e : e < 0 && e != -ECANCELED ? -e : 0};
220
+ }
221
+ }
222
+ io_uring_cq_advance(&s->ring, consumed);
223
+ }
224
+ #endif
225
+ return NULL;
226
+ }
227
+
228
+ void fs_driver_close(fs_state *s) {
229
+ #ifdef HAVE_SYS_EVENT_H
230
+ xfree(s->changes); s->changes = NULL;
231
+ s->change_count = s->change_capacity = 0;
232
+ #endif
233
+ #ifdef FARCE_URING
234
+ if (s->ring_live) { io_uring_queue_exit(&s->ring); s->ring_live = 0; }
235
+ #endif
236
+ if (s->descriptor >= 0) { close(s->descriptor); s->descriptor = -1; }
237
+ }
@@ -0,0 +1,299 @@
1
+ #include "containers.h"
2
+ #include "ruby/io.h"
3
+
4
+ #include <errno.h>
5
+ #include <fcntl.h>
6
+ #include <math.h>
7
+ #include <time.h>
8
+ #include <unistd.h>
9
+
10
+ static VALUE cExchanger;
11
+
12
+ typedef struct {
13
+ int read_fd;
14
+ int write_fd;
15
+ } exchanger_signal_t;
16
+
17
+ typedef struct exchanger_waiter {
18
+ VALUE offered;
19
+ VALUE received;
20
+ exchanger_signal_t signal;
21
+ bool matched;
22
+ } exchanger_waiter_t;
23
+
24
+ typedef struct {
25
+ pthread_mutex_t lock;
26
+ exchanger_waiter_t *waiting;
27
+ bool initialized;
28
+ } exchanger_t;
29
+
30
+ typedef struct {
31
+ bool finite;
32
+ double deadline;
33
+ } exchanger_timeout_t;
34
+
35
+ typedef struct {
36
+ exchanger_t *exchanger;
37
+ exchanger_waiter_t *waiter;
38
+ exchanger_timeout_t *timeout;
39
+ } exchanger_wait_context_t;
40
+
41
+ static void
42
+ exchanger_set_fd_flags(int fd)
43
+ {
44
+ #ifdef _WIN32
45
+ (void)fd;
46
+ #else
47
+ int flags = fcntl(fd, F_GETFL, 0);
48
+ if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
49
+ flags = fcntl(fd, F_GETFD, 0);
50
+ if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
51
+ #endif
52
+ }
53
+
54
+ static bool
55
+ exchanger_signal_initialize(exchanger_signal_t *signal)
56
+ {
57
+ int descriptors[2];
58
+ if (pipe(descriptors) != 0) return false;
59
+ signal->read_fd = descriptors[0];
60
+ signal->write_fd = descriptors[1];
61
+ exchanger_set_fd_flags(signal->read_fd);
62
+ exchanger_set_fd_flags(signal->write_fd);
63
+ return true;
64
+ }
65
+
66
+ static void
67
+ exchanger_signal_close(exchanger_signal_t *signal)
68
+ {
69
+ if (signal->read_fd >= 0) close(signal->read_fd);
70
+ if (signal->write_fd >= 0) close(signal->write_fd);
71
+ signal->read_fd = -1;
72
+ signal->write_fd = -1;
73
+ }
74
+
75
+ static void
76
+ exchanger_signal_set(exchanger_signal_t *signal)
77
+ {
78
+ unsigned char byte = 1;
79
+ ssize_t result;
80
+
81
+ do {
82
+ result = write(signal->write_fd, &byte, 1);
83
+ } while (result < 0 && errno == EINTR);
84
+ }
85
+
86
+ static double
87
+ exchanger_monotonic_now(void)
88
+ {
89
+ struct timespec now;
90
+ #ifdef CLOCK_MONOTONIC
91
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
92
+ return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
93
+ }
94
+ #endif
95
+ struct timeval fallback;
96
+ gettimeofday(&fallback, NULL);
97
+ return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
98
+ }
99
+
100
+ static exchanger_timeout_t
101
+ exchanger_parse_timeout(VALUE timeout)
102
+ {
103
+ exchanger_timeout_t parsed = {.finite = false, .deadline = 0};
104
+ if (NIL_P(timeout)) return parsed;
105
+
106
+ double seconds = NUM2DBL(timeout);
107
+ if (!isfinite(seconds) || seconds < 0) {
108
+ rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
109
+ }
110
+ parsed.finite = true;
111
+ parsed.deadline = exchanger_monotonic_now() + seconds;
112
+ return parsed;
113
+ }
114
+
115
+ static bool
116
+ exchanger_wait_for_descriptor(int fd, exchanger_timeout_t *timeout)
117
+ {
118
+ VALUE wait_timeout = Qnil;
119
+ if (timeout->finite) {
120
+ double remaining = timeout->deadline - exchanger_monotonic_now();
121
+ if (remaining <= 0) return false;
122
+ wait_timeout = DBL2NUM(remaining);
123
+ }
124
+
125
+ return containers_wait_for_readable(fd, wait_timeout);
126
+ }
127
+
128
+ static void
129
+ exchanger_free(void *pointer)
130
+ {
131
+ exchanger_t *exchanger = pointer;
132
+ pthread_mutex_destroy(&exchanger->lock);
133
+ ruby_xfree(exchanger);
134
+ }
135
+
136
+ static size_t
137
+ exchanger_memsize(const void *pointer)
138
+ {
139
+ return pointer ? sizeof(exchanger_t) : 0;
140
+ }
141
+
142
+ static const rb_data_type_t exchanger_type = {
143
+ .wrap_struct_name = "Ractor::Containers::Exchanger",
144
+ .function = {
145
+ .dmark = NULL,
146
+ .dfree = exchanger_free,
147
+ .dsize = exchanger_memsize,
148
+ .dcompact = NULL,
149
+ },
150
+ .flags = RUBY_TYPED_FROZEN_SHAREABLE,
151
+ };
152
+
153
+ static VALUE
154
+ exchanger_allocate(VALUE klass)
155
+ {
156
+ exchanger_t *exchanger;
157
+ VALUE object = TypedData_Make_Struct(klass, exchanger_t, &exchanger_type, exchanger);
158
+ pthread_mutex_init(&exchanger->lock, NULL);
159
+ exchanger->waiting = NULL;
160
+ exchanger->initialized = false;
161
+ return object;
162
+ }
163
+
164
+ static exchanger_t *
165
+ get_exchanger(VALUE self)
166
+ {
167
+ exchanger_t *exchanger;
168
+ TypedData_Get_Struct(self, exchanger_t, &exchanger_type, exchanger);
169
+ if (!exchanger->initialized) rb_raise(rb_eRuntimeError, "uninitialized Exchanger");
170
+ return exchanger;
171
+ }
172
+
173
+ static VALUE
174
+ exchanger_frozen_p(VALUE self)
175
+ {
176
+ (void)self;
177
+ return Qfalse;
178
+ }
179
+
180
+ static VALUE
181
+ exchanger_freeze(VALUE self)
182
+ {
183
+ containers_raise_unfreezable(self);
184
+ return Qnil;
185
+ }
186
+
187
+ static VALUE
188
+ exchanger_initialize(VALUE self)
189
+ {
190
+ exchanger_t *exchanger;
191
+ TypedData_Get_Struct(self, exchanger_t, &exchanger_type, exchanger);
192
+ if (exchanger->initialized) rb_raise(rb_eRuntimeError, "Exchanger is already initialized");
193
+ exchanger->initialized = true;
194
+ containers_publish_native_reference_free(self);
195
+ return self;
196
+ }
197
+
198
+ static VALUE
199
+ exchanger_wait_body(VALUE opaque)
200
+ {
201
+ exchanger_wait_context_t *context = (exchanger_wait_context_t *)opaque;
202
+ return exchanger_wait_for_descriptor(context->waiter->signal.read_fd, context->timeout)
203
+ ? Qtrue
204
+ : Qfalse;
205
+ }
206
+
207
+ static VALUE
208
+ exchanger_wait_cleanup(VALUE opaque)
209
+ {
210
+ exchanger_wait_context_t *context = (exchanger_wait_context_t *)opaque;
211
+ pthread_mutex_lock(&context->exchanger->lock);
212
+ if (context->exchanger->waiting == context->waiter) {
213
+ context->exchanger->waiting = NULL;
214
+ }
215
+ pthread_mutex_unlock(&context->exchanger->lock);
216
+ exchanger_signal_close(&context->waiter->signal);
217
+ return Qnil;
218
+ }
219
+
220
+ static VALUE
221
+ exchanger_exchange(int argc, VALUE *argv, VALUE self)
222
+ {
223
+ VALUE offered;
224
+ VALUE keywords = Qnil;
225
+ VALUE timeout_value = Qnil;
226
+ ID keyword_ids[] = {rb_intern("timeout")};
227
+ VALUE keyword_values[1];
228
+
229
+ rb_scan_args(argc, argv, "1:", &offered, &keywords);
230
+ if (!NIL_P(keywords)) {
231
+ rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
232
+ if (keyword_values[0] != Qundef) timeout_value = keyword_values[0];
233
+ }
234
+
235
+ containers_check_shareable(offered);
236
+ exchanger_timeout_t timeout = exchanger_parse_timeout(timeout_value);
237
+ exchanger_t *exchanger = get_exchanger(self);
238
+
239
+ pthread_mutex_lock(&exchanger->lock);
240
+ if (exchanger->waiting != NULL) {
241
+ exchanger_waiter_t *partner = exchanger->waiting;
242
+ exchanger->waiting = NULL;
243
+ VALUE received = partner->offered;
244
+ partner->received = offered;
245
+ partner->matched = true;
246
+ exchanger_signal_set(&partner->signal);
247
+ pthread_mutex_unlock(&exchanger->lock);
248
+ RB_GC_GUARD(offered);
249
+ return received;
250
+ }
251
+
252
+ if (timeout.finite && timeout.deadline <= exchanger_monotonic_now()) {
253
+ pthread_mutex_unlock(&exchanger->lock);
254
+ return rb_block_given_p() ? rb_yield_values(0) : Qnil;
255
+ }
256
+
257
+ exchanger_waiter_t waiter = {
258
+ .offered = offered,
259
+ .received = Qnil,
260
+ .signal = {.read_fd = -1, .write_fd = -1},
261
+ .matched = false,
262
+ };
263
+ if (!exchanger_signal_initialize(&waiter.signal)) {
264
+ int error = errno;
265
+ pthread_mutex_unlock(&exchanger->lock);
266
+ errno = error;
267
+ rb_sys_fail("pipe");
268
+ }
269
+ exchanger->waiting = &waiter;
270
+ pthread_mutex_unlock(&exchanger->lock);
271
+
272
+ exchanger_wait_context_t context = {
273
+ .exchanger = exchanger,
274
+ .waiter = &waiter,
275
+ .timeout = &timeout,
276
+ };
277
+ (void)rb_ensure(
278
+ exchanger_wait_body,
279
+ (VALUE)&context,
280
+ exchanger_wait_cleanup,
281
+ (VALUE)&context
282
+ );
283
+
284
+ RB_GC_GUARD(offered);
285
+ RB_GC_GUARD(waiter.received);
286
+ if (waiter.matched) return waiter.received;
287
+ return rb_block_given_p() ? rb_yield_values(0) : Qnil;
288
+ }
289
+
290
+ void
291
+ containers_init_exchanger(VALUE namespace)
292
+ {
293
+ cExchanger = rb_define_class_under(namespace, "Exchanger", rb_cObject);
294
+ rb_define_alloc_func(cExchanger, exchanger_allocate);
295
+ rb_define_method(cExchanger, "initialize", exchanger_initialize, 0);
296
+ rb_define_method(cExchanger, "exchange", exchanger_exchange, -1);
297
+ rb_define_method(cExchanger, "freeze", exchanger_freeze, 0);
298
+ rb_define_method(cExchanger, "frozen?", exchanger_frozen_p, 0);
299
+ }