farce 0.0.1.alpha2-x86-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/atom.c ADDED
@@ -0,0 +1,1023 @@
1
+ #include "containers.h"
2
+ #include "transaction.h"
3
+ #include "ruby/fiber/scheduler.h"
4
+ #include "ruby/io.h"
5
+
6
+ #include <errno.h>
7
+ #include <fcntl.h>
8
+ #include <math.h>
9
+ #include <stdint.h>
10
+ #include <time.h>
11
+ #include <unistd.h>
12
+
13
+ static VALUE cAtom;
14
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
15
+ static VALUE cWeakAtom;
16
+ static VALUE cUnsharedWeakAtom;
17
+
18
+ /* These functions are exported by Rubies with the probed typed-data weak
19
+ * reference callback, but their declarations intentionally remain internal. */
20
+ void rb_gc_declare_weak_references(VALUE object);
21
+ bool rb_gc_handle_weak_references_alive_p(VALUE object);
22
+ #endif
23
+
24
+ typedef struct atom_waiter atom_waiter_t;
25
+
26
+ struct atom_waiter {
27
+ int read_fd;
28
+ int write_fd;
29
+ bool notified;
30
+ atom_waiter_t *next;
31
+ };
32
+
33
+ typedef struct {
34
+ pthread_mutex_t lock;
35
+ VALUE value;
36
+ atom_waiter_t *waiters;
37
+ uint64_t version;
38
+ bool compare_by_identity;
39
+ uint8_t updating;
40
+ VALUE updating_fiber;
41
+ VALUE updating_thread;
42
+ bool weak;
43
+ bool unshared;
44
+ bool initialized;
45
+ } atom_t;
46
+
47
+ typedef struct {
48
+ bool finite;
49
+ bool collected;
50
+ double deadline;
51
+ } atom_timeout_t;
52
+
53
+ typedef struct {
54
+ VALUE fiber;
55
+ VALUE thread;
56
+ VALUE scheduler;
57
+ } atom_execution_context_t;
58
+
59
+ static atom_execution_context_t
60
+ atom_current_execution_context(void)
61
+ {
62
+ atom_execution_context_t context = {
63
+ .fiber = rb_fiber_current(),
64
+ .thread = rb_thread_current(),
65
+ .scheduler = rb_fiber_scheduler_current(),
66
+ };
67
+ return context;
68
+ }
69
+
70
+ static bool atom_wait_once(atom_t *atom, atom_timeout_t *timeout);
71
+
72
+ static double
73
+ atom_monotonic_now(void)
74
+ {
75
+ struct timespec now;
76
+ #ifdef CLOCK_MONOTONIC
77
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
78
+ return (double)now.tv_sec + (double)now.tv_nsec / 1000000000.0;
79
+ }
80
+ #endif
81
+ struct timeval fallback;
82
+ gettimeofday(&fallback, NULL);
83
+ return (double)fallback.tv_sec + (double)fallback.tv_usec / 1000000.0;
84
+ }
85
+
86
+ static atom_timeout_t
87
+ atom_parse_timeout(VALUE timeout)
88
+ {
89
+ atom_timeout_t parsed = {.finite = false, .deadline = 0};
90
+ if (NIL_P(timeout)) return parsed;
91
+ double seconds = NUM2DBL(timeout);
92
+ if (!isfinite(seconds) || seconds < 0) {
93
+ rb_raise(rb_eArgError, "timeout must be a finite, non-negative number or nil");
94
+ }
95
+ parsed.finite = true;
96
+ parsed.deadline = atom_monotonic_now() + seconds;
97
+ return parsed;
98
+ }
99
+
100
+ static void
101
+ atom_set_fd_flags(int fd)
102
+ {
103
+ #ifdef _WIN32
104
+ (void)fd;
105
+ #else
106
+ int flags = fcntl(fd, F_GETFL, 0);
107
+ if (flags >= 0) (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
108
+ flags = fcntl(fd, F_GETFD, 0);
109
+ if (flags >= 0) (void)fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
110
+ #endif
111
+ }
112
+
113
+ /* Called with atom->lock held. Every waiter owns a separate descriptor, so a
114
+ * single state transition can wake all waiters without consuming a shared
115
+ * notification. */
116
+ static void
117
+ atom_notify_waiters(atom_t *atom)
118
+ {
119
+ unsigned char byte = 1;
120
+ for (atom_waiter_t *waiter = atom->waiters; waiter; waiter = waiter->next) {
121
+ if (waiter->notified) continue;
122
+ ssize_t result;
123
+ do {
124
+ result = write(waiter->write_fd, &byte, 1);
125
+ } while (result < 0 && errno == EINTR);
126
+ if (result == 1 || (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
127
+ waiter->notified = true;
128
+ }
129
+ }
130
+ }
131
+
132
+ static void
133
+ atom_changed(atom_t *atom)
134
+ {
135
+ atom->version++;
136
+ atom_notify_waiters(atom);
137
+ }
138
+
139
+ static void
140
+ atom_finished_update(atom_t *atom, bool changed)
141
+ {
142
+ if (changed) atom->version++;
143
+ atom->updating = false;
144
+ atom->updating_fiber = Qnil;
145
+ atom->updating_thread = Qnil;
146
+ atom_notify_waiters(atom);
147
+ }
148
+
149
+ static void
150
+ atom_mark(void *pointer)
151
+ {
152
+ atom_t *atom = pointer;
153
+ if (!atom->weak) rb_gc_mark_movable(atom->value);
154
+ rb_gc_mark_movable(atom->updating_fiber);
155
+ rb_gc_mark_movable(atom->updating_thread);
156
+ }
157
+ static void
158
+ atom_compact(void *pointer)
159
+ {
160
+ atom_t *atom = pointer;
161
+ atom->value = rb_gc_location(atom->value);
162
+ atom->updating_fiber = rb_gc_location(atom->updating_fiber);
163
+ atom->updating_thread = rb_gc_location(atom->updating_thread);
164
+ }
165
+
166
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
167
+ /* The VM invokes this after marking while mutators are stopped. Taking the atom
168
+ * mutex here could deadlock if GC stopped a mutator while it owned the mutex. */
169
+ static void
170
+ atom_handle_weak_references(void *pointer)
171
+ {
172
+ atom_t *atom = pointer;
173
+ if (!atom->weak || NIL_P(atom->value)) return;
174
+ if (rb_gc_handle_weak_references_alive_p(atom->value)) return;
175
+
176
+ atom->value = Qnil;
177
+ atom_changed(atom);
178
+ }
179
+ #endif
180
+
181
+ static void
182
+ atom_free(void *pointer)
183
+ {
184
+ atom_t *atom = pointer;
185
+ pthread_mutex_destroy(&atom->lock);
186
+ ruby_xfree(atom);
187
+ }
188
+
189
+ static size_t
190
+ atom_memsize(const void *pointer)
191
+ {
192
+ return pointer ? sizeof(atom_t) : 0;
193
+ }
194
+
195
+ static const rb_data_type_t atom_type = {
196
+ .wrap_struct_name = "Ractor::Containers::Atom",
197
+ .function = {
198
+ .dmark = atom_mark,
199
+ .dfree = atom_free,
200
+ .dsize = atom_memsize,
201
+ .dcompact = atom_compact,
202
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
203
+ .handle_weak_references = atom_handle_weak_references,
204
+ #endif
205
+ },
206
+ .flags = RUBY_TYPED_FROZEN_SHAREABLE,
207
+ };
208
+
209
+ static void
210
+ atom_validate_references(VALUE self)
211
+ {
212
+ atom_t *atom;
213
+ TypedData_Get_Struct(self, atom_t, &atom_type, atom);
214
+ containers_check_shareable(atom->value);
215
+ containers_check_shareable(atom->updating_fiber);
216
+ containers_check_shareable(atom->updating_thread);
217
+ }
218
+
219
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
220
+ /* A separate type keeps mutable referents local even if the wrapper is frozen. */
221
+ static const rb_data_type_t unshared_weak_atom_type = {
222
+ .wrap_struct_name = "Farce::Internal::UnsharedWeakAtom",
223
+ .function = {
224
+ .dmark = atom_mark,
225
+ .dfree = atom_free,
226
+ .dsize = atom_memsize,
227
+ .dcompact = atom_compact,
228
+ .handle_weak_references = atom_handle_weak_references,
229
+ },
230
+ .parent = &atom_type,
231
+ };
232
+ #endif
233
+
234
+ static VALUE
235
+ atom_allocate(VALUE klass)
236
+ {
237
+ atom_t *atom;
238
+ const rb_data_type_t *type = &atom_type;
239
+ bool unshared = false;
240
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
241
+ unshared = cUnsharedWeakAtom &&
242
+ (klass == cUnsharedWeakAtom || rb_class_inherited_p(klass, cUnsharedWeakAtom) == Qtrue);
243
+ if (unshared) type = &unshared_weak_atom_type;
244
+ #endif
245
+ VALUE object = TypedData_Make_Struct(klass, atom_t, type, atom);
246
+ atom->unshared = unshared;
247
+ pthread_mutex_init(&atom->lock, NULL);
248
+ atom->value = Qnil;
249
+ atom->waiters = NULL;
250
+ atom->version = 0;
251
+ atom->compare_by_identity = false;
252
+ atom->updating = false;
253
+ atom->updating_fiber = Qnil;
254
+ atom->updating_thread = Qnil;
255
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
256
+ atom->weak = unshared || klass == cWeakAtom ||
257
+ (cWeakAtom && rb_class_inherited_p(klass, cWeakAtom) == Qtrue);
258
+ #else
259
+ atom->weak = false;
260
+ #endif
261
+ atom->initialized = false;
262
+ return object;
263
+ }
264
+
265
+ static atom_t *
266
+ get_atom(VALUE self)
267
+ {
268
+ atom_t *atom;
269
+ TypedData_Get_Struct(self, atom_t, &atom_type, atom);
270
+ if (!atom->initialized) rb_raise(rb_eRuntimeError, "uninitialized Atom");
271
+ return atom;
272
+ }
273
+
274
+ static void
275
+ atom_check_value(atom_t *atom, VALUE value)
276
+ {
277
+ if (!atom->unshared) containers_check_shareable(value);
278
+ }
279
+
280
+ /* Called with atom->lock held. Raising releases the short native mutex so the
281
+ * reservation owner's ensure handler can clear it normally. */
282
+ static void
283
+ atom_check_update_wait_locked(atom_t *atom, const atom_execution_context_t *context)
284
+ {
285
+ if (atom->updating_fiber == context->fiber) {
286
+ pthread_mutex_unlock(&atom->lock);
287
+ rb_raise(rb_eThreadError, "deadlock; recursive atom access during an update");
288
+ }
289
+ if (atom->updating_thread == context->thread && NIL_P(context->scheduler)) {
290
+ pthread_mutex_unlock(&atom->lock);
291
+ rb_raise(
292
+ rb_eThreadError,
293
+ "deadlock; atom update is owned by another unscheduled fiber"
294
+ );
295
+ }
296
+ }
297
+
298
+ static void
299
+ atom_begin_update_locked(atom_t *atom, const atom_execution_context_t *context)
300
+ {
301
+ atom->updating = true;
302
+ atom->updating_fiber = context->fiber;
303
+ atom->updating_thread = context->thread;
304
+ }
305
+
306
+ static void
307
+ atom_lock_for_update(atom_t *atom, const atom_execution_context_t *context)
308
+ {
309
+ atom_timeout_t timeout = {.finite = false, .deadline = 0};
310
+
311
+ for (;;) {
312
+ pthread_mutex_lock(&atom->lock);
313
+ if (!atom->updating) return;
314
+ atom_check_update_wait_locked(atom, context);
315
+ (void)atom_wait_once(atom, &timeout);
316
+ }
317
+ }
318
+
319
+ static VALUE
320
+ atom_extract_timeout(int argc, VALUE *argv, const char *format, VALUE *argument)
321
+ {
322
+ VALUE keywords = Qnil;
323
+ VALUE timeout = Qnil;
324
+ ID keyword_ids[] = {rb_intern("timeout")};
325
+ VALUE keyword_values[1];
326
+
327
+ if (argument) rb_scan_args(argc, argv, format, argument, &keywords);
328
+ else rb_scan_args(argc, argv, format, &keywords);
329
+ if (!NIL_P(keywords)) {
330
+ rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
331
+ if (keyword_values[0] != Qundef) timeout = keyword_values[0];
332
+ }
333
+ return timeout;
334
+ }
335
+
336
+ typedef struct {
337
+ atom_t *atom;
338
+ atom_waiter_t waiter;
339
+ atom_timeout_t *timeout;
340
+ } atom_wait_context_t;
341
+
342
+ static bool
343
+ atom_wait_for_descriptor(int fd, atom_timeout_t *timeout)
344
+ {
345
+ VALUE wait_timeout = Qnil;
346
+ if (timeout->finite) {
347
+ double remaining = timeout->deadline - atom_monotonic_now();
348
+ if (remaining <= 0) return false;
349
+ wait_timeout = DBL2NUM(remaining);
350
+ }
351
+ return containers_wait_for_readable(fd, wait_timeout);
352
+ }
353
+
354
+ static VALUE
355
+ atom_wait_body(VALUE opaque)
356
+ {
357
+ atom_wait_context_t *context = (atom_wait_context_t *)opaque;
358
+ return atom_wait_for_descriptor(context->waiter.read_fd, context->timeout) ? Qtrue : Qfalse;
359
+ }
360
+
361
+ static VALUE
362
+ atom_wait_cleanup(VALUE opaque)
363
+ {
364
+ atom_wait_context_t *context = (atom_wait_context_t *)opaque;
365
+ atom_waiter_t **link;
366
+
367
+ pthread_mutex_lock(&context->atom->lock);
368
+ for (link = &context->atom->waiters; *link; link = &(*link)->next) {
369
+ if (*link == &context->waiter) {
370
+ *link = context->waiter.next;
371
+ break;
372
+ }
373
+ }
374
+ pthread_mutex_unlock(&context->atom->lock);
375
+ close(context->waiter.read_fd);
376
+ close(context->waiter.write_fd);
377
+ return Qnil;
378
+ }
379
+
380
+ /* Called with atom->lock held and always returns with it released. */
381
+ static bool
382
+ atom_wait_once(atom_t *atom, atom_timeout_t *timeout)
383
+ {
384
+ int descriptors[2];
385
+ if (pipe(descriptors) != 0) {
386
+ int error = errno;
387
+ pthread_mutex_unlock(&atom->lock);
388
+ double seconds = timeout->finite ? timeout->deadline - atom_monotonic_now() : 0;
389
+ if ((error == EMFILE || error == ENFILE) && timeout->finite && seconds <= 0) return false;
390
+ VALUE remaining = timeout->finite ? DBL2NUM(seconds) : Qnil;
391
+ containers_wait_without_descriptor(error, "pipe", &timeout->collected, remaining);
392
+ return true;
393
+ }
394
+ atom_set_fd_flags(descriptors[0]);
395
+ atom_set_fd_flags(descriptors[1]);
396
+
397
+ atom_wait_context_t context = {
398
+ .atom = atom,
399
+ .waiter = {
400
+ .read_fd = descriptors[0],
401
+ .write_fd = descriptors[1],
402
+ .notified = false,
403
+ .next = atom->waiters,
404
+ },
405
+ .timeout = timeout,
406
+ };
407
+ atom->waiters = &context.waiter;
408
+ pthread_mutex_unlock(&atom->lock);
409
+ return RTEST(rb_ensure(atom_wait_body, (VALUE)&context, atom_wait_cleanup, (VALUE)&context));
410
+ }
411
+
412
+ /* Returns with atom->lock held on success and released on timeout. */
413
+ static bool
414
+ atom_lock_for_update_with_timeout(
415
+ atom_t *atom,
416
+ atom_timeout_t *timeout,
417
+ const atom_execution_context_t *context
418
+ )
419
+ {
420
+ for (;;) {
421
+ pthread_mutex_lock(&atom->lock);
422
+ if (!atom->updating) return true;
423
+ atom_check_update_wait_locked(atom, context);
424
+ if (!atom_wait_once(atom, timeout)) return false;
425
+ }
426
+ }
427
+
428
+ /* Returns with the state mutex held. Ordinary updates still permit value
429
+ * reads. Only the external commit reservation excludes them. */
430
+ static bool
431
+ atom_lock_for_read(atom_t *atom, atom_timeout_t *timeout)
432
+ {
433
+ for (;;) {
434
+ pthread_mutex_lock(&atom->lock);
435
+ if (atom->updating != FARCE_TRANSACTION_RESERVED) return true;
436
+ pthread_mutex_unlock(&atom->lock);
437
+ atom_execution_context_t context = atom_current_execution_context();
438
+ pthread_mutex_lock(&atom->lock);
439
+ if (atom->updating != FARCE_TRANSACTION_RESERVED) return true;
440
+ atom_check_update_wait_locked(atom, &context);
441
+ if (!atom_wait_once(atom, timeout) && timeout->finite) return false;
442
+ }
443
+ }
444
+
445
+ static VALUE
446
+ atom_reserved_value(VALUE self)
447
+ {
448
+ atom_t *atom = get_atom(self);
449
+ atom_timeout_t timeout = {.finite = false, .deadline = 0};
450
+ (void)atom_lock_for_read(atom, &timeout);
451
+ VALUE value = atom->value;
452
+ pthread_mutex_unlock(&atom->lock);
453
+ return value;
454
+ }
455
+
456
+ static VALUE
457
+ atom_freeze(VALUE self)
458
+ {
459
+ atom_t *atom;
460
+ TypedData_Get_Struct(self, atom_t, &atom_type, atom);
461
+ atom_timeout_t timeout = {.finite = false, .deadline = 0};
462
+ (void)atom_lock_for_read(atom, &timeout);
463
+ rb_obj_freeze(self);
464
+ pthread_mutex_unlock(&atom->lock);
465
+ return self;
466
+ }
467
+
468
+ static VALUE
469
+ atom_timeout_result(void)
470
+ {
471
+ return rb_block_given_p() ? rb_yield_values(0) : Qnil;
472
+ }
473
+
474
+ static VALUE
475
+ atom_initialize(int argc, VALUE *argv, VALUE self)
476
+ {
477
+ VALUE value = Qnil;
478
+ VALUE keywords = Qnil;
479
+ VALUE identity = Qundef;
480
+ ID keyword_ids[] = {rb_intern("compare_by_identity")};
481
+ VALUE keyword_values[1];
482
+ atom_t *atom;
483
+
484
+ rb_check_frozen(self);
485
+ rb_scan_args(argc, argv, "01:", &value, &keywords);
486
+ if (!NIL_P(keywords)) {
487
+ rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
488
+ identity = keyword_values[0];
489
+ }
490
+
491
+ TypedData_Get_Struct(self, atom_t, &atom_type, atom);
492
+ if (atom->initialized) rb_raise(rb_eRuntimeError, "Atom is already initialized");
493
+ atom_check_value(atom, value);
494
+ atom->value = value;
495
+ atom->compare_by_identity = identity == Qundef ? false : containers_strict_bool(identity, "compare_by_identity");
496
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
497
+ if (atom->weak) rb_gc_declare_weak_references(self);
498
+ #endif
499
+ atom->initialized = true;
500
+ if (atom->unshared) return self;
501
+ return containers_publish_native_with_references(self, atom_validate_references);
502
+ }
503
+
504
+ static VALUE
505
+ atom_value(VALUE self)
506
+ {
507
+ atom_t *atom = get_atom(self);
508
+ VALUE value;
509
+ pthread_mutex_lock(&atom->lock);
510
+ value = atom->value;
511
+ pthread_mutex_unlock(&atom->lock);
512
+ return value;
513
+ }
514
+
515
+ static VALUE
516
+ atom_set_value(VALUE self, VALUE value)
517
+ {
518
+ atom_t *atom = get_atom(self);
519
+ rb_check_frozen(self);
520
+ atom_check_value(atom, value);
521
+ atom_execution_context_t execution = atom_current_execution_context();
522
+ atom_lock_for_update(atom, &execution);
523
+ atom->value = value;
524
+ atom_changed(atom);
525
+ pthread_mutex_unlock(&atom->lock);
526
+ return value;
527
+ }
528
+
529
+ static VALUE
530
+ atom_get(int argc, VALUE *argv, VALUE self)
531
+ {
532
+ atom_t *atom = get_atom(self);
533
+ atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
534
+ atom_execution_context_t execution = atom_current_execution_context();
535
+
536
+ for (;;) {
537
+ pthread_mutex_lock(&atom->lock);
538
+ if (!atom->updating) {
539
+ VALUE value = atom->value;
540
+ pthread_mutex_unlock(&atom->lock);
541
+ return value;
542
+ }
543
+ atom_check_update_wait_locked(atom, &execution);
544
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
545
+ }
546
+ }
547
+
548
+ static VALUE
549
+ atom_store(int argc, VALUE *argv, VALUE self)
550
+ {
551
+ VALUE value;
552
+ atom_t *atom = get_atom(self);
553
+ rb_check_frozen(self);
554
+ VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &value);
555
+ atom_check_value(atom, value);
556
+ atom_timeout_t timeout = atom_parse_timeout(timeout_value);
557
+ rb_check_frozen(self);
558
+ atom_execution_context_t execution = atom_current_execution_context();
559
+
560
+ for (;;) {
561
+ pthread_mutex_lock(&atom->lock);
562
+ if (!atom->updating) {
563
+ atom->value = value;
564
+ atom_changed(atom);
565
+ pthread_mutex_unlock(&atom->lock);
566
+ return value;
567
+ }
568
+ atom_check_update_wait_locked(atom, &execution);
569
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
570
+ }
571
+ }
572
+
573
+ static VALUE
574
+ atom_swap(int argc, VALUE *argv, VALUE self)
575
+ {
576
+ VALUE value;
577
+ atom_t *atom = get_atom(self);
578
+ rb_check_frozen(self);
579
+ VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &value);
580
+ atom_check_value(atom, value);
581
+ atom_timeout_t timeout = atom_parse_timeout(timeout_value);
582
+ rb_check_frozen(self);
583
+ atom_execution_context_t execution = atom_current_execution_context();
584
+
585
+ for (;;) {
586
+ pthread_mutex_lock(&atom->lock);
587
+ if (!atom->updating) {
588
+ VALUE previous = atom->value;
589
+ atom->value = value;
590
+ atom_changed(atom);
591
+ pthread_mutex_unlock(&atom->lock);
592
+ return previous;
593
+ }
594
+ atom_check_update_wait_locked(atom, &execution);
595
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
596
+ }
597
+ }
598
+
599
+ typedef struct {
600
+ VALUE self;
601
+ atom_t *atom;
602
+ atom_execution_context_t execution;
603
+ VALUE current;
604
+ VALUE argument;
605
+ bool identity;
606
+ bool complete;
607
+ } atom_operation_t;
608
+
609
+ static VALUE
610
+ atom_operation_cleanup(VALUE opaque)
611
+ {
612
+ atom_operation_t *operation = (atom_operation_t *)opaque;
613
+ if (!operation->complete) {
614
+ pthread_mutex_lock(&operation->atom->lock);
615
+ atom_finished_update(operation->atom, false);
616
+ pthread_mutex_unlock(&operation->atom->lock);
617
+ }
618
+ return Qnil;
619
+ }
620
+
621
+ static VALUE
622
+ atom_store_body(VALUE opaque)
623
+ {
624
+ atom_operation_t *operation = (atom_operation_t *)opaque;
625
+ VALUE result = rb_yield_values(0);
626
+ rb_check_frozen(operation->self);
627
+ atom_check_value(operation->atom, result);
628
+ pthread_mutex_lock(&operation->atom->lock);
629
+ operation->atom->value = result;
630
+ atom_finished_update(operation->atom, true);
631
+ pthread_mutex_unlock(&operation->atom->lock);
632
+ operation->complete = true;
633
+ return result;
634
+ }
635
+
636
+ static VALUE
637
+ atom_store_if_absent(int argc, VALUE *argv, VALUE self)
638
+ {
639
+ atom_t *atom = get_atom(self);
640
+ atom_operation_t operation = {
641
+ .self = self,
642
+ .atom = atom,
643
+ .execution = atom_current_execution_context(),
644
+ .complete = false,
645
+ };
646
+ rb_check_frozen(self);
647
+ atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
648
+ rb_check_frozen(self);
649
+
650
+ rb_need_block();
651
+ if (!atom_lock_for_update_with_timeout(atom, &timeout, &operation.execution)) return Qnil;
652
+ if (!NIL_P(atom->value)) {
653
+ VALUE value = atom->value;
654
+ pthread_mutex_unlock(&atom->lock);
655
+ return value;
656
+ }
657
+ atom_begin_update_locked(atom, &operation.execution);
658
+ pthread_mutex_unlock(&atom->lock);
659
+ return rb_ensure(atom_store_body, (VALUE)&operation, atom_operation_cleanup, (VALUE)&operation);
660
+ }
661
+
662
+ typedef struct {
663
+ VALUE self;
664
+ atom_t *atom;
665
+ atom_execution_context_t execution;
666
+ VALUE current;
667
+ VALUE expected;
668
+ VALUE replacement;
669
+ bool identity;
670
+ bool complete;
671
+ } atom_cas_t;
672
+
673
+ static VALUE
674
+ atom_cas_cleanup(VALUE opaque)
675
+ {
676
+ atom_cas_t *operation = (atom_cas_t *)opaque;
677
+ if (!operation->complete) {
678
+ pthread_mutex_lock(&operation->atom->lock);
679
+ atom_finished_update(operation->atom, false);
680
+ pthread_mutex_unlock(&operation->atom->lock);
681
+ }
682
+ return Qnil;
683
+ }
684
+
685
+ static VALUE
686
+ atom_cas_body(VALUE opaque)
687
+ {
688
+ atom_cas_t *operation = (atom_cas_t *)opaque;
689
+ bool matches = operation->identity
690
+ ? operation->current == operation->expected
691
+ : RTEST(rb_equal(operation->current, operation->expected));
692
+ rb_check_frozen(operation->self);
693
+
694
+ pthread_mutex_lock(&operation->atom->lock);
695
+ if (matches) operation->atom->value = operation->replacement;
696
+ atom_finished_update(operation->atom, matches);
697
+ pthread_mutex_unlock(&operation->atom->lock);
698
+ operation->complete = true;
699
+ return matches ? Qtrue : Qfalse;
700
+ }
701
+
702
+ static VALUE
703
+ atom_compare_and_set(int argc, VALUE *argv, VALUE self)
704
+ {
705
+ VALUE expected;
706
+ VALUE replacement;
707
+ VALUE keywords = Qnil;
708
+ VALUE keyword_values[1] = {Qundef};
709
+ ID keyword_ids[] = {rb_intern("timeout")};
710
+ rb_scan_args(argc, argv, "2:", &expected, &replacement, &keywords);
711
+ if (!NIL_P(keywords)) rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
712
+
713
+ atom_t *atom = get_atom(self);
714
+ atom_cas_t operation;
715
+ rb_check_frozen(self);
716
+ atom_check_value(atom, expected);
717
+ atom_check_value(atom, replacement);
718
+ atom_timeout_t timeout = atom_parse_timeout(
719
+ keyword_values[0] == Qundef ? Qnil : keyword_values[0]
720
+ );
721
+ rb_check_frozen(self);
722
+ atom_execution_context_t execution = atom_current_execution_context();
723
+
724
+ if (!atom_lock_for_update_with_timeout(atom, &timeout, &execution)) return Qfalse;
725
+ operation.atom = atom;
726
+ operation.self = self;
727
+ operation.execution = execution;
728
+ operation.current = atom->value;
729
+ operation.expected = expected;
730
+ operation.replacement = replacement;
731
+ operation.identity = atom->compare_by_identity;
732
+ operation.complete = false;
733
+ atom_begin_update_locked(atom, &operation.execution);
734
+ pthread_mutex_unlock(&atom->lock);
735
+
736
+ return rb_ensure(atom_cas_body, (VALUE)&operation, atom_cas_cleanup, (VALUE)&operation);
737
+ }
738
+
739
+ static VALUE
740
+ atom_update_body(VALUE opaque)
741
+ {
742
+ atom_operation_t *operation = (atom_operation_t *)opaque;
743
+ VALUE result = rb_yield(operation->current);
744
+ rb_check_frozen(operation->self);
745
+ atom_check_value(operation->atom, result);
746
+ pthread_mutex_lock(&operation->atom->lock);
747
+ operation->atom->value = result;
748
+ atom_finished_update(operation->atom, true);
749
+ pthread_mutex_unlock(&operation->atom->lock);
750
+ operation->complete = true;
751
+ return result;
752
+ }
753
+
754
+ static VALUE
755
+ atom_update(int argc, VALUE *argv, VALUE self)
756
+ {
757
+ atom_t *atom = get_atom(self);
758
+ atom_operation_t operation = {
759
+ .self = self,
760
+ .atom = atom,
761
+ .execution = atom_current_execution_context(),
762
+ .complete = false,
763
+ };
764
+ rb_check_frozen(self);
765
+ atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
766
+ rb_check_frozen(self);
767
+ rb_need_block();
768
+
769
+ if (!atom_lock_for_update_with_timeout(atom, &timeout, &operation.execution)) return Qnil;
770
+ operation.current = atom->value;
771
+ atom_begin_update_locked(atom, &operation.execution);
772
+ pthread_mutex_unlock(&atom->lock);
773
+
774
+ return rb_ensure(atom_update_body, (VALUE)&operation, atom_operation_cleanup, (VALUE)&operation);
775
+ }
776
+
777
+ static VALUE
778
+ atom_upsert(int argc, VALUE *argv, VALUE self)
779
+ {
780
+ VALUE initial;
781
+ atom_t *atom = get_atom(self);
782
+ atom_operation_t operation = {
783
+ .self = self,
784
+ .atom = atom,
785
+ .execution = atom_current_execution_context(),
786
+ .complete = false,
787
+ };
788
+ rb_check_frozen(self);
789
+ atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "1:", &initial));
790
+ rb_check_frozen(self);
791
+ atom_check_value(atom, initial);
792
+ rb_need_block();
793
+
794
+ if (!atom_lock_for_update_with_timeout(atom, &timeout, &operation.execution)) return Qnil;
795
+ if (NIL_P(atom->value)) {
796
+ atom->value = initial;
797
+ atom_changed(atom);
798
+ pthread_mutex_unlock(&atom->lock);
799
+ return initial;
800
+ }
801
+ operation.current = atom->value;
802
+ atom_begin_update_locked(atom, &operation.execution);
803
+ pthread_mutex_unlock(&atom->lock);
804
+
805
+ return rb_ensure(atom_update_body, (VALUE)&operation, atom_operation_cleanup, (VALUE)&operation);
806
+ }
807
+
808
+ static bool
809
+ atom_values_equal(atom_t *atom, VALUE left, VALUE right)
810
+ {
811
+ return atom->compare_by_identity ? left == right : RTEST(rb_equal(left, right));
812
+ }
813
+
814
+ static VALUE
815
+ atom_wait_until_changed(int argc, VALUE *argv, VALUE self)
816
+ {
817
+ VALUE expected;
818
+ atom_t *atom = get_atom(self);
819
+ VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &expected);
820
+ atom_check_value(atom, expected);
821
+ atom_timeout_t timeout = atom_parse_timeout(timeout_value);
822
+ atom_execution_context_t execution = atom_current_execution_context();
823
+
824
+ for (;;) {
825
+ VALUE current;
826
+ uint64_t version;
827
+ pthread_mutex_lock(&atom->lock);
828
+ current = atom->value;
829
+ version = atom->version;
830
+ pthread_mutex_unlock(&atom->lock);
831
+
832
+ if (!atom_values_equal(atom, current, expected)) return current;
833
+
834
+ pthread_mutex_lock(&atom->lock);
835
+ if (atom->version != version) {
836
+ pthread_mutex_unlock(&atom->lock);
837
+ continue;
838
+ }
839
+ if (atom->updating) atom_check_update_wait_locked(atom, &execution);
840
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
841
+ }
842
+ }
843
+
844
+ static VALUE
845
+ atom_wait_until_changed_reserved(int argc, VALUE *argv, VALUE self)
846
+ {
847
+ VALUE expected;
848
+ atom_t *atom = get_atom(self);
849
+ VALUE timeout_value = atom_extract_timeout(argc, argv, "1:", &expected);
850
+ atom_check_value(atom, expected);
851
+ atom_timeout_t timeout = atom_parse_timeout(timeout_value);
852
+ atom_execution_context_t execution = atom_current_execution_context();
853
+
854
+ for (;;) {
855
+ VALUE current;
856
+ uint64_t version;
857
+ if (!atom_lock_for_read(atom, &timeout)) return atom_timeout_result();
858
+ current = atom->value;
859
+ version = atom->version;
860
+ pthread_mutex_unlock(&atom->lock);
861
+
862
+ if (!atom_values_equal(atom, current, expected)) return current;
863
+
864
+ if (!atom_lock_for_read(atom, &timeout)) return atom_timeout_result();
865
+ if (atom->version != version) {
866
+ pthread_mutex_unlock(&atom->lock);
867
+ continue;
868
+ }
869
+ if (atom->updating) atom_check_update_wait_locked(atom, &execution);
870
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
871
+ }
872
+ }
873
+
874
+ static VALUE
875
+ atom_wait_until_non_nil(int argc, VALUE *argv, VALUE self)
876
+ {
877
+ atom_t *atom = get_atom(self);
878
+ atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
879
+ atom_execution_context_t execution = atom_current_execution_context();
880
+
881
+ for (;;) {
882
+ pthread_mutex_lock(&atom->lock);
883
+ if (!NIL_P(atom->value)) {
884
+ VALUE current = atom->value;
885
+ pthread_mutex_unlock(&atom->lock);
886
+ return current;
887
+ }
888
+ if (atom->updating) atom_check_update_wait_locked(atom, &execution);
889
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
890
+ }
891
+ }
892
+
893
+ static VALUE
894
+ atom_wait_until_non_nil_reserved(int argc, VALUE *argv, VALUE self)
895
+ {
896
+ atom_t *atom = get_atom(self);
897
+ atom_timeout_t timeout = atom_parse_timeout(atom_extract_timeout(argc, argv, "0:", NULL));
898
+ atom_execution_context_t execution = atom_current_execution_context();
899
+
900
+ for (;;) {
901
+ if (!atom_lock_for_read(atom, &timeout)) return atom_timeout_result();
902
+ if (!NIL_P(atom->value)) {
903
+ VALUE current = atom->value;
904
+ pthread_mutex_unlock(&atom->lock);
905
+ return current;
906
+ }
907
+ if (atom->updating) atom_check_update_wait_locked(atom, &execution);
908
+ if (!atom_wait_once(atom, &timeout)) return atom_timeout_result();
909
+ }
910
+ }
911
+
912
+ static VALUE
913
+ atom_compare_by_identity_p(VALUE self)
914
+ {
915
+ return get_atom(self)->compare_by_identity ? Qtrue : Qfalse;
916
+ }
917
+
918
+ static bool
919
+ atom_transaction_valid(farce_transaction_entry_t *entry)
920
+ {
921
+ atom_t *atom = entry->source_data;
922
+ return atom->version == entry->version && !atom->updating;
923
+ }
924
+
925
+ static void
926
+ atom_transaction_apply(farce_transaction_entry_t *entry)
927
+ {
928
+ atom_t *atom = entry->source_data;
929
+ atom_t *working = entry->working_data;
930
+ atom->value = working->value;
931
+ }
932
+
933
+ static void
934
+ atom_transaction_notify(farce_transaction_entry_t *entry)
935
+ {
936
+ atom_changed(entry->source_data);
937
+ }
938
+
939
+ static void
940
+ atom_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE thread)
941
+ {
942
+ atom_t *atom = entry->source_data;
943
+ atom->updating = FARCE_TRANSACTION_RESERVED;
944
+ atom->updating_fiber = fiber;
945
+ atom->updating_thread = thread;
946
+ }
947
+
948
+ static void
949
+ atom_transaction_release(farce_transaction_entry_t *entry)
950
+ {
951
+ atom_finished_update(entry->source_data, false);
952
+ }
953
+
954
+ static const farce_transaction_ops_t atom_transaction_ops = {
955
+ atom_transaction_valid, atom_transaction_apply, atom_transaction_notify,
956
+ atom_transaction_reserve, atom_transaction_release,
957
+ };
958
+
959
+ static VALUE
960
+ atom_transaction_snapshot(VALUE self)
961
+ {
962
+ atom_t *source = get_atom(self);
963
+ if (source->weak) rb_raise(rb_eTypeError, "weak atoms do not support transactions");
964
+ VALUE working = atom_allocate(cAtom);
965
+ atom_t *copy;
966
+ TypedData_Get_Struct(working, atom_t, &atom_type, copy);
967
+ farce_transaction_entry_t *entry;
968
+ VALUE result = farce_transaction_entry_new(
969
+ self, working, source, copy, &source->lock, &atom_transaction_ops, &entry
970
+ );
971
+ pthread_mutex_lock(&source->lock);
972
+ copy->value = source->value;
973
+ copy->compare_by_identity = source->compare_by_identity;
974
+ copy->unshared = source->unshared;
975
+ copy->initialized = true;
976
+ entry->version = source->version;
977
+ pthread_mutex_unlock(&source->lock);
978
+ return result;
979
+ }
980
+
981
+ static void
982
+ define_atom_methods(VALUE klass, bool reserved_reads)
983
+ {
984
+ rb_define_alloc_func(klass, atom_allocate);
985
+ rb_define_method(klass, "initialize", atom_initialize, -1);
986
+ rb_define_method(klass, "value", reserved_reads ? atom_reserved_value : atom_value, 0);
987
+ rb_define_method(klass, "value=", atom_set_value, 1);
988
+ rb_define_method(klass, "get", atom_get, -1);
989
+ rb_define_method(klass, "store", atom_store, -1);
990
+ rb_define_method(klass, "swap", atom_swap, -1);
991
+ rb_define_method(klass, "store_if_absent", atom_store_if_absent, -1);
992
+ rb_define_method(klass, "transaction_snapshot", atom_transaction_snapshot, 0);
993
+ rb_define_method(klass, "compare_and_set", atom_compare_and_set, -1);
994
+ rb_define_method(klass, "update", atom_update, -1);
995
+ rb_define_method(klass, "upsert", atom_upsert, -1);
996
+ rb_define_method(klass, "wait_until_changed", reserved_reads ? atom_wait_until_changed_reserved : atom_wait_until_changed, -1);
997
+ rb_define_method(klass, "wait_until_non_nil", reserved_reads ? atom_wait_until_non_nil_reserved : atom_wait_until_non_nil, -1);
998
+ rb_define_method(klass, "compare_by_identity?", atom_compare_by_identity_p, 0);
999
+ }
1000
+
1001
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
1002
+ static VALUE
1003
+ unshared_weak_atom_initialize_copy(VALUE self, VALUE other)
1004
+ {
1005
+ rb_raise(rb_eTypeError, "cannot copy UnsharedWeakAtom");
1006
+ }
1007
+ #endif
1008
+
1009
+
1010
+ void
1011
+ containers_init_atom(VALUE namespace)
1012
+ {
1013
+ cAtom = rb_define_class_under(namespace, "Atom", rb_cObject);
1014
+ define_atom_methods(cAtom, true);
1015
+ rb_define_method(cAtom, "freeze", atom_freeze, 0);
1016
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
1017
+ cWeakAtom = rb_define_class_under(namespace, "WeakAtom", rb_cObject);
1018
+ define_atom_methods(cWeakAtom, false);
1019
+ cUnsharedWeakAtom = rb_define_class_under(namespace, "UnsharedWeakAtom", rb_cObject);
1020
+ define_atom_methods(cUnsharedWeakAtom, false);
1021
+ rb_define_private_method(cUnsharedWeakAtom, "initialize_copy", unshared_weak_atom_initialize_copy, 1);
1022
+ #endif
1023
+ }