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,505 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ require "farce/engine/shared/map_key_coordination"
6
+
7
+ module Farce
8
+ # @!visibility private
9
+ module Internal # :nodoc: all
10
+ class Map
11
+ include MapKeyCoordination
12
+ include TransactionMapBackend
13
+
14
+ BASIC_OBJECT_EQUAL_METHOD = BasicObject.instance_method(:equal?)
15
+ private_constant :BASIC_OBJECT_EQUAL_METHOD
16
+
17
+ class Key
18
+ attr_accessor :mutation_owner
19
+ attr_reader :value
20
+
21
+ def initialize(value) = @value = value
22
+
23
+ def hash
24
+ result = value.hash
25
+ owner = mutation_owner
26
+ Internal::Freeze.check(owner) if owner
27
+ result
28
+ end
29
+
30
+ def eql?(other)
31
+ result = other.is_a?(Key) && value.eql?(other.value)
32
+ owner = mutation_owner || (other.mutation_owner if other.is_a?(Key))
33
+ Internal::Freeze.check(owner) if owner
34
+ result
35
+ end
36
+
37
+ alias == eql?
38
+ end
39
+ private_constant :Key
40
+
41
+ # The Java bridge converts objects such as Ruby strings unless they are boxed.
42
+ class Value
43
+ attr_reader :value
44
+
45
+ def initialize(value) = @value = value
46
+ end
47
+ private_constant :Value
48
+
49
+ def initialize(
50
+ initial_mapping = nil,
51
+ compare_by_identity: false,
52
+ compare_keys_by_identity: compare_by_identity,
53
+ compare_values_by_identity: compare_by_identity
54
+ )
55
+ validate_boolean(compare_by_identity, "compare_by_identity")
56
+ validate_boolean(compare_keys_by_identity, "compare_keys_by_identity")
57
+ validate_boolean(compare_values_by_identity, "compare_values_by_identity")
58
+ raise TypeError, "initial mapping must be a Hash" unless initial_mapping.nil? || initial_mapping.is_a?(Hash)
59
+
60
+ @compare_keys_by_identity = compare_keys_by_identity
61
+ @compare_values_by_identity = compare_values_by_identity
62
+ @freeze_state = Flag.new(false)
63
+ @map = java.util.concurrent.ConcurrentHashMap.new
64
+ # Native operations may overlap. Block updates reserve one logical key.
65
+ @state_mutex = Mutex.new
66
+ @change_signal = Signal.new
67
+ @active_owner_fiber = nil
68
+ @active_owner_thread = nil
69
+ @active_owners = nil
70
+ initialize_key_coordination
71
+ initial_mapping&.each do |key, value|
72
+ check_frozen!
73
+ wrapped = wrap_key(key)
74
+ with_mutation_callback_guard(wrapped) { concurrent_store(wrapped, value) }
75
+ end
76
+ end
77
+
78
+ def freeze
79
+ state = @freeze_state
80
+ return super unless state
81
+
82
+ state.set
83
+ self
84
+ end
85
+
86
+ def frozen?
87
+ state = @freeze_state
88
+ state ? state.value : super
89
+ end
90
+
91
+ def check_mutation = check_frozen!
92
+
93
+ def prepare_mutation_key(key)
94
+ check_frozen!
95
+ normalize_external_key(key)
96
+ end
97
+
98
+ def [](key) = concurrent_get(wrap_key(key))
99
+
100
+ def fetch(*arguments)
101
+ unless arguments.length.between?(1, 2)
102
+ raise ArgumentError, "wrong number of arguments (given #{arguments.length}, expected 1..2)"
103
+ end
104
+
105
+ key, default = arguments
106
+ default_given = arguments.length == 2
107
+ warn "block supersedes default value argument", uplevel: 1 if block_given? && default_given
108
+ stored = @map.get(wrap_key(key))
109
+ return stored.value if stored
110
+ return yield(key) if block_given?
111
+ return default if default_given
112
+
113
+ raise KeyError.new("key not found: #{key.inspect}", receiver: self, key: key)
114
+ end
115
+
116
+ def []=(key, value)
117
+ check_frozen!
118
+ wrapped = wrap_key(key)
119
+ _, result = with_mutating_key_operation(wrapped, nil) do |reservation|
120
+ commit_mutation(reservation) do
121
+ native_operation { concurrent_store(wrapped, value) }
122
+ changed!
123
+ end
124
+ value
125
+ end
126
+ result
127
+ end
128
+
129
+ def get(key, timeout: nil, &fallback)
130
+ wrapped = wrap_key(key)
131
+ completed, result = with_key_operation(wrapped, timeout_deadline(timeout)) do
132
+ native_operation { concurrent_get(wrapped) }
133
+ end
134
+ completed ? result : fallback&.call
135
+ end
136
+
137
+ def store(key, value, timeout: nil, &fallback)
138
+ check_frozen!
139
+ wrapped = wrap_key(key)
140
+ completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
141
+ commit_mutation(reservation) do
142
+ native_operation { concurrent_store(wrapped, value) }
143
+ changed!
144
+ end
145
+ value
146
+ end
147
+ completed ? result : fallback&.call
148
+ end
149
+
150
+ def swap(key, replacement, timeout: nil, &fallback)
151
+ check_frozen!
152
+ wrapped = wrap_key(key)
153
+ completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
154
+ previous = nil
155
+ commit_mutation(reservation) do
156
+ previous = native_operation { concurrent_swap(wrapped, replacement) }
157
+ changed!
158
+ end
159
+ previous
160
+ end
161
+ completed ? result : fallback&.call
162
+ end
163
+
164
+ def store_if_absent(key, timeout: nil)
165
+ raise LocalJumpError, "no block given" unless block_given?
166
+ check_frozen!
167
+
168
+ wrapped = wrap_key(key)
169
+ completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
170
+ if native_operation { concurrent_key?(wrapped) }
171
+ native_operation { concurrent_get(wrapped) }
172
+ else
173
+ value = yield
174
+ stored = commit_mutation(reservation) do
175
+ native_operation { concurrent_store(wrapped, value) }
176
+ changed!
177
+ end
178
+ value if stored
179
+ end
180
+ end
181
+ completed ? result : nil
182
+ end
183
+
184
+ def compare_and_set(key, expected, replacement, timeout: nil)
185
+ check_frozen!
186
+ wrapped = wrap_key(key)
187
+ completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
188
+ next false unless native_operation { concurrent_key?(wrapped) }
189
+
190
+ current = native_operation { concurrent_get(wrapped) }
191
+ next false unless values_equal?(current, expected)
192
+
193
+ replaced = commit_mutation(reservation) do
194
+ native_operation { concurrent_store(wrapped, replacement) }
195
+ changed!
196
+ end
197
+ replaced
198
+ end
199
+ completed && result
200
+ end
201
+
202
+ def update(key, timeout: nil)
203
+ raise LocalJumpError, "no block given" unless block_given?
204
+ check_frozen!
205
+
206
+ wrapped = wrap_key(key)
207
+ completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
208
+ value = yield(native_operation { concurrent_get(wrapped) })
209
+ stored = commit_mutation(reservation) do
210
+ native_operation { concurrent_store(wrapped, value) }
211
+ changed!
212
+ end
213
+ value if stored
214
+ end
215
+ completed ? result : nil
216
+ end
217
+
218
+ # Yield presence and value under the key reservation.
219
+ # MAP_KEEP and MAP_DELETE are control results. Other results replace the value.
220
+ # Return whether a change committed.
221
+ def modify(key) # rubocop:disable Naming/PredicateMethod
222
+ raise LocalJumpError, "no block given" unless block_given?
223
+ check_frozen!
224
+ wrapped = wrap_key(key)
225
+ _, result = with_mutating_key_operation(wrapped, nil) do |reservation|
226
+ present, current = native_operation { [concurrent_key?(wrapped), concurrent_get(wrapped)] }
227
+ value = yield(present, current)
228
+ next false if MAP_KEEP.equal?(value)
229
+ remove = MAP_DELETE.equal?(value)
230
+ next false if remove && !present
231
+ commit_mutation(reservation) do
232
+ native_operation do
233
+ remove ? concurrent_delete(wrapped) : concurrent_store(wrapped, value)
234
+ end
235
+ changed!
236
+ end
237
+ end
238
+ !!result
239
+ end
240
+
241
+ def upsert(key, initial_value, timeout: nil)
242
+ raise LocalJumpError, "no block given" unless block_given?
243
+ check_frozen!
244
+
245
+ wrapped = wrap_key(key)
246
+ completed, result = with_mutating_key_operation(wrapped, timeout_deadline(timeout)) do |reservation|
247
+ present = native_operation { concurrent_key?(wrapped) }
248
+ value = present ? yield(native_operation { concurrent_get(wrapped) }) : initial_value
249
+ stored = commit_mutation(reservation) do
250
+ native_operation { concurrent_store(wrapped, value) }
251
+ changed!
252
+ end
253
+ value if stored
254
+ end
255
+ completed ? result : nil
256
+ end
257
+
258
+ def wait_until_changed(key, expected, timeout: nil, &fallback)
259
+ wait_for_value(key, expected, timeout_deadline(timeout), fallback, non_nil: false)
260
+ end
261
+
262
+ def wait_until_non_nil(key, timeout: nil, &fallback)
263
+ wait_for_value(key, nil, timeout_deadline(timeout), fallback, non_nil: true)
264
+ end
265
+
266
+ def key?(key) = concurrent_key?(wrap_key(key))
267
+ def size = @map.size
268
+ def compare_keys_by_identity? = @compare_keys_by_identity
269
+ def compare_values_by_identity? = @compare_values_by_identity
270
+
271
+ def keys = entries_snapshot.map(&:first)
272
+
273
+ def each(&block)
274
+ return enum_for(__callee__) { size } unless block
275
+
276
+ entries_snapshot.each { |pair| block.call(pair) }
277
+ self
278
+ end
279
+ alias each_pair each
280
+
281
+ def each_live
282
+ return enum_for(__method__) { size } unless block_given?
283
+ iterator = @map.entry_set.iterator
284
+ while iterator.has_next
285
+ entry = iterator.next
286
+ yield [entry.key.value, entry.value.value]
287
+ end
288
+ self
289
+ end
290
+
291
+ def each_key(&block)
292
+ return enum_for(__callee__) { size } unless block
293
+
294
+ entries_snapshot.each { |pair| block.call(pair.first) }
295
+ self
296
+ end
297
+
298
+ def each_value(&block)
299
+ return enum_for(__callee__) { size } unless block
300
+
301
+ entries_snapshot.each { |pair| block.call(pair.last) }
302
+ self
303
+ end
304
+
305
+ def clear
306
+ check_frozen!
307
+ clear_key_operations do
308
+ native_operation { @map.clear }
309
+ changed!
310
+ end
311
+ self
312
+ end
313
+
314
+ def delete(key)
315
+ check_frozen!
316
+ wrapped = wrap_key(key)
317
+ _, result = with_mutating_key_operation(wrapped, nil) do |reservation|
318
+ value = nil
319
+ commit_mutation(reservation) do
320
+ value = native_operation { concurrent_delete(wrapped) }
321
+ changed!
322
+ end
323
+ value
324
+ end
325
+ result
326
+ end
327
+
328
+ def getkey(key)
329
+ wrapped = wrap_key(key)
330
+ return unless concurrent_key?(wrapped)
331
+
332
+ @map.each_pair do |stored, _value|
333
+ return stored.value if stored.eql?(wrapped)
334
+ end
335
+ nil
336
+ end
337
+
338
+ private
339
+
340
+ def check_frozen! = Internal::Freeze.check(self)
341
+
342
+ def commit_mutation(reservation, &)
343
+ check_frozen!
344
+ reservation.commit(&)
345
+ end
346
+
347
+ def with_mutating_key_operation(key, deadline, &)
348
+ with_key_operation(key, deadline) do |reservation|
349
+ with_mutation_callback_guard(key) do
350
+ check_frozen!
351
+ yield reservation
352
+ end
353
+ end
354
+ end
355
+
356
+ def with_mutation_callback_guard(key)
357
+ return yield unless key.is_a?(Key)
358
+
359
+ key.mutation_owner = self
360
+ yield
361
+ ensure
362
+ key.mutation_owner = nil if key.is_a?(Key)
363
+ end
364
+
365
+ def wrap_key(key)
366
+ return IdentityKey.new(key) if compare_keys_by_identity?
367
+ key = String.instance_method(:-@).bind_call(key) if String === key && !key.frozen?
368
+ Key.new(key)
369
+ end
370
+
371
+ def entries_snapshot
372
+ entries = []
373
+ @map.each_pair { |key, value| entries << [key.value, value.value] }
374
+ entries
375
+ end
376
+
377
+ def concurrent_get(key)
378
+ stored = @map.get(key)
379
+ stored&.value
380
+ end
381
+
382
+ def concurrent_store(key, value)
383
+ @map.put(key, Value.new(value))
384
+ value
385
+ end
386
+
387
+ def concurrent_swap(key, value)
388
+ previous = @map.put(key, Value.new(value))
389
+ previous&.value
390
+ end
391
+
392
+ def concurrent_key?(key) = @map.contains_key(key)
393
+
394
+ def concurrent_delete(key)
395
+ previous = @map.remove(key)
396
+ previous&.value
397
+ end
398
+
399
+ def native_operation(&)
400
+ entered = false
401
+ @state_mutex.synchronize do
402
+ reject_active_operation_reentry!
403
+ Thread.handle_interrupt(INTERRUPT_MASK) do
404
+ register_active_operation
405
+ entered = true
406
+ end
407
+ end
408
+ yield
409
+ ensure
410
+ Thread.handle_interrupt(INTERRUPT_MASK) { leave_operation } if entered
411
+ end
412
+
413
+ def validate_boolean(value, name)
414
+ return if identical?(value, true) || identical?(value, false)
415
+ raise ArgumentError, "#{name} must be true or false"
416
+ end
417
+
418
+ def leave_operation
419
+ @state_mutex.synchronize { unregister_active_operation }
420
+ end
421
+
422
+ def changed! = @change_signal.broadcast
423
+
424
+ def wait_for_value(key, expected, deadline, fallback, non_nil:)
425
+ wrapped = wrap_key(key)
426
+ while true
427
+ generation = @change_signal.generation
428
+ completed, current = with_key_operation(wrapped, deadline) do
429
+ native_operation { concurrent_get(wrapped) }
430
+ end
431
+ return fallback&.call unless completed
432
+ ready = non_nil ? !current.nil? : !values_equal?(current, expected)
433
+ return current if ready
434
+ return fallback&.call unless wait_for_signal(@change_signal, generation, deadline)
435
+ end
436
+ end
437
+
438
+ def wait_for_signal(signal, generation, deadline)
439
+ return signal.wait(generation) unless deadline
440
+
441
+ remaining = deadline - Clock.now
442
+ return false unless remaining.positive?
443
+ !signal.wait(generation, timeout: remaining).nil?
444
+ end
445
+
446
+ def timeout_deadline(timeout)
447
+ return if timeout.nil?
448
+
449
+ timeout = Float(timeout)
450
+ if !timeout.finite? || timeout.negative?
451
+ raise ArgumentError, "timeout must be a finite, non-negative number or nil"
452
+ end
453
+ Clock.now + timeout
454
+ end
455
+
456
+ def reject_active_operation_reentry!
457
+ return unless active_operation_owned_by?(Fiber.current)
458
+
459
+ raise ThreadError, "deadlock; recursive map access during an operation"
460
+ end
461
+
462
+ def register_active_operation
463
+ fiber = Fiber.current
464
+ thread = Thread.current
465
+ if @active_owners
466
+ @active_owners[fiber] = thread
467
+ elsif @active_owner_fiber
468
+ owners = {}.compare_by_identity
469
+ owners[@active_owner_fiber] = @active_owner_thread
470
+ owners[fiber] = thread
471
+ @active_owners = owners
472
+ @active_owner_fiber = @active_owner_thread = nil
473
+ else
474
+ @active_owner_thread = thread
475
+ @active_owner_fiber = fiber
476
+ end
477
+ end
478
+
479
+ def unregister_active_operation
480
+ if @active_owners
481
+ removed = @active_owners.delete(Fiber.current)
482
+ if @active_owners.one?
483
+ @active_owner_fiber, @active_owner_thread = @active_owners.first
484
+ @active_owners = nil
485
+ end
486
+ removed
487
+ elsif identical?(@active_owner_fiber, Fiber.current)
488
+ removed = @active_owner_thread
489
+ @active_owner_fiber = @active_owner_thread = nil
490
+ removed
491
+ end
492
+ end
493
+
494
+ def active_operation_owned_by?(fiber)
495
+ @active_owners ? @active_owners.key?(fiber) : identical?(@active_owner_fiber, fiber)
496
+ end
497
+
498
+ def values_equal?(left, right)
499
+ compare_values_by_identity? ? identical?(left, right) : left == right
500
+ end
501
+
502
+ def identical?(left, right) = BASIC_OBJECT_EQUAL_METHOD.bind_call(left, right)
503
+ end
504
+ end
505
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Internal
7
+ # RubyNumeric's final Java clone methods return self. Invoke Object's copy
8
+ # setup on a fresh Numeric to preserve Ruby's hooks and singleton methods.
9
+ module MutableNumericCopy
10
+ DUP_SETUP = org.jruby.RubyBasicObject.java_class.declared_method(
11
+ "dupSetup", org.jruby.runtime.ThreadContext.java_class, org.jruby.RubyBasicObject.java_class,
12
+ )
13
+
14
+ CLONE_SETUP = org.jruby.RubyBasicObject.java_class.declared_method(
15
+ "cloneSetup", org.jruby.runtime.ThreadContext.java_class, org.jruby.RubyBasicObject.java_class,
16
+ org.jruby.runtime.builtin.IRubyObject.java_class,
17
+ )
18
+
19
+ DUP_SETUP.accessible = CLONE_SETUP.accessible = true
20
+
21
+ private_constant :DUP_SETUP, :CLONE_SETUP
22
+
23
+ def dup = copy_numeric(DUP_SETUP)
24
+
25
+ def clone(freeze: nil)
26
+ unless freeze.nil? || freeze.equal?(true) || freeze.equal?(false)
27
+ raise ArgumentError, "unexpected value for freeze: #{freeze.class}"
28
+ end
29
+ copy_numeric(CLONE_SETUP, JRuby.reference(freeze))
30
+ end
31
+
32
+ private
33
+
34
+ def copy_numeric(method, *)
35
+ copy = JRuby.reference(self.class.allocate)
36
+ method.invoke(JRuby.reference(self), JRuby.runtime.current_context, copy, *)
37
+ rescue Java::JavaLangReflect::InvocationTargetException => e
38
+ raise e.cause
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ require "java"
6
+ require "farce/engine/jvm/extension"
7
+
8
+ module Farce
9
+ # @!visibility private
10
+ module Internal # :nodoc: all
11
+ class Signal < JVMExtension::QueueSignal
12
+ NANOSECONDS = java.util.concurrent.TimeUnit::NANOSECONDS
13
+ READABLE = IO::READABLE
14
+ private_constant :NANOSECONDS, :READABLE
15
+
16
+ def initialize
17
+ super
18
+ @fiber_waiters = java.util.concurrent.ConcurrentHashMap.new
19
+ @thread_waiters = java.util.concurrent.atomic.AtomicInteger.new
20
+ Freeze.publish(self)
21
+ end
22
+
23
+ # JRuby-dev 10.1.2 cannot construct a Java subclass after a module is included.
24
+ # Keep these methods local until its reifier is fixed.
25
+ def freeze = raise(TypeError, "#{self.class} cannot be frozen")
26
+ def frozen? = false
27
+
28
+ def generation = phase
29
+ def num_waiting = @thread_waiters.get + @fiber_waiters.size
30
+
31
+ def broadcast
32
+ generation = broadcastThreads
33
+
34
+ # Publish the generation even with no waiters, so a waiter registering
35
+ # concurrently can detect the broadcast before it parks.
36
+ return generation if @fiber_waiters.empty?
37
+
38
+ @fiber_waiters.key_set.each do |writer|
39
+ writer.write_nonblock(".", exception: false)
40
+ rescue IOError, SystemCallError
41
+ nil
42
+ end
43
+ generation
44
+ end
45
+
46
+ def wait(observed = nil, timeout: nil)
47
+ validate_generation(observed) unless observed.nil?
48
+ timeout = parse_timeout(timeout)
49
+ observed ||= generation
50
+
51
+ scheduler = Fiber.current_scheduler if Fiber.respond_to?(:current_scheduler)
52
+ # JRuby may run a later scheduled fiber on a backing thread where this is nil.
53
+ scheduler ||= Fiber.scheduler if !Fiber.current.blocking? && Fiber.respond_to?(:scheduler)
54
+ return wait_with_scheduler(scheduler, observed, timeout) { yield if block_given? } if scheduler
55
+
56
+ wait_with_phaser(observed, timeout) { yield if block_given? }
57
+ end
58
+
59
+ private
60
+
61
+ def validate_generation(generation)
62
+ raise TypeError, "generation must be an Integer" unless generation.is_a?(::Integer)
63
+ end
64
+
65
+ def parse_timeout(timeout)
66
+ return unless timeout
67
+
68
+ timeout = Float(timeout)
69
+ if !timeout.finite? || timeout.negative?
70
+ raise ArgumentError, "timeout must be a finite, non-negative number or nil"
71
+ end
72
+ timeout
73
+ end
74
+
75
+ def wait_with_phaser(observed, timeout)
76
+ current = generation
77
+ return current unless current == observed
78
+
79
+ deadline = Clock.now + timeout if timeout
80
+ registered = false
81
+ begin
82
+ Thread.handle_interrupt(INTERRUPT_MASK) do
83
+ @thread_waiters.increment_and_get
84
+ registered = true
85
+ end
86
+ loop do
87
+ current = generation
88
+ return current unless current == observed
89
+
90
+ remaining = deadline - Clock.now if deadline
91
+ return yield if remaining && !remaining.positive?
92
+
93
+ # Java Phaser waits do not deliver Ruby cancellation until they return.
94
+ # Retry bounded slices without shortening the caller's deadline.
95
+ interval = LeaseWaiting.wait_interval(remaining)
96
+ begin
97
+ return await_advance_interruptibly(observed, (interval * 1_000_000_000).ceil, NANOSECONDS)
98
+ rescue Java::JavaUtilConcurrent::TimeoutException
99
+ # A positive Ruby sleep delivers :on_blocking interrupts too.
100
+ # JRuby optimizes sleep(0) without entering a blocking region.
101
+ sleep 0.000001
102
+ end
103
+ end
104
+ ensure
105
+ Thread.handle_interrupt(INTERRUPT_MASK) do
106
+ @thread_waiters.decrement_and_get if registered
107
+ end
108
+ end
109
+ end
110
+
111
+ def wait_with_scheduler(scheduler, observed, timeout)
112
+ current = generation
113
+ return current unless current == observed
114
+
115
+ # The Java monitor closes the registration/commit race. Publish the
116
+ # registration flag under an interrupt mask so cancellation cannot
117
+ # leak a registration between the Java return and Ruby assignment.
118
+ registered = false
119
+ Thread.handle_interrupt(INTERRUPT_MASK) do
120
+ beginSchedulerWait
121
+ registered = true
122
+ end
123
+ deadline = Clock.now + timeout if timeout
124
+ reader, writer = IO.pipe
125
+ @fiber_waiters[writer] = true
126
+
127
+ loop do
128
+ current = generation
129
+ return current unless current == observed
130
+
131
+ remaining = deadline - Clock.now if deadline
132
+ return yield if remaining && !remaining.positive?
133
+
134
+ scheduler.io_wait(reader, READABLE, remaining)
135
+ end
136
+ ensure
137
+ Thread.handle_interrupt(INTERRUPT_MASK) do
138
+ @fiber_waiters.delete(writer) if writer
139
+ reader&.close
140
+ writer&.close
141
+ ensure
142
+ endSchedulerWait if registered
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end