farce 0.0.1.alpha2-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +26 -0
  3. data/CONTRIBUTING.md +71 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +1524 -0
  6. data/SECURITY.md +10 -0
  7. data/docs/benchmarks.md +185 -0
  8. data/docs/gems/dry-types.md +290 -0
  9. data/docs/gems/msgpack.md +70 -0
  10. data/docs/gems/ractor-shim.md +63 -0
  11. data/docs/modes.md +628 -0
  12. data/docs/scopes.md +649 -0
  13. data/docs/variants.md +346 -0
  14. data/ext/ext_helper.rb +20 -0
  15. data/ext/farce/README.md +21 -0
  16. data/ext/farce/atom.c +1023 -0
  17. data/ext/farce/bounded_map.c +1683 -0
  18. data/ext/farce/containers.h +77 -0
  19. data/ext/farce/counter.c +399 -0
  20. data/ext/farce/darwin.c +100 -0
  21. data/ext/farce/depend +12 -0
  22. data/ext/farce/dict.c +1523 -0
  23. data/ext/farce/dict.h +152 -0
  24. data/ext/farce/drivers.c +237 -0
  25. data/ext/farce/exchanger.c +299 -0
  26. data/ext/farce/extconf.rb +99 -0
  27. data/ext/farce/farce.c +359 -0
  28. data/ext/farce/flag.c +288 -0
  29. data/ext/farce/io.c +338 -0
  30. data/ext/farce/lock.c +510 -0
  31. data/ext/farce/map.c +2240 -0
  32. data/ext/farce/priority_queue.c +2056 -0
  33. data/ext/farce/queue.c +1059 -0
  34. data/ext/farce/reactor.c +820 -0
  35. data/ext/farce/reactor.h +103 -0
  36. data/ext/farce/shareable.h +31 -0
  37. data/ext/farce/signal.c +350 -0
  38. data/ext/farce/transaction.c +354 -0
  39. data/ext/farce/transaction.h +40 -0
  40. data/ext/farce/tree_map.c +1953 -0
  41. data/ext/farce/trie.c +2020 -0
  42. data/ext/farce/unshareable.c +155 -0
  43. data/ext/farce/unshared_io_pool.h +234 -0
  44. data/ext/farce/unshared_signal.c +263 -0
  45. data/ext/farce/unshared_wait.h +193 -0
  46. data/ext/farce/unsupported.c +6 -0
  47. data/ext/farce/vector.c +1195 -0
  48. data/ext/farce/weak_map.c +1714 -0
  49. data/ext/java/org/farce/BoundedMap.java +394 -0
  50. data/ext/java/org/farce/FiberScheduler.java +141 -0
  51. data/ext/java/org/farce/PriorityKey.java +45 -0
  52. data/ext/java/org/farce/PriorityQueue.java +373 -0
  53. data/ext/java/org/farce/QueueSignal.java +28 -0
  54. data/ext/rebind/README.md +14 -0
  55. data/ext/rebind/extconf.rb +10 -0
  56. data/ext/rebind/rebind.c +186 -0
  57. data/lib/farce/_yard/internal.rb +13 -0
  58. data/lib/farce/_yard/macros.rb +61 -0
  59. data/lib/farce/_yard/ractor.rb +46 -0
  60. data/lib/farce/abstract/atom.rb +186 -0
  61. data/lib/farce/abstract/bounded_map.rb +232 -0
  62. data/lib/farce/abstract/collection.rb +151 -0
  63. data/lib/farce/abstract/concurrent_map.rb +381 -0
  64. data/lib/farce/abstract/counter.rb +193 -0
  65. data/lib/farce/abstract/duplicable_map.rb +229 -0
  66. data/lib/farce/abstract/exchanger.rb +26 -0
  67. data/lib/farce/abstract/flag.rb +71 -0
  68. data/lib/farce/abstract/lazy.rb +115 -0
  69. data/lib/farce/abstract/lease.rb +104 -0
  70. data/lib/farce/abstract/lease_map.rb +261 -0
  71. data/lib/farce/abstract/lease_pool.rb +91 -0
  72. data/lib/farce/abstract/lfu_map.rb +20 -0
  73. data/lib/farce/abstract/lru_map.rb +25 -0
  74. data/lib/farce/abstract/map.rb +345 -0
  75. data/lib/farce/abstract/molecule.rb +245 -0
  76. data/lib/farce/abstract/port.rb +74 -0
  77. data/lib/farce/abstract/priority_queue.rb +117 -0
  78. data/lib/farce/abstract/queue.rb +269 -0
  79. data/lib/farce/abstract/scheduler.rb +111 -0
  80. data/lib/farce/abstract/set.rb +910 -0
  81. data/lib/farce/abstract/sorted_set.rb +172 -0
  82. data/lib/farce/abstract/timer_queue.rb +136 -0
  83. data/lib/farce/abstract/tree_map.rb +269 -0
  84. data/lib/farce/abstract/value.rb +68 -0
  85. data/lib/farce/abstract/vector.rb +1126 -0
  86. data/lib/farce/abstract/weak_atom.rb +32 -0
  87. data/lib/farce/abstract/weak_key_map.rb +12 -0
  88. data/lib/farce/abstract/weak_map.rb +13 -0
  89. data/lib/farce/abstract/weak_set.rb +12 -0
  90. data/lib/farce/abstract/weak_value_map.rb +12 -0
  91. data/lib/farce/abstract.rb +17 -0
  92. data/lib/farce/atom.rb +293 -0
  93. data/lib/farce/class_mirror.rb +87 -0
  94. data/lib/farce/clock.rb +121 -0
  95. data/lib/farce/config.rb +229 -0
  96. data/lib/farce/counter.rb +71 -0
  97. data/lib/farce/deduper.rb +122 -0
  98. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  99. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  101. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  102. data/lib/farce/engine/jruby/map.rb +505 -0
  103. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  104. data/lib/farce/engine/jruby/signal.rb +147 -0
  105. data/lib/farce/engine/jruby.rb +63 -0
  106. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  107. data/lib/farce/engine/jvm/counter.rb +102 -0
  108. data/lib/farce/engine/jvm/extension.rb +32 -0
  109. data/lib/farce/engine/jvm/farce.jar +0 -0
  110. data/lib/farce/engine/jvm/flag.rb +79 -0
  111. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  112. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  113. data/lib/farce/engine/jvm/types.rb +180 -0
  114. data/lib/farce/engine/jvm.rb +19 -0
  115. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  116. data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
  117. data/lib/farce/engine/ruby/3.4/port.rb +186 -0
  118. data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
  119. data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
  120. data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
  121. data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
  122. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  123. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  124. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  125. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  126. data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
  127. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  128. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  129. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  130. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  131. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  132. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  133. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  134. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  135. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  136. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  137. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  138. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  139. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  140. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  141. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  142. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  143. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  144. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  145. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  146. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  147. data/lib/farce/engine/ruby.rb +130 -0
  148. data/lib/farce/engine/shared/atom.rb +10 -0
  149. data/lib/farce/engine/shared/exchanger.rb +130 -0
  150. data/lib/farce/engine/shared/identity_key.rb +22 -0
  151. data/lib/farce/engine/shared/lease.rb +10 -0
  152. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  153. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  154. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  155. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  156. data/lib/farce/engine/shared/port.rb +44 -0
  157. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  158. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  159. data/lib/farce/engine/shared/queue.rb +233 -0
  160. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  161. data/lib/farce/engine/shared/rebindable.rb +15 -0
  162. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  163. data/lib/farce/engine/shared/strict_map.rb +117 -0
  164. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  165. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  166. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  167. data/lib/farce/engine/shared/trie.rb +317 -0
  168. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  169. data/lib/farce/engine/shared/unshareable.rb +14 -0
  170. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  171. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  172. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  173. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  174. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  175. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  176. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  177. data/lib/farce/engine/shared/vault.rb +31 -0
  178. data/lib/farce/engine/shared/vector.rb +83 -0
  179. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  180. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  181. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  182. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  183. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  184. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  185. data/lib/farce/engine/shared.rb +63 -0
  186. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  187. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  188. data/lib/farce/engine/truffleruby/map.rb +656 -0
  189. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  190. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  191. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  192. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  193. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  194. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  195. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  196. data/lib/farce/engine/truffleruby.rb +148 -0
  197. data/lib/farce/envelope.rb +335 -0
  198. data/lib/farce/error.rb +35 -0
  199. data/lib/farce/exchanger.rb +41 -0
  200. data/lib/farce/flag.rb +27 -0
  201. data/lib/farce/integrations/active_support/blank.rb +68 -0
  202. data/lib/farce/integrations/active_support/clock.rb +17 -0
  203. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  204. data/lib/farce/integrations/active_support/map.rb +175 -0
  205. data/lib/farce/integrations/active_support/set.rb +60 -0
  206. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  207. data/lib/farce/integrations/active_support/vector.rb +231 -0
  208. data/lib/farce/integrations/active_support.rb +9 -0
  209. data/lib/farce/integrations/activesupport.rb +5 -0
  210. data/lib/farce/integrations/bson.rb +108 -0
  211. data/lib/farce/integrations/cbor.rb +51 -0
  212. data/lib/farce/integrations/concurrent.rb +112 -0
  213. data/lib/farce/integrations/dry-types.rb +5 -0
  214. data/lib/farce/integrations/dry_types.rb +694 -0
  215. data/lib/farce/integrations/json.rb +7 -0
  216. data/lib/farce/integrations/msgpack.rb +112 -0
  217. data/lib/farce/integrations/oj.rb +69 -0
  218. data/lib/farce/integrations/psych.rb +191 -0
  219. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  220. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  221. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  222. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  223. data/lib/farce/integrations/shared/to_json.rb +42 -0
  224. data/lib/farce/integrations/sorted_set.rb +11 -0
  225. data/lib/farce/integrations/weakref.rb +38 -0
  226. data/lib/farce/integrations/yajl.rb +10 -0
  227. data/lib/farce/integrations.rb +156 -0
  228. data/lib/farce/internal/_frozen_config.rb +11 -0
  229. data/lib/farce/internal/autoloads.rb +47 -0
  230. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  231. data/lib/farce/internal/converter.rb +106 -0
  232. data/lib/farce/internal/copyable.rb +31 -0
  233. data/lib/farce/internal/delegation.rb +20 -0
  234. data/lib/farce/internal/external_transaction.rb +59 -0
  235. data/lib/farce/internal/fake_ractor.rb +179 -0
  236. data/lib/farce/internal/freeze.rb +118 -0
  237. data/lib/farce/internal/inspect.rb +157 -0
  238. data/lib/farce/internal/key_lock_map.rb +29 -0
  239. data/lib/farce/internal/key_normalizer.rb +289 -0
  240. data/lib/farce/internal/lease_initialization.rb +115 -0
  241. data/lib/farce/internal/lease_map.rb +441 -0
  242. data/lib/farce/internal/lease_pool_state.rb +228 -0
  243. data/lib/farce/internal/lease_state.rb +342 -0
  244. data/lib/farce/internal/lease_waiting.rb +13 -0
  245. data/lib/farce/internal/managed_queue.rb +26 -0
  246. data/lib/farce/internal/map_value_modes.rb +227 -0
  247. data/lib/farce/internal/marshal_support.rb +227 -0
  248. data/lib/farce/internal/mixin.rb +20 -0
  249. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  250. data/lib/farce/internal/noncopyable.rb +17 -0
  251. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  252. data/lib/farce/internal/pool_supervisor.rb +41 -0
  253. data/lib/farce/internal/pool_worker.rb +61 -0
  254. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  255. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  256. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  257. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  258. data/lib/farce/internal/portable_transaction.rb +164 -0
  259. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  260. data/lib/farce/internal/reservation_waiting.rb +24 -0
  261. data/lib/farce/internal/scheduled_task.rb +48 -0
  262. data/lib/farce/internal/scheduler_io.rb +171 -0
  263. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  264. data/lib/farce/internal/select_scheduler.rb +235 -0
  265. data/lib/farce/internal/storage.rb +162 -0
  266. data/lib/farce/internal/strict_lease.rb +30 -0
  267. data/lib/farce/internal/strict_lease_map.rb +20 -0
  268. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  269. data/lib/farce/internal/thread_pool.rb +166 -0
  270. data/lib/farce/internal/transaction_conflict.rb +9 -0
  271. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  272. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  273. data/lib/farce/internal/undefined.rb +22 -0
  274. data/lib/farce/internal/unshared_lease.rb +29 -0
  275. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  276. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  277. data/lib/farce/internal/value_serialization.rb +13 -0
  278. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  279. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  280. data/lib/farce/internal.rb +129 -0
  281. data/lib/farce/lazy.rb +100 -0
  282. data/lib/farce/lazy_ref.rb +40 -0
  283. data/lib/farce/lease.rb +28 -0
  284. data/lib/farce/lease_map.rb +34 -0
  285. data/lib/farce/lease_pool.rb +29 -0
  286. data/lib/farce/lfu_map.rb +53 -0
  287. data/lib/farce/local/atom.rb +23 -0
  288. data/lib/farce/local/counter.rb +88 -0
  289. data/lib/farce/local/flag.rb +65 -0
  290. data/lib/farce/local/lazy.rb +38 -0
  291. data/lib/farce/local/lazy_ref.rb +36 -0
  292. data/lib/farce/local/lease.rb +77 -0
  293. data/lib/farce/local/lease_map.rb +80 -0
  294. data/lib/farce/local/lease_pool.rb +38 -0
  295. data/lib/farce/local/lfu_map.rb +55 -0
  296. data/lib/farce/local/lru_map.rb +62 -0
  297. data/lib/farce/local/map.rb +47 -0
  298. data/lib/farce/local/molecule.rb +45 -0
  299. data/lib/farce/local/priority_queue.rb +53 -0
  300. data/lib/farce/local/queue.rb +32 -0
  301. data/lib/farce/local/scoped.rb +166 -0
  302. data/lib/farce/local/set.rb +18 -0
  303. data/lib/farce/local/sorted_set.rb +50 -0
  304. data/lib/farce/local/timer_queue.rb +41 -0
  305. data/lib/farce/local/tree_map.rb +47 -0
  306. data/lib/farce/local/vector.rb +30 -0
  307. data/lib/farce/local/weak_atom.rb +23 -0
  308. data/lib/farce/local/weak_key_map.rb +39 -0
  309. data/lib/farce/local/weak_map.rb +39 -0
  310. data/lib/farce/local/weak_set.rb +18 -0
  311. data/lib/farce/local/weak_value_map.rb +39 -0
  312. data/lib/farce/local.rb +35 -0
  313. data/lib/farce/lock.rb +54 -0
  314. data/lib/farce/lru_map.rb +63 -0
  315. data/lib/farce/map.rb +58 -0
  316. data/lib/farce/mode_manager.rb +142 -0
  317. data/lib/farce/molecule.rb +60 -0
  318. data/lib/farce/mutable.rb +171 -0
  319. data/lib/farce/pool.rb +332 -0
  320. data/lib/farce/port.rb +134 -0
  321. data/lib/farce/priority_queue.rb +70 -0
  322. data/lib/farce/proxy/register.rb +101 -0
  323. data/lib/farce/proxy/supervisor.rb +137 -0
  324. data/lib/farce/proxy/wrapper.rb +49 -0
  325. data/lib/farce/proxy.rb +291 -0
  326. data/lib/farce/queue.rb +60 -0
  327. data/lib/farce/ractor.rb +269 -0
  328. data/lib/farce/read_write_lock.rb +256 -0
  329. data/lib/farce/reference.rb +158 -0
  330. data/lib/farce/resolv/dns.rb +42 -0
  331. data/lib/farce/resolv.rb +85 -0
  332. data/lib/farce/scheduler.rb +536 -0
  333. data/lib/farce/set.rb +39 -0
  334. data/lib/farce/shareable.rb +150 -0
  335. data/lib/farce/signal.rb +97 -0
  336. data/lib/farce/sorted_set.rb +36 -0
  337. data/lib/farce/strict/atom.rb +34 -0
  338. data/lib/farce/strict/counter.rb +10 -0
  339. data/lib/farce/strict/exchanger.rb +25 -0
  340. data/lib/farce/strict/flag.rb +10 -0
  341. data/lib/farce/strict/lazy.rb +26 -0
  342. data/lib/farce/strict/lazy_ref.rb +26 -0
  343. data/lib/farce/strict/lease.rb +27 -0
  344. data/lib/farce/strict/lease_map.rb +33 -0
  345. data/lib/farce/strict/lease_pool.rb +28 -0
  346. data/lib/farce/strict/lfu_map.rb +27 -0
  347. data/lib/farce/strict/lru_map.rb +28 -0
  348. data/lib/farce/strict/map.rb +54 -0
  349. data/lib/farce/strict/molecule.rb +16 -0
  350. data/lib/farce/strict/port.rb +40 -0
  351. data/lib/farce/strict/priority_queue.rb +20 -0
  352. data/lib/farce/strict/queue.rb +17 -0
  353. data/lib/farce/strict/set.rb +14 -0
  354. data/lib/farce/strict/sorted_set.rb +19 -0
  355. data/lib/farce/strict/timer_queue.rb +13 -0
  356. data/lib/farce/strict/tree_map.rb +27 -0
  357. data/lib/farce/strict/vector.rb +24 -0
  358. data/lib/farce/strict/weak_atom.rb +31 -0
  359. data/lib/farce/strict/weak_key_map.rb +49 -0
  360. data/lib/farce/strict/weak_map.rb +49 -0
  361. data/lib/farce/strict/weak_set.rb +14 -0
  362. data/lib/farce/strict/weak_value_map.rb +48 -0
  363. data/lib/farce/strict.rb +28 -0
  364. data/lib/farce/system.rb +40 -0
  365. data/lib/farce/thread_scheduler.rb +70 -0
  366. data/lib/farce/timer_queue.rb +66 -0
  367. data/lib/farce/transaction/atom.rb +73 -0
  368. data/lib/farce/transaction/map.rb +25 -0
  369. data/lib/farce/transaction/map_operations.rb +143 -0
  370. data/lib/farce/transaction/molecule.rb +90 -0
  371. data/lib/farce/transaction/mutable.rb +61 -0
  372. data/lib/farce/transaction/set.rb +17 -0
  373. data/lib/farce/transaction/set_operations.rb +48 -0
  374. data/lib/farce/transaction/sorted_set.rb +18 -0
  375. data/lib/farce/transaction/tree_map.rb +78 -0
  376. data/lib/farce/transaction/vector.rb +139 -0
  377. data/lib/farce/transaction/wrapper.rb +202 -0
  378. data/lib/farce/transaction.rb +343 -0
  379. data/lib/farce/tree_map.rb +56 -0
  380. data/lib/farce/unsafe/lfu_map.rb +36 -0
  381. data/lib/farce/unsafe/lru_map.rb +36 -0
  382. data/lib/farce/unsafe/tree_map.rb +21 -0
  383. data/lib/farce/unsafe.rb +29 -0
  384. data/lib/farce/unshareable.rb +67 -0
  385. data/lib/farce/unshared/atom.rb +24 -0
  386. data/lib/farce/unshared/counter.rb +10 -0
  387. data/lib/farce/unshared/flag.rb +10 -0
  388. data/lib/farce/unshared/lazy.rb +38 -0
  389. data/lib/farce/unshared/lazy_ref.rb +26 -0
  390. data/lib/farce/unshared/lease.rb +27 -0
  391. data/lib/farce/unshared/lease_map.rb +26 -0
  392. data/lib/farce/unshared/lease_pool.rb +25 -0
  393. data/lib/farce/unshared/lfu_map.rb +20 -0
  394. data/lib/farce/unshared/lru_map.rb +20 -0
  395. data/lib/farce/unshared/map.rb +49 -0
  396. data/lib/farce/unshared/molecule.rb +16 -0
  397. data/lib/farce/unshared/priority_queue.rb +16 -0
  398. data/lib/farce/unshared/queue.rb +27 -0
  399. data/lib/farce/unshared/set.rb +14 -0
  400. data/lib/farce/unshared/sorted_set.rb +28 -0
  401. data/lib/farce/unshared/timer_queue.rb +16 -0
  402. data/lib/farce/unshared/tree_map.rb +21 -0
  403. data/lib/farce/unshared/vector.rb +19 -0
  404. data/lib/farce/unshared/weak_atom.rb +31 -0
  405. data/lib/farce/unshared/weak_key_map.rb +44 -0
  406. data/lib/farce/unshared/weak_map.rb +44 -0
  407. data/lib/farce/unshared/weak_set.rb +14 -0
  408. data/lib/farce/unshared/weak_value_map.rb +43 -0
  409. data/lib/farce/unshared.rb +28 -0
  410. data/lib/farce/vector.rb +228 -0
  411. data/lib/farce/version.rb +8 -0
  412. data/lib/farce/walker/definitions.rb +186 -0
  413. data/lib/farce/walker/modification.rb +355 -0
  414. data/lib/farce/walker.rb +319 -0
  415. data/lib/farce/weak_atom.rb +121 -0
  416. data/lib/farce/weak_key_map.rb +31 -0
  417. data/lib/farce/weak_map.rb +33 -0
  418. data/lib/farce/weak_ref.rb +67 -0
  419. data/lib/farce/weak_set.rb +82 -0
  420. data/lib/farce/weak_value.rb +195 -0
  421. data/lib/farce/weak_value_map.rb +32 -0
  422. data/lib/farce.rb +519 -0
  423. metadata +468 -0
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Abstract
7
+ # @abstract Common superclass for weak atomic references.
8
+ #
9
+ # A weak atom does not keep its current value alive. Its value becomes `nil`
10
+ # after garbage collection when no strong references remain. Collection
11
+ # timing is controlled by the garbage collector. In-flight operations may
12
+ # temporarily retain values they observe. Updates are serialized even when
13
+ # collection changes the observed value.
14
+ class WeakAtom < Atom
15
+ # @param value [BasicObject, nil] the initial value
16
+ # @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
17
+ def initialize(value = nil, compare_by_identity: false)
18
+ @atom = internal_atom_class.new(value, compare_by_identity:)
19
+ super()
20
+ end
21
+
22
+ private
23
+
24
+ # @abstract
25
+ # simplecov:disable
26
+ def internal_atom_class
27
+ raise NoMethodError, "Farce::Abstract::WeakAtom should not be instantiated directly. Use a subclass instead."
28
+ end
29
+ # simplecov:enable
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Abstract
7
+ # @abstract Common superclass for concurrent maps that retain keys weakly.
8
+ class WeakKeyMap < ConcurrentMap
9
+ def weak_keys? = true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Abstract
7
+ # @abstract Common superclass for concurrent maps that retain keys and values weakly.
8
+ class WeakMap < ConcurrentMap
9
+ def weak_keys? = true
10
+ def weak_values? = true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Abstract
7
+ # @abstract A set whose elements are retained weakly.
8
+ # Heap elements can disappear once no other strong reference remains.
9
+ class WeakSet < Set
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ module Abstract
7
+ # @abstract Common superclass for concurrent maps that retain values weakly.
8
+ class WeakValueMap < ConcurrentMap
9
+ def weak_values? = true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # Module holding abstract versions of classes that have multiple public implementations.
7
+ #
8
+ # Subclasses of these can be found under the following namespaces:
9
+ # * {Farce} - for Ractor-shareable implementations
10
+ # * {Farce::Local} - implementations scoped by Ractor/Thread/Fiber/etc
11
+ # * {Farce::Strict} - for Ractor-shareable implementations accepting only shareable values
12
+ # * {Farce::Unshared} - for Ractor-unshareable implementations
13
+ # * {Farce::Unsafe} – for implementations that are not thread-safe
14
+ module Abstract
15
+ include Internal::Autoloads
16
+ end
17
+ end
data/lib/farce/atom.rb ADDED
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # A Ractor-shareable atomic reference that strongly retains its current value and can hold non-shareable values.
7
+ #
8
+ # Values are transferred according to the atom's default {#mode}, or an operation-specific mode where supported.
9
+ # Values returned by the atom are automatically unwrapped. Updates are serialized, and comparison operations compare
10
+ # the stored values without claiming or opening their envelopes when possible.
11
+ #
12
+ # @example Creating a new atom
13
+ # atom = Farce::Atom.new("initial value")
14
+ # atom.value # => "initial value"
15
+ #
16
+ # # atoms are ractor-shareable
17
+ # Ractor.new(atom) do |atom|
18
+ # atom.update { |current| current + " updated" } # => "initial value updated"
19
+ # end
20
+ #
21
+ # # waits for the other ractor to perform its update
22
+ # atom.wait_until_changed "initial value"
23
+ #
24
+ # @example Updating a counter atomically
25
+ # # Note: Farce::Counter would have better performance
26
+ # counter = Farce::Atom.new(0)
27
+ # counter.update { |count| count + 1 } # => 1
28
+ # counter.compare_and_set(1, 2) # => true
29
+ # counter.value # => 2
30
+ #
31
+ # @example Automatically making values shareable
32
+ # atom = Farce::Atom.new([], mode: :make_shareable)
33
+ # atom.update { |items| items + [:job] } # => [:job]
34
+ # atom.value # => [:job]
35
+ # Ractor.shareable?(atom.value) # => true
36
+ class Atom < Farce::Abstract::Atom
37
+ include Shareable::Delegated
38
+
39
+ NIL_VALUE = Object.new.freeze
40
+ TIMED_OUT = Object.new.freeze
41
+ private_constant :NIL_VALUE, :TIMED_OUT
42
+
43
+ # @!macro modes
44
+ # @param value [BasicObject, nil] the initial value
45
+ # @param compare_by_identity [Boolean] whether comparisons use object identity instead of equality
46
+ # @param mode [Symbol] the default mode used to transfer values between Ractors
47
+ def initialize(value = nil, compare_by_identity: false, mode: :copy)
48
+ unless compare_by_identity == true || compare_by_identity == false
49
+ raise ArgumentError, "compare_by_identity must be a boolean"
50
+ end
51
+
52
+ @manager = ModeManager.new(mode:)
53
+ @compare_by_identity = compare_by_identity
54
+ @atom = Internal::Atom.new(wrap_value(value), compare_by_identity: true)
55
+ super()
56
+ end
57
+
58
+ # @api private
59
+ def marshal_dump
60
+ stored = @atom.value
61
+ stored = nil if NIL_VALUE.equal?(stored)
62
+ [1, Internal::MarshalSupport.value(stored), @manager, compare_by_identity?, frozen?]
63
+ end
64
+
65
+ # @api private
66
+ def marshal_load(data)
67
+ stored, @manager, identity, frozen = Internal::MarshalSupport.payload(data, 4)
68
+ @compare_by_identity = identity
69
+ @atom = Internal::Atom.new(NIL_VALUE, compare_by_identity: true)
70
+ publish_shareable
71
+ value = Internal::MarshalSupport.restore_value(stored)
72
+ @atom.store(nil.equal?(value) ? NIL_VALUE : value)
73
+ Internal::MarshalSupport.freeze(self, frozen)
74
+ end
75
+
76
+ # @api private
77
+ def transaction_wrapper(transaction)
78
+ Transaction::Atom.new(transaction, self, @atom, manager: @manager, nil_value: NIL_VALUE)
79
+ end
80
+
81
+ # The default mode used to transfer values between Ractors.
82
+ # @return [Symbol]
83
+ def mode = @manager.mode
84
+
85
+ # Whether comparisons use object identity instead of equality.
86
+ # @return [Boolean]
87
+ def compare_by_identity? = @compare_by_identity
88
+
89
+ # Return the current value without waiting for an update in progress.
90
+ # @return [BasicObject, nil] the current value
91
+ def value = unwrap_value(@atom.value)
92
+
93
+ # Store a value using the default mode.
94
+ # @param new_value [BasicObject, nil] the new value
95
+ # @return [BasicObject, nil] the new value
96
+ def value=(new_value)
97
+ store(new_value)
98
+ end
99
+
100
+ # Return the current value, waiting for any update in progress.
101
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
102
+ # @yield called when the timeout expires
103
+ # @return [BasicObject, nil] the current value or the fallback result
104
+ def get(timeout: nil, &) = unwrap_result(@atom.get(timeout:) { TIMED_OUT }, &)
105
+
106
+ # Store a new value, waiting for any update in progress.
107
+ # @!macro modes
108
+ # @param new_value [BasicObject, nil] the new value
109
+ # @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
110
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
111
+ # @yield called when the timeout expires
112
+ # @return [BasicObject, nil] the stored value or the fallback result
113
+ def store(new_value, mode: nil, timeout: nil, &)
114
+ check_frozen!
115
+ new_value = wrap_value(new_value, mode:)
116
+ unwrap_result(@atom.store(new_value, timeout:) { TIMED_OUT }, &)
117
+ end
118
+
119
+ # Replace the current value and return the previous value.
120
+ # @!macro modes
121
+ # @param new_value [BasicObject, nil] the new value
122
+ # @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
123
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
124
+ # @yield called when the timeout expires
125
+ # @return [BasicObject, nil] the previous value or the fallback result
126
+ def swap(new_value, mode: nil, timeout: nil, &)
127
+ check_frozen!
128
+ new_value = wrap_value(new_value, mode:)
129
+ unwrap_result(@atom.swap(new_value, timeout:) { TIMED_OUT }, &)
130
+ end
131
+
132
+ # Compute and store a value if the current value is nil.
133
+ # @!macro modes
134
+ # @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
135
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
136
+ # @yield computes the value to store when the current value is nil
137
+ # @yieldreturn [BasicObject, nil] the value to store
138
+ # @return [BasicObject, nil] the current or newly stored value, or nil when the timeout expires
139
+ def store_if_absent(mode: nil, timeout: nil)
140
+ raise LocalJumpError, "no block given" unless block_given?
141
+ check_frozen!
142
+
143
+ result = @atom.update(timeout:) do |current|
144
+ next current unless NIL_VALUE.equal?(current)
145
+
146
+ value = yield
147
+ check_frozen!
148
+ wrap_value(value, mode:)
149
+ end
150
+ unwrap_value(result)
151
+ end
152
+
153
+ # Atomically replace the current value if it matches the expected value.
154
+ # @!macro modes
155
+ # @param expected [BasicObject, nil] the value to compare with the current value
156
+ # @param new_value [BasicObject, nil] the replacement value
157
+ # @param mode [Symbol, nil] the replacement's transfer mode, or nil to use the atom's default mode
158
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
159
+ # @return [Boolean] whether the value was replaced
160
+ def compare_and_set(expected, new_value, mode: nil, timeout: nil)
161
+ check_frozen!
162
+ expected = wrap_comparison(expected)
163
+ matched = false
164
+
165
+ @atom.update(timeout:) do |current|
166
+ equal = values_equal?(current, expected)
167
+ check_frozen!
168
+ next current unless equal
169
+
170
+ matched = true
171
+ wrap_value(new_value, mode:)
172
+ end
173
+ matched
174
+ end
175
+
176
+ # Atomically replace the current value with the result of a block.
177
+ # @!macro modes
178
+ # @param mode [Symbol, nil] the result's transfer mode, or nil to use the atom's default mode
179
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
180
+ # @yield receives the current value and computes its replacement
181
+ # @yieldparam current [BasicObject, nil] the current value
182
+ # @yieldreturn [BasicObject, nil] the replacement value
183
+ # @return [BasicObject, nil] the replacement value, or nil when the timeout expires
184
+ def update(mode: nil, timeout: nil)
185
+ raise LocalJumpError, "no block given" unless block_given?
186
+ check_frozen!
187
+ result = @atom.update(timeout:) do |current|
188
+ value = yield(unwrap_value(current))
189
+ check_frozen!
190
+ wrap_value(value, mode:)
191
+ end
192
+ unwrap_value(result)
193
+ end
194
+
195
+ # Store an initial value if the current value is nil, otherwise replace it with the result of a block.
196
+ # @!macro modes
197
+ # @param initial_value [BasicObject, nil] the value to store when the current value is nil
198
+ # @param mode [Symbol, nil] the transfer mode, or nil to use the atom's default mode
199
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
200
+ # @yield receives a non-nil current value and computes its replacement
201
+ # @yieldparam current [BasicObject] the current value
202
+ # @yieldreturn [BasicObject, nil] the replacement value
203
+ # @return [BasicObject, nil] the current replacement or initial value, or nil when the timeout expires
204
+ def upsert(initial_value, mode: nil, timeout: nil)
205
+ raise LocalJumpError, "no block given" unless block_given?
206
+ check_frozen!
207
+
208
+ result = @atom.update(timeout:) do |current|
209
+ if NIL_VALUE.equal?(current)
210
+ wrap_value(initial_value, mode:)
211
+ else
212
+ value = yield(unwrap_value(current))
213
+ check_frozen!
214
+ wrap_value(value, mode:)
215
+ end
216
+ end
217
+ unwrap_value(result)
218
+ end
219
+
220
+ # Wait until the current value no longer matches an expected value.
221
+ # @param expected [BasicObject, nil] the value to compare with the current value
222
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
223
+ # @yield called when the timeout expires
224
+ # @return [BasicObject, nil] the changed value or the fallback result
225
+ def wait_until_changed(expected, timeout: nil, &)
226
+ expected = wrap_comparison(expected)
227
+ deadline = Internal.timeout_deadline(timeout)
228
+
229
+ while true
230
+ current = @atom.get(timeout: Internal.remaining_timeout(deadline)) { TIMED_OUT }
231
+ return unwrap_result(current, &) if TIMED_OUT.equal?(current)
232
+ return unwrap_value(current) unless values_equal?(current, expected)
233
+
234
+ result = @atom.wait_until_changed(current, timeout: Internal.remaining_timeout(deadline)) { TIMED_OUT }
235
+ return unwrap_result(result, &) if TIMED_OUT.equal?(result)
236
+ end
237
+ end
238
+
239
+ # Wait until the current value is not nil.
240
+ # @param timeout [Numeric, nil] the maximum number of seconds to wait
241
+ # @yield called when the timeout expires
242
+ # @return [BasicObject, nil] the non-nil value or the fallback result
243
+ def wait_until_non_nil(timeout: nil, &) = wait_until_changed(nil, timeout:, &)
244
+
245
+ # @api private
246
+ def inspect_with(inspector)
247
+ super do
248
+ yield if block_given?
249
+ inspector.attribute(:mode, mode)
250
+ end
251
+ end
252
+
253
+ private
254
+
255
+ def freeze_backend = @atom
256
+
257
+ def wrap_value(value, mode: nil)
258
+ value.nil? ? NIL_VALUE : @manager.wrap(value, mode:)
259
+ end
260
+
261
+ def wrap_comparison(value)
262
+ mode = compare_by_identity? ? :local : :copy
263
+ wrap_value(value, mode:)
264
+ end
265
+
266
+ def unwrap_value(value)
267
+ return if NIL_VALUE.equal?(value)
268
+
269
+ @manager.unwrap(value)
270
+ end
271
+
272
+ def inspect_value(inspector, prefix = "value=")
273
+ value = @atom.value
274
+ value = nil if NIL_VALUE.equal?(value)
275
+ inspector.breakable if prefix
276
+
277
+ if @manager.managed_envelope?(value)
278
+ inspector.from_envelope(value, prefix)
279
+ else
280
+ inspector.text(prefix) if prefix
281
+ inspector.object(value)
282
+ end
283
+ end
284
+
285
+ def unwrap_result(result)
286
+ return unwrap_value(result) unless TIMED_OUT.equal?(result)
287
+
288
+ yield if block_given?
289
+ end
290
+
291
+ def values_equal?(left, right) = @manager.same_value?(left, right, identity: compare_by_identity?)
292
+ end
293
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # Map Ruby classes to a parallel inheritance hierarchy.
7
+ # Undefined classes use the nearest defined ancestor's mirror.
8
+ # An optional block turns each lookup into a cached value for that source class.
9
+ #
10
+ # @example Specialize behavior with ordinary method inheritance
11
+ # mirror = Farce::ClassMirror.new
12
+ # mirror.define(Object) { def label = "object" }
13
+ # mirror.define(Array) { def label = "array of #{super}" }
14
+ # mirror[Array].new.label # => "array of object"
15
+ class ClassMirror
16
+ include Farce::Shareable::Tracked
17
+
18
+ # @param base_class [Class] the mirror for BasicObject
19
+ # @yieldparam mirror [Class] the nearest defined mirror class
20
+ # @yieldparam klass [Class] the class passed to {#[]}
21
+ # @yieldreturn [Object] a Ractor-shareable value to cache for klass
22
+ # @note The block must be convertible to a shareable proc.
23
+ def initialize(base_class = Class.new, &)
24
+ raise TypeError, "Class expected, got #{base_class.class}" unless base_class.is_a?(Class)
25
+ @mapping = Strict::Map.new({ BasicObject => base_class })
26
+ @cache = Strict::WeakKeyMap.new
27
+ @callback = block_given? ? Ractor.shareable_proc(&) : nil
28
+ super()
29
+ end
30
+
31
+ # @api private
32
+ def marshal_dump
33
+ raise TypeError, "class mirror callbacks cannot be marshaled" if @callback
34
+ [1, @mapping.to_h, frozen?]
35
+ end
36
+
37
+ # @api private
38
+ def marshal_load(data)
39
+ mapping, frozen = Internal::MarshalSupport.payload(data, 2)
40
+ @mapping = Strict::Map.new(mapping)
41
+ @cache = Strict::WeakKeyMap.new
42
+ @callback = nil
43
+ @farce_freeze_state = Internal::Flag.new(false)
44
+ publish_shareable
45
+ Internal::MarshalSupport.freeze(self, frozen)
46
+ end
47
+
48
+ # Look up a mirror or the cached result of the constructor block.
49
+ # @param klass [Class] the source class
50
+ # @return [Object] the mirror class or the block result
51
+ # @raise [TypeError] if klass is not a Class
52
+ def [](klass)
53
+ raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
54
+ @cache.store_if_absent(klass) do
55
+ result = lookup(klass)
56
+ @callback ? @callback.call(result, klass) : result
57
+ end
58
+ end
59
+
60
+ # Define or extend a mirror class from the main Ractor.
61
+ # Missing ancestors are defined automatically. The block defines methods
62
+ # in an included module, so `super` can call earlier definitions.
63
+ # Existing lookup results are invalidated after each definition.
64
+ # @param klass [Class] the source class
65
+ # @yield evaluated as a module body
66
+ # @return [Class] the mirror class
67
+ # @raise [Ractor::IsolationError] outside the main Ractor
68
+ # @raise [TypeError] if klass is not a Class
69
+ def define(klass, &)
70
+ check_frozen!
71
+ raise Ractor::IsolationError, "must be called from main Ractor" unless Ractor.main?
72
+ raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
73
+ mixin = Module.new(&) if block_given?
74
+ mirror = @mapping.store_if_absent(klass) { Class.new(define(klass.superclass)) }
75
+ mirror.include(mixin) if mixin
76
+ @cache.clear
77
+ mirror
78
+ end
79
+
80
+ private
81
+
82
+ def lookup(klass)
83
+ raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
84
+ @mapping.fetch(klass) { lookup(klass.superclass) }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+ # warn_indent: true
4
+
5
+ module Farce
6
+ # Wrapper module for clock-related functionality.
7
+ # Converts everything to a monotonic clock time in seconds, from when `farce/clock` was loaded.
8
+ # @see Farce.clock
9
+ module Clock
10
+ extend self
11
+
12
+ CLOCK_MONOTONIC = Process::CLOCK_MONOTONIC
13
+ CUTOFF_CLOCK = 600 # 10 minutes
14
+ CUTOFF_TIME = 1_000_000_000 # September 9, 2001 or 31-ish years ¯\_(ツ)_/¯
15
+ MONOTONIC = Process.clock_gettime(CLOCK_MONOTONIC)
16
+ REAL_TIME = Process.clock_gettime(Process::CLOCK_REALTIME)
17
+ TIME_DIFF = REAL_TIME - MONOTONIC
18
+ private_constant :CLOCK_MONOTONIC, :CUTOFF_CLOCK, :CUTOFF_TIME, :MONOTONIC, :REAL_TIME, :TIME_DIFF
19
+
20
+ # Converts the given value to a monotonic clock time in seconds, assuming that it already represents a clock time.
21
+ # Returns the current clock time if no value is given.
22
+ #
23
+ # Same as calling {#parse parse(clock: value)}
24
+ #
25
+ # @param value [nil, #to_f] the value to convert to clock time
26
+ # @raise [ArgumentError] if the resulting clock time is NaN
27
+ # @return [Float] the clock time in seconds
28
+ def clock(value) = validate_timestamp(value ? value.to_f : current)
29
+
30
+ # Returns the current monotonic clock time in seconds.
31
+ # @return [Float] the clock time in seconds
32
+ def current = Process.clock_gettime(CLOCK_MONOTONIC) - MONOTONIC
33
+
34
+ # Converts the given value to a monotonic clock time in seconds.
35
+ #
36
+ # @param value [nil, Numeric, Time, ActiveSupport::Duration, Hash]
37
+ # The value to convert to clock time
38
+ #
39
+ # If the value is `nil`, it is treated as the current time.
40
+ #
41
+ # ActiveSupport durations are relative offsets when the ActiveSupport integration is loaded.
42
+ #
43
+ # Other numeric values use the following cutoffs:
44
+ # * Up to 600 (10 minutes): Offsets from the current time.
45
+ # * Greater than 600, up to one billion: Clock times (like the value returned by {#current}).
46
+ # * Greater than one billion: UNIX timestamps (like the value returned by `Time.now.to_f`).
47
+ #
48
+ # Hashes may contain up to one key-value pair, with the key being one of the following:
49
+ # * `:at`, `:time`, or `:timeout_at` for {#time fixed times}
50
+ # * `:delay`, `:in`, `:offset`, `:timeout`, or `:wait` for {#offset relative offsets}
51
+ # * `:clock` for {#clock clock times}
52
+ #
53
+ # @raise [ArgumentError] if the resulting clock time is NaN
54
+ # @return [Float] the clock time in seconds
55
+ def parse(value = nil)
56
+ case value
57
+ when nil, UNDEFINED then return current
58
+ when Float, Integer then return value > CUTOFF_CLOCK ? time(value) : offset(value)
59
+ when Time then return time(value)
60
+ when Hash
61
+ case value.size
62
+ when 0 then return current
63
+ when 1
64
+ return time(value[:at]) if value.key?(:at)
65
+ method = argument = nil
66
+ value.each_pair { |key, option| method, argument = key, option }
67
+ return public_send(method, argument)
68
+ end
69
+ else
70
+ return at(value.to_time) if value.respond_to?(:to_time)
71
+ return parse(value.to_f) if value.is_a?(Numeric)
72
+ end
73
+ raise TypeError, "Cannot convert #{value.inspect} to clock time"
74
+ end
75
+
76
+ # Converts the given value to a monotonic clock time in seconds.
77
+ # The value is assumed to be a fixed point in time (independent of the current time).
78
+ #
79
+ # If the value is numeric and greater than one billion, it is treated as a UNIX timestamp.
80
+ # Meaning that you can't pass a clock time greater than 31 years, or a timestamp before September 9, 2001.
81
+ #
82
+ # @param value [Numeric, Time] the value to convert to clock time
83
+ # @raise [ArgumentError] if the resulting clock time is NaN
84
+ # @return [Float] the clock time in seconds
85
+ def time(value)
86
+ case value
87
+ when Float, Integer then timestamp = value > CUTOFF_TIME ? value - REAL_TIME : Float(value)
88
+ when Time then timestamp = value.to_f - REAL_TIME
89
+ when Numeric then return time(value.to_f)
90
+ else raise TypeError, "Cannot convert #{value.class} to clock time"
91
+ end
92
+ validate_timestamp(timestamp)
93
+ end
94
+
95
+ # Converts the given value to a monotonic clock time in seconds.
96
+ # The value is assumed to be a relative offset from the current time.
97
+ #
98
+ # @param value [Numeric] the value to convert to clock time
99
+ # @raise [ArgumentError] if the resulting clock time is NaN
100
+ # @return [Float] the clock time in seconds
101
+ def offset(value)
102
+ return validate_timestamp(current + value.to_f) if value.is_a?(Numeric)
103
+ raise TypeError, "Cannot convert #{value.class} to clock time"
104
+ end
105
+
106
+ alias now current
107
+ alias at time
108
+ alias timeout_at time
109
+ alias delay offset
110
+ alias in offset
111
+ alias timeout offset
112
+ alias wait offset
113
+
114
+ private
115
+
116
+ def validate_timestamp(timestamp)
117
+ raise ArgumentError, "timestamp must not be NaN" if timestamp.nan?
118
+ timestamp
119
+ end
120
+ end
121
+ end