├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs ├── rand.rs └── time.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | # RustRover 13 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 14 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 15 | # and can be added to the global gitignore or merged into this file. For a more nuclear 16 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 17 | #.idea/ 18 | 19 | # Added by cargo 20 | 21 | /target 22 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.4.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.74" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 31 | dependencies = [ 32 | "addr2line", 33 | "cfg-if", 34 | "libc", 35 | "miniz_oxide", 36 | "object", 37 | "rustc-demangle", 38 | "windows-targets", 39 | ] 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "2.9.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 46 | 47 | [[package]] 48 | name = "bytes" 49 | version = "1.10.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 52 | 53 | [[package]] 54 | name = "cfg-if" 55 | version = "1.0.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 58 | 59 | [[package]] 60 | name = "equivalent" 61 | version = "1.0.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 64 | 65 | [[package]] 66 | name = "getrandom" 67 | version = "0.2.15" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 70 | dependencies = [ 71 | "cfg-if", 72 | "libc", 73 | "wasi 0.11.0+wasi-snapshot-preview1", 74 | ] 75 | 76 | [[package]] 77 | name = "getrandom" 78 | version = "0.3.2" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 81 | dependencies = [ 82 | "cfg-if", 83 | "libc", 84 | "r-efi", 85 | "wasi 0.14.2+wasi-0.2.4", 86 | ] 87 | 88 | [[package]] 89 | name = "gimli" 90 | version = "0.31.1" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 93 | 94 | [[package]] 95 | name = "hashbrown" 96 | version = "0.15.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 99 | 100 | [[package]] 101 | name = "indexmap" 102 | version = "2.8.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 105 | dependencies = [ 106 | "equivalent", 107 | "hashbrown", 108 | ] 109 | 110 | [[package]] 111 | name = "libc" 112 | version = "0.2.171" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 115 | 116 | [[package]] 117 | name = "libm" 118 | version = "0.2.11" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" 121 | 122 | [[package]] 123 | name = "lock_api" 124 | version = "0.4.12" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 127 | dependencies = [ 128 | "autocfg", 129 | "scopeguard", 130 | ] 131 | 132 | [[package]] 133 | name = "mad-turmoil" 134 | version = "0.1.0" 135 | dependencies = [ 136 | "libc", 137 | "rand 0.9.0", 138 | "tokio", 139 | "turmoil", 140 | ] 141 | 142 | [[package]] 143 | name = "memchr" 144 | version = "2.7.4" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 147 | 148 | [[package]] 149 | name = "miniz_oxide" 150 | version = "0.8.5" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 153 | dependencies = [ 154 | "adler2", 155 | ] 156 | 157 | [[package]] 158 | name = "mio" 159 | version = "1.0.3" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 162 | dependencies = [ 163 | "libc", 164 | "wasi 0.11.0+wasi-snapshot-preview1", 165 | "windows-sys", 166 | ] 167 | 168 | [[package]] 169 | name = "num-traits" 170 | version = "0.2.19" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 173 | dependencies = [ 174 | "autocfg", 175 | "libm", 176 | ] 177 | 178 | [[package]] 179 | name = "object" 180 | version = "0.36.7" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 183 | dependencies = [ 184 | "memchr", 185 | ] 186 | 187 | [[package]] 188 | name = "once_cell" 189 | version = "1.21.3" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 192 | 193 | [[package]] 194 | name = "parking_lot" 195 | version = "0.12.3" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 198 | dependencies = [ 199 | "lock_api", 200 | "parking_lot_core", 201 | ] 202 | 203 | [[package]] 204 | name = "parking_lot_core" 205 | version = "0.9.10" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 208 | dependencies = [ 209 | "cfg-if", 210 | "libc", 211 | "redox_syscall", 212 | "smallvec", 213 | "windows-targets", 214 | ] 215 | 216 | [[package]] 217 | name = "pin-project-lite" 218 | version = "0.2.16" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 221 | 222 | [[package]] 223 | name = "ppv-lite86" 224 | version = "0.2.21" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 227 | dependencies = [ 228 | "zerocopy", 229 | ] 230 | 231 | [[package]] 232 | name = "proc-macro2" 233 | version = "1.0.94" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 236 | dependencies = [ 237 | "unicode-ident", 238 | ] 239 | 240 | [[package]] 241 | name = "quote" 242 | version = "1.0.40" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 245 | dependencies = [ 246 | "proc-macro2", 247 | ] 248 | 249 | [[package]] 250 | name = "r-efi" 251 | version = "5.2.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 254 | 255 | [[package]] 256 | name = "rand" 257 | version = "0.8.5" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 260 | dependencies = [ 261 | "libc", 262 | "rand_chacha 0.3.1", 263 | "rand_core 0.6.4", 264 | ] 265 | 266 | [[package]] 267 | name = "rand" 268 | version = "0.9.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" 271 | dependencies = [ 272 | "rand_chacha 0.9.0", 273 | "rand_core 0.9.3", 274 | "zerocopy", 275 | ] 276 | 277 | [[package]] 278 | name = "rand_chacha" 279 | version = "0.3.1" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 282 | dependencies = [ 283 | "ppv-lite86", 284 | "rand_core 0.6.4", 285 | ] 286 | 287 | [[package]] 288 | name = "rand_chacha" 289 | version = "0.9.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 292 | dependencies = [ 293 | "ppv-lite86", 294 | "rand_core 0.9.3", 295 | ] 296 | 297 | [[package]] 298 | name = "rand_core" 299 | version = "0.6.4" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 302 | dependencies = [ 303 | "getrandom 0.2.15", 304 | ] 305 | 306 | [[package]] 307 | name = "rand_core" 308 | version = "0.9.3" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 311 | dependencies = [ 312 | "getrandom 0.3.2", 313 | ] 314 | 315 | [[package]] 316 | name = "rand_distr" 317 | version = "0.4.3" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" 320 | dependencies = [ 321 | "num-traits", 322 | "rand 0.8.5", 323 | ] 324 | 325 | [[package]] 326 | name = "redox_syscall" 327 | version = "0.5.10" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" 330 | dependencies = [ 331 | "bitflags", 332 | ] 333 | 334 | [[package]] 335 | name = "rustc-demangle" 336 | version = "0.1.24" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 339 | 340 | [[package]] 341 | name = "scoped-tls" 342 | version = "1.0.1" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 345 | 346 | [[package]] 347 | name = "scopeguard" 348 | version = "1.2.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 351 | 352 | [[package]] 353 | name = "signal-hook-registry" 354 | version = "1.4.2" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 357 | dependencies = [ 358 | "libc", 359 | ] 360 | 361 | [[package]] 362 | name = "smallvec" 363 | version = "1.14.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 366 | 367 | [[package]] 368 | name = "socket2" 369 | version = "0.5.9" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 372 | dependencies = [ 373 | "libc", 374 | "windows-sys", 375 | ] 376 | 377 | [[package]] 378 | name = "syn" 379 | version = "2.0.100" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 382 | dependencies = [ 383 | "proc-macro2", 384 | "quote", 385 | "unicode-ident", 386 | ] 387 | 388 | [[package]] 389 | name = "tokio" 390 | version = "1.44.1" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" 393 | dependencies = [ 394 | "backtrace", 395 | "bytes", 396 | "libc", 397 | "mio", 398 | "parking_lot", 399 | "pin-project-lite", 400 | "signal-hook-registry", 401 | "socket2", 402 | "tokio-macros", 403 | "windows-sys", 404 | ] 405 | 406 | [[package]] 407 | name = "tokio-macros" 408 | version = "2.5.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 411 | dependencies = [ 412 | "proc-macro2", 413 | "quote", 414 | "syn", 415 | ] 416 | 417 | [[package]] 418 | name = "tracing" 419 | version = "0.1.41" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 422 | dependencies = [ 423 | "pin-project-lite", 424 | "tracing-attributes", 425 | "tracing-core", 426 | ] 427 | 428 | [[package]] 429 | name = "tracing-attributes" 430 | version = "0.1.28" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 433 | dependencies = [ 434 | "proc-macro2", 435 | "quote", 436 | "syn", 437 | ] 438 | 439 | [[package]] 440 | name = "tracing-core" 441 | version = "0.1.33" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 444 | dependencies = [ 445 | "once_cell", 446 | ] 447 | 448 | [[package]] 449 | name = "turmoil" 450 | version = "0.6.6" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "1f0d6c134ef37268c94d50fb74252af1c34c5c88389e2c1af85654da944ceb52" 453 | dependencies = [ 454 | "bytes", 455 | "indexmap", 456 | "rand 0.8.5", 457 | "rand_distr", 458 | "scoped-tls", 459 | "tokio", 460 | "tracing", 461 | ] 462 | 463 | [[package]] 464 | name = "unicode-ident" 465 | version = "1.0.18" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 468 | 469 | [[package]] 470 | name = "wasi" 471 | version = "0.11.0+wasi-snapshot-preview1" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 474 | 475 | [[package]] 476 | name = "wasi" 477 | version = "0.14.2+wasi-0.2.4" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 480 | dependencies = [ 481 | "wit-bindgen-rt", 482 | ] 483 | 484 | [[package]] 485 | name = "windows-sys" 486 | version = "0.52.0" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 489 | dependencies = [ 490 | "windows-targets", 491 | ] 492 | 493 | [[package]] 494 | name = "windows-targets" 495 | version = "0.52.6" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 498 | dependencies = [ 499 | "windows_aarch64_gnullvm", 500 | "windows_aarch64_msvc", 501 | "windows_i686_gnu", 502 | "windows_i686_gnullvm", 503 | "windows_i686_msvc", 504 | "windows_x86_64_gnu", 505 | "windows_x86_64_gnullvm", 506 | "windows_x86_64_msvc", 507 | ] 508 | 509 | [[package]] 510 | name = "windows_aarch64_gnullvm" 511 | version = "0.52.6" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 514 | 515 | [[package]] 516 | name = "windows_aarch64_msvc" 517 | version = "0.52.6" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 520 | 521 | [[package]] 522 | name = "windows_i686_gnu" 523 | version = "0.52.6" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 526 | 527 | [[package]] 528 | name = "windows_i686_gnullvm" 529 | version = "0.52.6" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 532 | 533 | [[package]] 534 | name = "windows_i686_msvc" 535 | version = "0.52.6" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 538 | 539 | [[package]] 540 | name = "windows_x86_64_gnu" 541 | version = "0.52.6" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 544 | 545 | [[package]] 546 | name = "windows_x86_64_gnullvm" 547 | version = "0.52.6" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 550 | 551 | [[package]] 552 | name = "windows_x86_64_msvc" 553 | version = "0.52.6" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 556 | 557 | [[package]] 558 | name = "wit-bindgen-rt" 559 | version = "0.39.0" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 562 | dependencies = [ 563 | "bitflags", 564 | ] 565 | 566 | [[package]] 567 | name = "zerocopy" 568 | version = "0.8.24" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" 571 | dependencies = [ 572 | "zerocopy-derive", 573 | ] 574 | 575 | [[package]] 576 | name = "zerocopy-derive" 577 | version = "0.8.24" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" 580 | dependencies = [ 581 | "proc-macro2", 582 | "quote", 583 | "syn", 584 | ] 585 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mad-turmoil" 3 | version = "0.1.0" 4 | edition = "2024" 5 | license = "Apache-2.0" 6 | description = "madsim-inspired determinism for turmoil-based simulation tests" 7 | 8 | [dependencies] 9 | libc = "0.2" 10 | rand = "0.9" 11 | tokio = "1" 12 | turmoil = "0.6" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mad-turmoil 2 | 3 | [madsim](https://github.com/madsim-rs/madsim)-inspired determinism for [turmoil](https://github.com/tokio-rs/turmoil)-based simulation tests 4 | 5 | See [blog post](https://s2.dev/blog/dst) 6 | 7 | ## Setup 8 | 9 | Make sure you are only depending on this crate for simulation binaries! 10 | 11 | ```rust 12 | fn main() -> eyre::Result<()> { 13 | let rng_seed = std::env::var("DST_SEED")?.parse()?; 14 | 15 | // Taming randomness... 16 | 17 | let mut rng = StdRng::seed_from_u64(rng_seed); 18 | mad_turmoil::rand::set_rng(rng.clone()); 19 | assert_eq!(rng.next_u64(), mad_turmoil::rand::get_rng().next_u64()); 20 | 21 | // Additionally, if you are using fastrand (possibly transitively!) 22 | fastrand::seed(rng_seed); 23 | 24 | // Taming time... 25 | 26 | let _tokio_time_guard = mad_turmoil::time::SimClocksGuard::init(); 27 | 28 | // Go forth and create turmoil... 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod rand; 2 | pub mod time; 3 | -------------------------------------------------------------------------------- /src/rand.rs: -------------------------------------------------------------------------------- 1 | //! Derived from 2 | 3 | use std::{ 4 | borrow::BorrowMut, 5 | fs::File, 6 | io::{self, Read}, 7 | sync::{Arc, Mutex, MutexGuard, OnceLock}, 8 | }; 9 | 10 | use rand::{RngCore, rngs::StdRng}; 11 | 12 | static RNG_CELL: OnceLock>> = OnceLock::new(); 13 | 14 | pub fn set_rng(rng: StdRng) { 15 | RNG_CELL 16 | .set(Arc::new(Mutex::new(rng))) 17 | .expect("Single init") 18 | } 19 | 20 | pub fn try_rng() -> Option> { 21 | RNG_CELL.get().map(|m| m.lock().expect("RNG lock")) 22 | } 23 | 24 | pub fn get_rng() -> MutexGuard<'static, StdRng> { 25 | try_rng().expect("RNG init") 26 | } 27 | 28 | fn fill_with_dev_urandom(dest: &mut [u8]) -> io::Result<()> { 29 | let mut file = File::open("/dev/urandom")?; 30 | file.read_exact(dest)?; 31 | Ok(()) 32 | } 33 | 34 | #[unsafe(no_mangle)] 35 | #[inline(never)] 36 | unsafe extern "C" fn getrandom(buf: *mut u8, buflen: usize, _flags: u32) -> isize { 37 | // 38 | if !buf.is_null() && buflen > 0 { 39 | let dest = unsafe { std::slice::from_raw_parts_mut(buf, buflen) }; 40 | match try_rng() { 41 | Some(mut rng) => { 42 | rng.borrow_mut().fill_bytes(dest); 43 | } 44 | None => { 45 | // for call sites (e.g. test runner, getting random seed if not set in env var) 46 | // before the test has set the RNG. 47 | if fill_with_dev_urandom(dest).is_err() { 48 | return -1; 49 | } 50 | } 51 | } 52 | buflen as isize 53 | } else { 54 | -1 55 | } 56 | } 57 | 58 | #[unsafe(no_mangle)] 59 | #[cfg(target_os = "macos")] 60 | #[inline(never)] 61 | unsafe extern "C" fn CCRandomGenerateBytes(buf: *mut u8, buflen: usize) -> i32 { 62 | // For Mac OS 63 | // - 64 | // - 65 | if unsafe { getrandom(buf, buflen, 0) } as i32 != -1 { 66 | 0 67 | } else { 68 | -1 69 | } 70 | } 71 | 72 | #[unsafe(no_mangle)] 73 | #[inline(never)] 74 | unsafe extern "C" fn getentropy(buf: *mut u8, buflen: usize) -> i32 { 75 | // 76 | if buflen > 256 { 77 | return -1; 78 | } 79 | match unsafe { getrandom(buf, buflen, 0) } { 80 | -1 => -1, 81 | _ => 0, 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | //! Derived from 2 | 3 | use std::{ 4 | sync::atomic::{AtomicBool, Ordering}, 5 | time::Duration, 6 | }; 7 | 8 | static USE_SIM_CLOCKS: AtomicBool = AtomicBool::new(false); 9 | 10 | /// Scopes checking for Tokio runtime. 11 | /// 12 | /// Otherwise `tokio::runtime::Handle::try_current()` as process is tearing down can result in a 13 | /// SIGTRAP: "global allocator may not use TLS" 14 | pub struct SimClocksGuard(()); 15 | 16 | impl SimClocksGuard { 17 | pub fn init() -> Self { 18 | USE_SIM_CLOCKS.store(true, Ordering::Release); 19 | Self(()) 20 | } 21 | } 22 | 23 | impl Drop for SimClocksGuard { 24 | fn drop(&mut self) { 25 | USE_SIM_CLOCKS.store(false, Ordering::Release); 26 | } 27 | } 28 | 29 | /// Based on innards of [std::time::Instant], which [tokio::time::Instant] wraps. 30 | #[derive(Debug, Copy, Clone)] 31 | struct StdTimespec { 32 | tv_sec: u64, 33 | tv_nsec: u32, 34 | } 35 | 36 | impl From for libc::timespec { 37 | fn from(value: StdTimespec) -> Self { 38 | Self { 39 | tv_sec: value.tv_sec as i64, 40 | tv_nsec: value.tv_nsec as libc::c_long, 41 | } 42 | } 43 | } 44 | 45 | fn turmoil_elapsed() -> libc::timespec { 46 | let elapsed = turmoil::sim_elapsed().unwrap_or(Duration::ZERO); 47 | libc::timespec { 48 | tv_sec: elapsed.as_secs() as i64, 49 | tv_nsec: elapsed.subsec_nanos() as libc::c_long, 50 | } 51 | } 52 | 53 | unsafe fn tokio_instant_now() -> libc::timespec { 54 | let instant = tokio::time::Instant::now(); 55 | let ts: StdTimespec = unsafe { std::mem::transmute(instant) }; 56 | ts.into() 57 | } 58 | 59 | #[unsafe(no_mangle)] 60 | #[inline(never)] 61 | unsafe extern "C" fn clock_gettime( 62 | clockid: libc::clockid_t, 63 | tp: *mut libc::timespec, 64 | ) -> libc::c_int { 65 | // 66 | if USE_SIM_CLOCKS.load(Ordering::Acquire) && tokio::runtime::Handle::try_current().is_ok() { 67 | let timespec = match clockid { 68 | libc::CLOCK_REALTIME => Some(turmoil_elapsed()), 69 | #[cfg(target_os = "linux")] 70 | libc::CLOCK_REALTIME_COARSE => Some(turmoil_elapsed()), 71 | libc::CLOCK_MONOTONIC | libc::CLOCK_MONOTONIC_RAW => { 72 | Some(unsafe { tokio_instant_now() }) 73 | } 74 | #[cfg(target_os = "linux")] 75 | libc::CLOCK_MONOTONIC_COARSE | libc::CLOCK_BOOTTIME => { 76 | Some(unsafe { tokio_instant_now() }) 77 | } 78 | #[cfg(target_os = "macos")] 79 | libc::CLOCK_UPTIME_RAW => Some(unsafe { tokio_instant_now() }), 80 | _ => { 81 | eprintln!( 82 | "Unsupported clock, real implementation will be used -- clock_gettime({clockid:?})" 83 | ); 84 | None 85 | } 86 | }; 87 | if let Some(timespec) = timespec { 88 | unsafe { tp.write(timespec) }; 89 | return 0; 90 | } 91 | } 92 | 93 | // In the course of a DST, a few notable callers of clock_gettime still need handling here: 94 | // - mimalloc 95 | // - tracing 96 | // - fastrand uses the current time as an input to its hasher, even if setting a seed 97 | // While the latter two should be deterministic in our use (when and how many times invoked), 98 | // mimalloc might not be. If it were deterministic, we could use some atomic counter as a 99 | // spoofed clock, but for now, simply returning the Unix epoch seems fine. 100 | unsafe { 101 | tp.write(libc::timespec { 102 | tv_sec: 0, 103 | tv_nsec: 0, 104 | }); 105 | } 106 | 0 107 | } 108 | --------------------------------------------------------------------------------