├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── benches └── run.rs ├── compare.py └── src ├── executor.rs ├── fakeio.rs ├── future.rs ├── lib.rs ├── list.rs ├── main.rs ├── run.rs └── waker.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "atty" 7 | version = "0.2.14" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 10 | dependencies = [ 11 | "hermit-abi", 12 | "libc", 13 | "winapi", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.1.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 21 | 22 | [[package]] 23 | name = "bitflags" 24 | version = "1.3.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 27 | 28 | [[package]] 29 | name = "bstr" 30 | version = "0.2.17" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 33 | dependencies = [ 34 | "lazy_static", 35 | "memchr", 36 | "regex-automata", 37 | "serde", 38 | ] 39 | 40 | [[package]] 41 | name = "bumpalo" 42 | version = "3.11.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" 45 | 46 | [[package]] 47 | name = "cast" 48 | version = "0.3.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 51 | 52 | [[package]] 53 | name = "cfg-if" 54 | version = "1.0.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 57 | 58 | [[package]] 59 | name = "clap" 60 | version = "2.34.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 63 | dependencies = [ 64 | "bitflags", 65 | "textwrap", 66 | "unicode-width", 67 | ] 68 | 69 | [[package]] 70 | name = "criterion" 71 | version = "0.3.6" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" 74 | dependencies = [ 75 | "atty", 76 | "cast", 77 | "clap", 78 | "criterion-plot", 79 | "csv", 80 | "itertools", 81 | "lazy_static", 82 | "num-traits", 83 | "oorandom", 84 | "plotters", 85 | "rayon", 86 | "regex", 87 | "serde", 88 | "serde_cbor", 89 | "serde_derive", 90 | "serde_json", 91 | "tinytemplate", 92 | "walkdir", 93 | ] 94 | 95 | [[package]] 96 | name = "criterion-plot" 97 | version = "0.4.5" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" 100 | dependencies = [ 101 | "cast", 102 | "itertools", 103 | ] 104 | 105 | [[package]] 106 | name = "crossbeam-channel" 107 | version = "0.5.6" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" 110 | dependencies = [ 111 | "cfg-if", 112 | "crossbeam-utils", 113 | ] 114 | 115 | [[package]] 116 | name = "crossbeam-deque" 117 | version = "0.8.2" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" 120 | dependencies = [ 121 | "cfg-if", 122 | "crossbeam-epoch", 123 | "crossbeam-utils", 124 | ] 125 | 126 | [[package]] 127 | name = "crossbeam-epoch" 128 | version = "0.9.10" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" 131 | dependencies = [ 132 | "autocfg", 133 | "cfg-if", 134 | "crossbeam-utils", 135 | "memoffset", 136 | "once_cell", 137 | "scopeguard", 138 | ] 139 | 140 | [[package]] 141 | name = "crossbeam-utils" 142 | version = "0.8.11" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" 145 | dependencies = [ 146 | "cfg-if", 147 | "once_cell", 148 | ] 149 | 150 | [[package]] 151 | name = "csv" 152 | version = "1.1.6" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 155 | dependencies = [ 156 | "bstr", 157 | "csv-core", 158 | "itoa 0.4.8", 159 | "ryu", 160 | "serde", 161 | ] 162 | 163 | [[package]] 164 | name = "csv-core" 165 | version = "0.1.10" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 168 | dependencies = [ 169 | "memchr", 170 | ] 171 | 172 | [[package]] 173 | name = "either" 174 | version = "1.8.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 177 | 178 | [[package]] 179 | name = "half" 180 | version = "1.8.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 183 | 184 | [[package]] 185 | name = "hermit-abi" 186 | version = "0.1.19" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 189 | dependencies = [ 190 | "libc", 191 | ] 192 | 193 | [[package]] 194 | name = "itertools" 195 | version = "0.10.3" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 198 | dependencies = [ 199 | "either", 200 | ] 201 | 202 | [[package]] 203 | name = "itoa" 204 | version = "0.4.8" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 207 | 208 | [[package]] 209 | name = "itoa" 210 | version = "1.0.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 213 | 214 | [[package]] 215 | name = "js-sys" 216 | version = "0.3.59" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" 219 | dependencies = [ 220 | "wasm-bindgen", 221 | ] 222 | 223 | [[package]] 224 | name = "lazy_static" 225 | version = "1.4.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 228 | 229 | [[package]] 230 | name = "libc" 231 | version = "0.2.132" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 234 | 235 | [[package]] 236 | name = "log" 237 | version = "0.4.17" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 240 | dependencies = [ 241 | "cfg-if", 242 | ] 243 | 244 | [[package]] 245 | name = "memchr" 246 | version = "2.5.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 249 | 250 | [[package]] 251 | name = "memoffset" 252 | version = "0.6.5" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 255 | dependencies = [ 256 | "autocfg", 257 | ] 258 | 259 | [[package]] 260 | name = "num-traits" 261 | version = "0.2.15" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 264 | dependencies = [ 265 | "autocfg", 266 | ] 267 | 268 | [[package]] 269 | name = "num_cpus" 270 | version = "1.13.1" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 273 | dependencies = [ 274 | "hermit-abi", 275 | "libc", 276 | ] 277 | 278 | [[package]] 279 | name = "once_cell" 280 | version = "1.13.1" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" 283 | 284 | [[package]] 285 | name = "oorandom" 286 | version = "11.1.3" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 289 | 290 | [[package]] 291 | name = "plotters" 292 | version = "0.3.3" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "716b4eeb6c4a1d3ecc956f75b43ec2e8e8ba80026413e70a3f41fd3313d3492b" 295 | dependencies = [ 296 | "num-traits", 297 | "plotters-backend", 298 | "plotters-svg", 299 | "wasm-bindgen", 300 | "web-sys", 301 | ] 302 | 303 | [[package]] 304 | name = "plotters-backend" 305 | version = "0.3.4" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" 308 | 309 | [[package]] 310 | name = "plotters-svg" 311 | version = "0.3.3" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" 314 | dependencies = [ 315 | "plotters-backend", 316 | ] 317 | 318 | [[package]] 319 | name = "proc-macro2" 320 | version = "1.0.43" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 323 | dependencies = [ 324 | "unicode-ident", 325 | ] 326 | 327 | [[package]] 328 | name = "quote" 329 | version = "1.0.21" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 332 | dependencies = [ 333 | "proc-macro2", 334 | ] 335 | 336 | [[package]] 337 | name = "rayon" 338 | version = "1.5.3" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" 341 | dependencies = [ 342 | "autocfg", 343 | "crossbeam-deque", 344 | "either", 345 | "rayon-core", 346 | ] 347 | 348 | [[package]] 349 | name = "rayon-core" 350 | version = "1.9.3" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" 353 | dependencies = [ 354 | "crossbeam-channel", 355 | "crossbeam-deque", 356 | "crossbeam-utils", 357 | "num_cpus", 358 | ] 359 | 360 | [[package]] 361 | name = "regex" 362 | version = "1.6.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 365 | dependencies = [ 366 | "regex-syntax", 367 | ] 368 | 369 | [[package]] 370 | name = "regex-automata" 371 | version = "0.1.10" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 374 | 375 | [[package]] 376 | name = "regex-syntax" 377 | version = "0.6.27" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 380 | 381 | [[package]] 382 | name = "rust-async-bench" 383 | version = "0.1.0" 384 | dependencies = [ 385 | "criterion", 386 | "libc", 387 | "slab", 388 | ] 389 | 390 | [[package]] 391 | name = "ryu" 392 | version = "1.0.11" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 395 | 396 | [[package]] 397 | name = "same-file" 398 | version = "1.0.6" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 401 | dependencies = [ 402 | "winapi-util", 403 | ] 404 | 405 | [[package]] 406 | name = "scopeguard" 407 | version = "1.1.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 410 | 411 | [[package]] 412 | name = "serde" 413 | version = "1.0.144" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" 416 | 417 | [[package]] 418 | name = "serde_cbor" 419 | version = "0.11.2" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" 422 | dependencies = [ 423 | "half", 424 | "serde", 425 | ] 426 | 427 | [[package]] 428 | name = "serde_derive" 429 | version = "1.0.144" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" 432 | dependencies = [ 433 | "proc-macro2", 434 | "quote", 435 | "syn", 436 | ] 437 | 438 | [[package]] 439 | name = "serde_json" 440 | version = "1.0.85" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" 443 | dependencies = [ 444 | "itoa 1.0.3", 445 | "ryu", 446 | "serde", 447 | ] 448 | 449 | [[package]] 450 | name = "slab" 451 | version = "0.4.7" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 454 | dependencies = [ 455 | "autocfg", 456 | ] 457 | 458 | [[package]] 459 | name = "syn" 460 | version = "1.0.99" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 463 | dependencies = [ 464 | "proc-macro2", 465 | "quote", 466 | "unicode-ident", 467 | ] 468 | 469 | [[package]] 470 | name = "textwrap" 471 | version = "0.11.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 474 | dependencies = [ 475 | "unicode-width", 476 | ] 477 | 478 | [[package]] 479 | name = "tinytemplate" 480 | version = "1.2.1" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 483 | dependencies = [ 484 | "serde", 485 | "serde_json", 486 | ] 487 | 488 | [[package]] 489 | name = "unicode-ident" 490 | version = "1.0.3" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 493 | 494 | [[package]] 495 | name = "unicode-width" 496 | version = "0.1.9" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 499 | 500 | [[package]] 501 | name = "walkdir" 502 | version = "2.3.2" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 505 | dependencies = [ 506 | "same-file", 507 | "winapi", 508 | "winapi-util", 509 | ] 510 | 511 | [[package]] 512 | name = "wasm-bindgen" 513 | version = "0.2.82" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" 516 | dependencies = [ 517 | "cfg-if", 518 | "wasm-bindgen-macro", 519 | ] 520 | 521 | [[package]] 522 | name = "wasm-bindgen-backend" 523 | version = "0.2.82" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" 526 | dependencies = [ 527 | "bumpalo", 528 | "log", 529 | "once_cell", 530 | "proc-macro2", 531 | "quote", 532 | "syn", 533 | "wasm-bindgen-shared", 534 | ] 535 | 536 | [[package]] 537 | name = "wasm-bindgen-macro" 538 | version = "0.2.82" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" 541 | dependencies = [ 542 | "quote", 543 | "wasm-bindgen-macro-support", 544 | ] 545 | 546 | [[package]] 547 | name = "wasm-bindgen-macro-support" 548 | version = "0.2.82" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" 551 | dependencies = [ 552 | "proc-macro2", 553 | "quote", 554 | "syn", 555 | "wasm-bindgen-backend", 556 | "wasm-bindgen-shared", 557 | ] 558 | 559 | [[package]] 560 | name = "wasm-bindgen-shared" 561 | version = "0.2.82" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" 564 | 565 | [[package]] 566 | name = "web-sys" 567 | version = "0.3.59" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" 570 | dependencies = [ 571 | "js-sys", 572 | "wasm-bindgen", 573 | ] 574 | 575 | [[package]] 576 | name = "winapi" 577 | version = "0.3.9" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 580 | dependencies = [ 581 | "winapi-i686-pc-windows-gnu", 582 | "winapi-x86_64-pc-windows-gnu", 583 | ] 584 | 585 | [[package]] 586 | name = "winapi-i686-pc-windows-gnu" 587 | version = "0.4.0" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 590 | 591 | [[package]] 592 | name = "winapi-util" 593 | version = "0.1.5" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 596 | dependencies = [ 597 | "winapi", 598 | ] 599 | 600 | [[package]] 601 | name = "winapi-x86_64-pc-windows-gnu" 602 | version = "0.4.0" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 605 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-async-bench" 3 | version = "0.1.0" 4 | authors = ["Justin Karneges "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libc = "0.2" 11 | slab = "0.4" 12 | 13 | [dev-dependencies] 14 | criterion = "0.3" 15 | 16 | [[bench]] 17 | name = "run" 18 | harness = false 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Async Benchmark 2 | 3 | This project compares the performance of a manually written event loop vs async/await, in Rust. 4 | 5 | To be clear, this is not a comparison of thread pools vs coroutines. It's also not a comparison of async runtimes. It's a comparison of single-threaded manually written event loop code vs single-threaded async/await code, to determine the minimum overhead of adopting async in a Rust application. 6 | 7 | ## Goals 8 | 9 | * Determine to what extent async Rust may require conventionally costly operations, such as heap allocs or syscalls. 10 | * Compare the CPU usage of a manually written event loop to a minimal async executor. 11 | * Measure the CPU costs of some fundamental async implementation choices, such as boxed futures and atomic wakers. 12 | * Answer the question: is async Rust a "zero cost abstraction"? 13 | 14 | ## How it works 15 | 16 | In order to simulate a somewhat realistic application style, the benchmarks implement a fake network server that responds to requests. The fake server accepts "connections", which are bidirectional streams of bytes. For each connection, it reads a line of text as a request, then it writes a line of text as a response. Multiple variations of this fake server are implemented. One variation uses a manually written event loop and the rest use async. 17 | 18 | Each async benchmark uses one of the following executors: 19 | 20 | * `ArgExecutor`: The most minimal executor. It supports exactly one `Future` type, passing an argument to the future when spawning. Multiple kinds of task logic can be supported by having the one future type call out to different async functions depending on the argument. Futures are not boxed (they are stored by value in a `Vec`), and waker data is not separately allocated. 21 | 22 | * `BoxExecutor`: Supports arbitrary `Future` types by boxing them. Otherwise, it works similarly to `ArgExecutor` in that waker data is not separately allocated. 23 | 24 | * `BoxRcExecutor`: A more conventional executor, that supports arbitrary `Future` types by boxing them, and allocates ref-counted wakers. It supports both Rc-based and Arc-based wakers. 25 | 26 | The benchmarks are: 27 | 28 | * `manual`: A manually written event loop, to use as a basis for comparison. 29 | * `nonbox`: Uses `ArgExecutor`, the most minimal executor. It uses no heap allocs at runtime, but all futures take up the same amount of space. 30 | * `callerbox`: Like `nonbox`, but the caller boxes the futures and uses the box as the one future type to execute. This works because the standard library implements `Future` for `Pin>`. It boxes the same future type used by the `nonbox` benchmark, so all futures still take up the same amount of space. 31 | * `large+nonbox`: Like `nonbox`, but a larger future is used. 32 | * `box`: Uses `BoxExecutor`. This means heap allocs are used at runtime, but different futures can take up different amounts of space. 33 | * `box+callerbox`: Like `box`, but the caller boxes the futures instead of the executor doing the boxing. 34 | * `large+box`: Like `box`, but a larger future is used. 35 | * `box+rc`: Uses `BoxRcExecutor` with an unsafe Rc-based waker. Using such a waker from another thread would cause undefined behavior. 36 | * `box+chkrc`: Uses `BoxRcExecutor` with a "safe" Rc-based waker. The waker has thread-affinity and panics if it is used from another thread. 37 | * `box+arc`: Uses `BoxRcExecutor` with an Arc-based waker (`std::task::Wake`). 38 | 39 | Additionally, there are variations of these benchmarks that make I/O syscalls, suffixed with `+syscalls`. 40 | 41 | Each benchmark performs 256 request/response transactions. 42 | 43 | ### I/O counts 44 | 45 | To count I/O calls for the manual event loop and minimal async executor, run `cargo run`: 46 | 47 | ``` 48 | $ cargo run 49 | Finished dev [unoptimized + debuginfo] target(s) in 0.03s 50 | Running `target/debug/rust-async-bench` 51 | manual: register=257 unregister=257 poll=258 accept=512 read=512 write=512 52 | async: register=257 unregister=257 poll=258 accept=512 read=512 write=512 53 | ``` 54 | 55 | (Note: since this only counts operations and is not a speed test, the build mode doesn't matter.) 56 | 57 | Running `cargo test` will verify these expected counts for all implementation variations. 58 | 59 | ## Benchmarks 60 | 61 | To measure the speed of the manual event loop vs. the various async implementations/configurations, run `cargo bench`. 62 | 63 | Some results running on Linux: 64 | 65 | ``` 66 | manual time: [26.262 µs 26.270 µs 26.279 µs] 67 | nonbox time: [88.471 µs 88.497 µs 88.529 µs] 68 | callerbox time: [91.892 µs 91.907 µs 91.926 µs] 69 | large+nonbox time: [189.18 µs 189.24 µs 189.31 µs] 70 | box time: [96.430 µs 96.447 µs 96.469 µs] 71 | box+callerbox time: [98.313 µs 98.329 µs 98.347 µs] 72 | large+box time: [212.57 µs 212.72 µs 212.92 µs] 73 | box+rc time: [98.380 µs 98.399 µs 98.422 µs] 74 | box+chkrc time: [114.84 µs 114.86 µs 114.88 µs] 75 | box+arc time: [103.19 µs 103.21 µs 103.23 µs] 76 | manual+syscalls time: [988.31 µs 988.57 µs 988.83 µs] 77 | nonbox+syscalls time: [1.0816 ms 1.0821 ms 1.0825 ms] 78 | box+syscalls time: [1.0965 ms 1.0967 ms 1.0970 ms] 79 | box+rc+syscalls time: [1.0981 ms 1.0984 ms 1.0987 ms] 80 | box+chkrc+syscalls time: [1.1174 ms 1.1179 ms 1.1185 ms] 81 | box+arc+syscalls time: [1.1086 ms 1.1089 ms 1.1092 ms] 82 | ``` 83 | 84 | ## Analysis 85 | 86 | ### Summary 87 | 88 | * Async may require 9.4% more CPU than hand written code, but likely much less in real applications. 89 | * Boxing futures has relatively insignificant cost and is worth it for flexibility and ergonomics. 90 | * Arc-based wakers are the best we've got. 91 | * Async possibly qualifies as a zero cost abstraction if you've already bought into the `Future` trait. In practice it may have a (very low) cost compared to code that wouldn't have used `Future` or similar. 92 | 93 | ### Costs 94 | 95 | All of the async benchmarks are slower than the `manual` benchmark, so async Rust has a measurable cost. 96 | 97 | That said, async Rust does not require the use of any conventionally costly operations, such as heap allocs, atomics, mutexes, or thread local storage, nor does it require introducing extra syscalls, buffer copies, or algorithms with worse big O complexity, compared to what would be required by a manually written poll loop. Not even a hashmap is required. At minimum, the required costs involve shuffling around bools, ints, and references, and using collections with fast O(1) operations such as arrays and linked lists. This is proven by `ArgExecutor`. 98 | 99 | Boxing tasks has an additional cost (see `nonbox` vs. `box` and `large+nonbox` vs. `large+box`). This is not really surprising. 100 | 101 | Embedding wakers in existing data structures instead of separately allocating them doesn't make a meaningful difference (see `box` vs. `box+rc`). 102 | 103 | Arc-based wakers are slower than unsafe Rc-based wakers (see `box+rc` vs. `box+arc`). However, they are faster than checked Rc-based wakers (see `box+chkrc` vs. `box+arc`). 104 | 105 | ### Relative costs 106 | 107 | In a real application, task accounting should normally be a very small part of overall compute time. The benchmarks with syscalls help highlight this. These benchmarks make a non-blocking call to `libc::read` whenever there is an I/O operation. The read is against a pipe that never has data in it, and so the call always returns an error. Adding in these syscalls significantly reduces the time differences between the benchmarks. For example, `manual` is 70% faster than `nonbox`, but `manual+syscalls` is only 8.6% faster than `nonbox+syscalls` (or reversed, `nonbox+syscalls` is 9.4% slower). 108 | 109 | These no-op syscalls only scratch the surface in terms of what a real application might do. In a real application, I/O syscalls will likely do meaningful things (such as read non-zero amounts of data) and thus cost more. Real applications will also perform real application logic. The benchmarks test 256 requests. If an application were to spend even 10 microseconds per request doing meaningful work, the manual event loop would only be 2.5% faster. 110 | 111 | Another way of looking at it: the difference between `manual` and `nonbox` is 62.2us. Divided by 256, that's an overhead of around 243 nanoseconds for async execution per request. In a real app, that's practically free. 112 | 113 | ### Boxing futures 114 | 115 | Boxing futures has a small cost (`nonbox+syscalls` is 1.3% faster than `box+syscalls`). However, not boxing has drawbacks too: all futures take up the same amount of space, and spawning needs to be done indirectly to avoid circular references. Also, unless the space for all futures is allocated up front, allocations will need to be made at runtime anyway. 116 | 117 | Given the small relative cost of boxing, the cost is well worth it for the flexibility and ergonomics it brings. A high performance network server running on a typical OS should box its futures. 118 | 119 | The only time to avoid boxing might be in hard real-time applications, for example games with frame-rendering deadlines, or embedded systems without support for allocations. 120 | 121 | ### Rc vs Arc wakers 122 | 123 | In theory, Rc-based wakers have legitimate value, in that they are faster than Arc-based wakers (`box+rc+syscalls` is 0.9% faster than `box+arc+syscalls`) and can simply be dropped in where applicable (single-threaded executors). Unfortunately, it is currently not possible to use Rc-based wakers safely without sacrificing their performance gains. 124 | 125 | Using unsafe wakers is probably not worth it for their small relative gains. It seems like it would be a hard thing to audit. Waker construction could be marked `unsafe`, but waker proliferation and use would not be. 126 | 127 | Arc-based wakers are faster than safe Rc-based wakers, at least when there is no contention on the wakers between multiple threads. And if you're using single-threaded executors, then there should be no contention. This means Arc-based wakers are the safest, fastest choice for either single-threaded or multi-threaded executors. 128 | 129 | ### Zero cost? 130 | 131 | Is async Rust a "zero cost abstraction"? 132 | 133 | Let's start by quoting Bjarne Stroustrup's definition: "What you don't use, you don't pay for. And further: What you do use, you couldn't hand code any better." 134 | 135 | If you don't use async at all, then you don't pay anything for it. That much is true. However, the `manual` benchmark beats `nonbox`, suggesting that a non-blocking event loop can be hand-coded better than by using async. 136 | 137 | Async Rust has different goals than that manually written code though, such as encapsulation (hiding everything behind a poll() method) and reactor/executor decoupling (wakers). If you've already bought into using the `Future` trait, then async generators may very well be zero cost. But if your hand written code wouldn't normally have used `Future` or something like it, then adopting the whole of async will have a cost. Speaking for myself, I've never implemented `Future`-like encapsulation or decoupling in my own event loop code. Probably this is because the code was always application-specific and not part of a composable framework. 138 | 139 | The relative cost of async is low though, and it comes with the huge benefit of being able to write code that is much easier to reason about and to extend. Again speaking for myself, I'll admit I've made compromises in the control flows of my own event loop code, in order to increase maintainability and comprehension at the cost of correctness. Async makes it practical to implement complex control flow correctly, that is even easier to maintain and comprehend than without. 140 | 141 | ## Implementation details 142 | 143 | In general, the code in this project is written in a mostly-safe, mostly-idiomatic manner. Everything is designed to be performant, by selecting good algorithms and avoiding conventionally costly operations. It may be possible to make things faster by merging reactor and executor logic, or by not using wakers, or by writing more unsafe code, but I felt I drew a reasonable line. 144 | 145 | This project uses "fake" I/O objects that work in memory. The I/O primitives are `FakeListener`, `FakeStream`, and `Poll`, analogous to `TcpListener`, `TcpStream`, and Mio's `Poll`. There is no client side, and thus no client-side overhead when benchmarking. 146 | 147 | There are two kinds of tasks to perform: accept connections and process connections. The manual benchmark is implemented as a poll loop with all tasks intermingled. The async benchmarks are implemented using individual future instances for each task, that are then executed concurrently. 148 | 149 | All variations can be run with or without syscalls. When syscalls are enabled, `libc::read` is called on an empty pipe every time there would have been an I/O operation. 150 | 151 | It is relatively straightforward to write a single-threaded poll loop server that doesn't use heap allocations or synchronization primitives. Doing the same with async/await, and doing it without making extra syscalls, is a bit trickier. The following techniques are used: 152 | 153 | * I/O objects register/unregister with the poller when they are initialized/dropped as opposed to when I/O futures are used. They also keep track of their readiness state at all times. This helps reduce the overhead of the I/O futures. For example, if a stream is known to be not readable and `read()` is called on it, the returned future will immediately return `Pending` when polled, without performing a syscall. 154 | 155 | * `ArgExecutor` is generic over a single future type, `F`, and it stores the futures as non-boxed values. In order to support two kinds of tasks with only one future type, the accept handler and connection handler are implemented within the same async function, and the desired task is selected via argument. This way we can avoid heap allocations when spawning, at the cost of all the futures taking up the same amount of memory. 156 | 157 | * In `ArgExecutor`, the waker points at a struct that is known not to move for the lifetime of a future, and this struct contains references to the associated executor and task. This enables the waker to find the executor and the task it is responsible for, without having to do any heap allocations on its own or use thread local storage to find the executor. For this to be safe, a waker (or more specifically the underlying shared data of a waker, as a waker can be cloned) must not outlive the future it was created for. This is a pretty reasonable condition to adhere to, and the executor asserts it at runtime whenever a future completes. 158 | 159 | * Lifetime annotations everywhere! There is no `Rc` used in the `fakeio` module nor in `ArgExecutor`, and all shared objects are passed along as references. The reactor must live as long as the executor and the I/O objects, the executor must live as long as the top-level futures, the top-level futures must live as long as the I/O objects, and the I/O objects must live as long as the I/O futures. Somehow it all works. The Rust compiler is amazing. 160 | -------------------------------------------------------------------------------- /benches/run.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use rust_async_bench::run; 3 | 4 | fn criterion_benchmark(c: &mut Criterion) { 5 | run::run_manual(false, |r| { 6 | c.bench_function("manual", |b| b.iter(|| r())); 7 | }); 8 | 9 | run::run_nonbox(false, |r| { 10 | c.bench_function("nonbox", |b| b.iter(|| r())); 11 | }); 12 | 13 | run::run_callerbox(false, |r| { 14 | c.bench_function("callerbox", |b| b.iter(|| r())); 15 | }); 16 | 17 | run::run_large_nonbox(false, |r| { 18 | c.bench_function("large+nonbox", |b| b.iter(|| r())); 19 | }); 20 | 21 | run::run_box(false, |r| { 22 | c.bench_function("box", |b| b.iter(|| r())); 23 | }); 24 | 25 | run::run_box_callerbox(false, |r| { 26 | c.bench_function("box+callerbox", |b| b.iter(|| r())); 27 | }); 28 | 29 | run::run_large_box(false, |r| { 30 | c.bench_function("large+box", |b| b.iter(|| r())); 31 | }); 32 | 33 | run::run_box_rc(false, run::BoxRcMode::RcWaker, |r| { 34 | c.bench_function("box+rc", |b| b.iter(|| r())); 35 | }); 36 | 37 | run::run_box_rc(false, run::BoxRcMode::CheckedRcWaker, |r| { 38 | c.bench_function("box+chkrc", |b| b.iter(|| r())); 39 | }); 40 | 41 | run::run_box_rc(false, run::BoxRcMode::ArcWaker, |r| { 42 | c.bench_function("box+arc", |b| b.iter(|| r())); 43 | }); 44 | 45 | run::run_manual(true, |r| { 46 | c.bench_function("manual+syscalls", |b| b.iter(|| r())); 47 | }); 48 | 49 | run::run_nonbox(true, |r| { 50 | c.bench_function("nonbox+syscalls", |b| b.iter(|| r())); 51 | }); 52 | 53 | run::run_box(true, |r| { 54 | c.bench_function("box+syscalls", |b| b.iter(|| r())); 55 | }); 56 | 57 | run::run_box_rc(true, run::BoxRcMode::RcWaker, |r| { 58 | c.bench_function("box+rc+syscalls", |b| b.iter(|| r())); 59 | }); 60 | 61 | run::run_box_rc(true, run::BoxRcMode::CheckedRcWaker, |r| { 62 | c.bench_function("box+chkrc+syscalls", |b| b.iter(|| r())); 63 | }); 64 | 65 | run::run_box_rc(true, run::BoxRcMode::ArcWaker, |r| { 66 | c.bench_function("box+arc+syscalls", |b| b.iter(|| r())); 67 | }); 68 | } 69 | 70 | criterion_group!(benches, criterion_benchmark); 71 | criterion_main!(benches); 72 | -------------------------------------------------------------------------------- /compare.py: -------------------------------------------------------------------------------- 1 | # expects the output of multiple runs of "cargo bench" as stdin 2 | 3 | import sys 4 | 5 | names = [] 6 | tests = {} 7 | 8 | for line in sys.stdin: 9 | if 'time:' not in line: 10 | continue 11 | 12 | parts = line.split() 13 | 14 | name = parts[0] 15 | time = float(parts[4]) 16 | unit = parts[5] 17 | 18 | if unit == 'ns': 19 | nanos = int(time) 20 | elif unit == 'µs' or unit == 'us': 21 | nanos = int(time * 1000) 22 | elif unit == 'ms': 23 | nanos = int(time * 1000000) 24 | else: 25 | raise ValueError(f'unsupported unit: {unit}') 26 | 27 | best = None 28 | if name in tests: 29 | best = tests[name][0] 30 | 31 | if best is None or nanos < best: 32 | if name not in tests: 33 | names.append(name) 34 | 35 | tests[name] = (nanos, line.strip()) 36 | 37 | for name in names: 38 | print('{}'.format(tests[name][1])) 39 | 40 | print('') 41 | 42 | t = tests['manual+syscalls'] 43 | tests['manual+work'] = (t[0] + 2560000, t[1]) 44 | t = tests['nonbox+syscalls'] 45 | tests['nonbox+work'] = (t[0] + 2560000, t[1]) 46 | 47 | compare = [ 48 | ('manual', 'nonbox'), 49 | ('manual+syscalls', 'nonbox+syscalls'), 50 | ('nonbox+syscalls', 'manual+syscalls'), 51 | ('nonbox+syscalls', 'box+syscalls'), 52 | ('box+rc+syscalls', 'box+arc+syscalls'), 53 | ('manual+work', 'nonbox+work'), 54 | ] 55 | 56 | for a, b in compare: 57 | ta = tests[a][0] 58 | tb = tests[b][0] 59 | 60 | if ta >= 1000 and tb >= 1000: 61 | ta /= 1000 62 | tb /= 1000 63 | 64 | v = ta / tb 65 | if v < 1: 66 | p = -((1 - v) * 100) 67 | else: 68 | p = (v - 1) * 100 69 | 70 | print('{} / {} = {:.4} ({:.4}%)'.format(a, b, v, p)) 71 | 72 | print('') 73 | 74 | d = tests['nonbox'][0] - tests['manual'][0] 75 | print('nonbox - manual = {}ns, /256 = {}ns'.format(d, int(d / 256))) 76 | -------------------------------------------------------------------------------- /src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | 4 | pub type BoxFuture = Pin>>; 5 | 6 | mod arg { 7 | use crate::list; 8 | use crate::waker::{EmbedWake, EmbedWaker}; 9 | use slab::Slab; 10 | use std::cell::RefCell; 11 | use std::future::Future; 12 | use std::io; 13 | use std::mem::MaybeUninit; 14 | use std::pin::Pin; 15 | use std::task::{Context, Poll}; 16 | 17 | struct Task<'a, W> { 18 | waker: EmbedWaker<'a, W>, 19 | awake: bool, 20 | } 21 | 22 | struct TasksData<'a, F, W> { 23 | nodes: Slab>>, 24 | next: list::List, 25 | futs: Vec>, 26 | } 27 | 28 | struct Tasks<'a, F> { 29 | data: RefCell>, 30 | } 31 | 32 | impl<'a, F> Tasks<'a, F> 33 | where 34 | F: Future + 'a, 35 | { 36 | fn new(tasks_max: usize) -> Self { 37 | let mut data = TasksData { 38 | nodes: Slab::with_capacity(tasks_max), 39 | next: list::List::default(), 40 | futs: Vec::with_capacity(tasks_max), 41 | }; 42 | 43 | unsafe { data.futs.set_len(tasks_max) }; 44 | 45 | Self { 46 | data: RefCell::new(data), 47 | } 48 | } 49 | 50 | fn is_empty(&self) -> bool { 51 | self.data.borrow().nodes.is_empty() 52 | } 53 | 54 | fn add(&'a self, get_fut_fn: S) -> Result<(), ()> 55 | where 56 | S: FnOnce(&mut MaybeUninit), 57 | { 58 | let data = &mut *self.data.borrow_mut(); 59 | 60 | if data.nodes.len() == data.nodes.capacity() { 61 | return Err(()); 62 | } 63 | 64 | let entry = data.nodes.vacant_entry(); 65 | let key = entry.key(); 66 | 67 | let waker = EmbedWaker::new(self, key); 68 | 69 | let task = Task { waker, awake: true }; 70 | 71 | entry.insert(list::Node::new(task)); 72 | 73 | data.next.push_back(&mut data.nodes, key); 74 | 75 | get_fut_fn(&mut data.futs[key]); 76 | 77 | Ok(()) 78 | } 79 | 80 | fn wake(&self, task_id: usize) { 81 | let data = &mut *self.data.borrow_mut(); 82 | 83 | let task = &mut data.nodes[task_id].value; 84 | 85 | if !task.awake { 86 | task.awake = true; 87 | 88 | data.next.remove(&mut data.nodes, task_id); 89 | data.next.push_back(&mut data.nodes, task_id); 90 | } 91 | } 92 | 93 | fn process_next(&self) { 94 | loop { 95 | let (nkey, task_ptr, fut_ptr) = { 96 | let tasks = &mut *self.data.borrow_mut(); 97 | 98 | let nkey = match tasks.next.head { 99 | Some(nkey) => nkey, 100 | None => break, 101 | }; 102 | 103 | tasks.next.remove(&mut tasks.nodes, nkey); 104 | 105 | let task = &mut tasks.nodes[nkey].value; 106 | 107 | task.awake = false; 108 | 109 | let fut = unsafe { tasks.futs[nkey].assume_init_mut() }; 110 | 111 | (nkey, task as *mut Task, fut as *mut F) 112 | }; 113 | 114 | // SAFETY: task won't move/drop while this pointer is in use. 115 | // we don't allow inserting into the slab beyond its capacity, 116 | // therefore its items never move. and the only place we remove 117 | // the pointed-to item is at the end of this function, after we 118 | // are no longer using the pointer 119 | let task = unsafe { task_ptr.as_mut().unwrap() }; 120 | 121 | // SAFETY: fut never moves, and won't drop while this pointer 122 | // is in use. we don't add or remove items to/from the vec 123 | // after its initialization. drop is done in-place, after we 124 | // are no longer using the pointer 125 | let mut fut = unsafe { Pin::new_unchecked(fut_ptr.as_mut().unwrap()) }; 126 | 127 | let done = { 128 | // SAFETY: as established above, the task won't move, 129 | // thus neither will the waker field 130 | let w = unsafe { Pin::new_unchecked(&mut task.waker) }; 131 | 132 | let mut waker_mem = MaybeUninit::uninit(); 133 | 134 | let mut cx = Context::from_waker(w.as_std(&mut waker_mem)); 135 | 136 | match fut.as_mut().poll(&mut cx) { 137 | Poll::Ready(_) => true, 138 | Poll::Pending => false, 139 | } 140 | }; 141 | 142 | if done { 143 | let tasks = &mut *self.data.borrow_mut(); 144 | 145 | unsafe { tasks.futs[nkey].assume_init_drop() }; 146 | 147 | let task = &mut tasks.nodes[nkey].value; 148 | 149 | assert_eq!(task.waker.ref_count(), 1); 150 | 151 | tasks.next.remove(&mut tasks.nodes, nkey); 152 | tasks.nodes.remove(nkey); 153 | } 154 | } 155 | } 156 | } 157 | 158 | impl EmbedWake for Tasks<'_, F> 159 | where 160 | F: Future, 161 | { 162 | fn wake(&self, task_id: usize) { 163 | Tasks::wake(self, task_id); 164 | } 165 | } 166 | 167 | struct SpawnerData { 168 | ctx: *const (), 169 | spawn_fn: unsafe fn(*const (), A) -> Result<(), ()>, 170 | } 171 | 172 | pub struct ArgSpawner { 173 | data: RefCell>>, 174 | } 175 | 176 | impl ArgSpawner { 177 | pub fn new() -> Self { 178 | Self { 179 | data: RefCell::new(None), 180 | } 181 | } 182 | 183 | pub fn spawn(&self, arg: A) -> Result<(), ()> { 184 | match &*self.data.borrow() { 185 | Some(data) => unsafe { (data.spawn_fn)(data.ctx, arg) }, 186 | None => Err(()), 187 | } 188 | } 189 | } 190 | 191 | pub struct ArgExecutor<'sp, 'ex, F, A, S> { 192 | tasks: Tasks<'ex, F>, 193 | spawn_fn: S, 194 | spawner: RefCell>>, 195 | } 196 | 197 | impl<'sp: 'ex, 'ex, F, A: 'sp, S> ArgExecutor<'sp, 'ex, F, A, S> 198 | where 199 | F: Future + 'ex, 200 | S: Fn(A, &mut MaybeUninit) + 'ex, 201 | { 202 | pub fn new(tasks_max: usize, spawn_fn: S) -> Self { 203 | Self { 204 | tasks: Tasks::new(tasks_max), 205 | spawn_fn, 206 | spawner: RefCell::new(None), 207 | } 208 | } 209 | 210 | pub fn spawn(&'ex self, arg: A) -> Result<(), ()> { 211 | self.tasks.add(|dest| (self.spawn_fn)(arg, dest)) 212 | } 213 | 214 | pub fn set_spawner(&self, spawner: &'sp ArgSpawner) { 215 | *self.spawner.borrow_mut() = Some(spawner); 216 | 217 | let mut spawner = self.spawner.borrow_mut(); 218 | let spawner = spawner.as_mut().unwrap(); 219 | 220 | *spawner.data.borrow_mut() = Some(SpawnerData { 221 | ctx: self as *const Self as *const (), 222 | spawn_fn: Self::spawn_by_arg_fn, 223 | }); 224 | } 225 | 226 | unsafe fn spawn_by_arg_fn(ctx: *const (), arg: A) -> Result<(), ()> { 227 | let executor = { (ctx as *const Self).as_ref().unwrap() }; 228 | 229 | executor.spawn(arg) 230 | } 231 | 232 | pub fn run

(&self, park: P) 233 | where 234 | P: Fn() -> Result<(), io::Error>, 235 | { 236 | loop { 237 | self.tasks.process_next(); 238 | 239 | if self.tasks.is_empty() { 240 | break; 241 | } 242 | 243 | park().unwrap(); 244 | } 245 | } 246 | } 247 | 248 | impl<'sp, 'ex, F, A: 'sp, S> Drop for ArgExecutor<'sp, 'ex, F, A, S> { 249 | fn drop(&mut self) { 250 | if let Some(spawner) = &mut *self.spawner.borrow_mut() { 251 | *spawner.data.borrow_mut() = None; 252 | } 253 | } 254 | } 255 | } 256 | 257 | mod bx { 258 | use crate::list; 259 | use crate::waker::{EmbedWake, EmbedWaker}; 260 | use slab::Slab; 261 | use std::cell::RefCell; 262 | use std::future::Future; 263 | use std::io; 264 | use std::mem::MaybeUninit; 265 | use std::pin::Pin; 266 | use std::task::{Context, Poll}; 267 | 268 | struct Task<'a, W> { 269 | fut: Option + 'a>>>, 270 | waker: EmbedWaker<'a, W>, 271 | awake: bool, 272 | } 273 | 274 | struct TasksData<'a, W> { 275 | nodes: Slab>>, 276 | next: list::List, 277 | } 278 | 279 | struct Tasks<'a> { 280 | data: RefCell>, 281 | } 282 | 283 | impl<'a> Tasks<'a> { 284 | fn new(tasks_max: usize) -> Self { 285 | let data = TasksData { 286 | nodes: Slab::with_capacity(tasks_max), 287 | next: list::List::default(), 288 | }; 289 | 290 | Self { 291 | data: RefCell::new(data), 292 | } 293 | } 294 | 295 | fn is_empty(&self) -> bool { 296 | self.data.borrow().nodes.is_empty() 297 | } 298 | 299 | fn add(&'a self, f: Pin + 'a>>) -> Result<(), ()> { 300 | let data = &mut *self.data.borrow_mut(); 301 | 302 | if data.nodes.len() == data.nodes.capacity() { 303 | return Err(()); 304 | } 305 | 306 | let entry = data.nodes.vacant_entry(); 307 | let key = entry.key(); 308 | 309 | let waker = EmbedWaker::new(self, key); 310 | 311 | let task = Task { 312 | fut: Some(f), 313 | waker, 314 | awake: true, 315 | }; 316 | 317 | entry.insert(list::Node::new(task)); 318 | 319 | data.next.push_back(&mut data.nodes, key); 320 | 321 | Ok(()) 322 | } 323 | 324 | fn wake(&self, task_id: usize) { 325 | let data = &mut *self.data.borrow_mut(); 326 | 327 | let task = &mut data.nodes[task_id].value; 328 | 329 | if !task.awake { 330 | task.awake = true; 331 | 332 | data.next.remove(&mut data.nodes, task_id); 333 | data.next.push_back(&mut data.nodes, task_id); 334 | } 335 | } 336 | 337 | fn process_next(&self) { 338 | loop { 339 | let (nkey, task_ptr) = { 340 | let tasks = &mut *self.data.borrow_mut(); 341 | 342 | let nkey = match tasks.next.head { 343 | Some(nkey) => nkey, 344 | None => break, 345 | }; 346 | 347 | tasks.next.remove(&mut tasks.nodes, nkey); 348 | 349 | let task = &mut tasks.nodes[nkey].value; 350 | 351 | task.awake = false; 352 | 353 | (nkey, task as *mut Task) 354 | }; 355 | 356 | // SAFETY: task won't move/drop while this pointer is in use. 357 | // we don't allow inserting into the slab beyond its capacity, 358 | // therefore its items never move. and the only place we remove 359 | // the pointed-to item is at the end of this function, after we 360 | // are no longer using the pointer 361 | let task = unsafe { task_ptr.as_mut().unwrap() }; 362 | 363 | let done = { 364 | let fut: &mut Pin + 'a>> = 365 | task.fut.as_mut().unwrap(); 366 | 367 | // SAFETY: as established above, the task won't move, 368 | // thus neither will the waker field 369 | let w = unsafe { Pin::new_unchecked(&mut task.waker) }; 370 | 371 | let mut waker_mem = MaybeUninit::uninit(); 372 | 373 | let mut cx = Context::from_waker(w.as_std(&mut waker_mem)); 374 | 375 | match fut.as_mut().poll(&mut cx) { 376 | Poll::Ready(_) => true, 377 | Poll::Pending => false, 378 | } 379 | }; 380 | 381 | if done { 382 | task.fut = None; 383 | 384 | assert_eq!(task.waker.ref_count(), 1); 385 | 386 | let tasks = &mut *self.data.borrow_mut(); 387 | 388 | tasks.next.remove(&mut tasks.nodes, nkey); 389 | tasks.nodes.remove(nkey); 390 | } 391 | } 392 | } 393 | } 394 | 395 | impl EmbedWake for Tasks<'_> { 396 | fn wake(&self, task_id: usize) { 397 | Tasks::wake(self, task_id); 398 | } 399 | } 400 | 401 | struct SpawnerData<'a> { 402 | ctx: *const (), 403 | spawn_fn: unsafe fn(*const (), Pin + 'a>>) -> Result<(), ()>, 404 | } 405 | 406 | pub struct BoxSpawner<'a> { 407 | data: RefCell>>, 408 | } 409 | 410 | impl<'a> BoxSpawner<'a> { 411 | pub fn new() -> Self { 412 | Self { 413 | data: RefCell::new(None), 414 | } 415 | } 416 | 417 | pub fn spawn(&self, f: F) -> Result<(), ()> 418 | where 419 | F: Future + 'a, 420 | { 421 | self.spawn_boxed(Box::pin(f)) 422 | } 423 | 424 | pub fn spawn_boxed(&self, f: Pin + 'a>>) -> Result<(), ()> { 425 | match &*self.data.borrow() { 426 | Some(data) => unsafe { (data.spawn_fn)(data.ctx, f) }, 427 | None => Err(()), 428 | } 429 | } 430 | } 431 | 432 | pub struct BoxExecutor<'sp: 'ex, 'ex> { 433 | tasks: Tasks<'ex>, 434 | spawner: RefCell>>, 435 | } 436 | 437 | impl<'sp: 'ex, 'ex> BoxExecutor<'sp, 'ex> { 438 | pub fn new(tasks_max: usize) -> Self { 439 | Self { 440 | tasks: Tasks::new(tasks_max), 441 | spawner: RefCell::new(None), 442 | } 443 | } 444 | 445 | pub fn spawn(&'ex self, f: Pin + 'sp>>) -> Result<(), ()> { 446 | self.tasks.add(f) 447 | } 448 | 449 | pub fn set_spawner(&self, spawner: &'sp BoxSpawner<'sp>) { 450 | *self.spawner.borrow_mut() = Some(spawner); 451 | 452 | let mut spawner = self.spawner.borrow_mut(); 453 | let spawner = spawner.as_mut().unwrap(); 454 | 455 | *spawner.data.borrow_mut() = Some(SpawnerData { 456 | ctx: self as *const Self as *const (), 457 | spawn_fn: Self::spawn_fn, 458 | }); 459 | } 460 | 461 | unsafe fn spawn_fn( 462 | ctx: *const (), 463 | f: Pin + 'sp>>, 464 | ) -> Result<(), ()> { 465 | let executor = { (ctx as *const Self).as_ref().unwrap() }; 466 | 467 | executor.spawn(f) 468 | } 469 | 470 | pub fn run

(&self, park: P) 471 | where 472 | P: Fn() -> Result<(), io::Error>, 473 | { 474 | loop { 475 | self.tasks.process_next(); 476 | 477 | if self.tasks.is_empty() { 478 | break; 479 | } 480 | 481 | park().unwrap(); 482 | } 483 | } 484 | } 485 | 486 | impl<'sp, 'ex> Drop for BoxExecutor<'sp, 'ex> { 487 | fn drop(&mut self) { 488 | if let Some(spawner) = &mut *self.spawner.borrow_mut() { 489 | *spawner.data.borrow_mut() = None; 490 | } 491 | } 492 | } 493 | } 494 | 495 | mod boxrc { 496 | use super::BoxFuture; 497 | use crate::list; 498 | use crate::waker::{CheckedLocalWake, LocalWake, WakerFactory}; 499 | use slab::Slab; 500 | use std::cell::RefCell; 501 | use std::future::Future; 502 | use std::io; 503 | use std::rc::{Rc, Weak}; 504 | use std::sync::Arc; 505 | use std::task::{Context, Poll, Wake, Waker}; 506 | use std::thread::{self, ThreadId}; 507 | 508 | struct TaskWaker { 509 | tasks: Weak, 510 | task_id: usize, 511 | thread_id: ThreadId, 512 | } 513 | 514 | // SAFETY: we promise to not send wakers across threads 515 | unsafe impl Send for TaskWaker {} 516 | unsafe impl Sync for TaskWaker {} 517 | 518 | impl LocalWake for TaskWaker { 519 | fn wake(self: Rc) { 520 | LocalWake::wake_by_ref(&self); 521 | } 522 | 523 | fn wake_by_ref(self: &Rc) { 524 | if let Some(tasks) = self.tasks.upgrade() { 525 | tasks.wake(self.task_id); 526 | } 527 | } 528 | } 529 | 530 | impl CheckedLocalWake for TaskWaker { 531 | fn thread_id(self: &Rc) -> ThreadId { 532 | self.thread_id 533 | } 534 | 535 | fn wake(self: Rc) { 536 | CheckedLocalWake::wake_by_ref(&self); 537 | } 538 | 539 | fn wake_by_ref(self: &Rc) { 540 | if let Some(tasks) = self.tasks.upgrade() { 541 | tasks.wake(self.task_id); 542 | } 543 | } 544 | } 545 | 546 | impl Wake for TaskWaker { 547 | fn wake(self: Arc) { 548 | self.wake_by_ref(); 549 | } 550 | 551 | fn wake_by_ref(self: &Arc) { 552 | if let Some(tasks) = self.tasks.upgrade() { 553 | tasks.wake(self.task_id); 554 | } 555 | } 556 | } 557 | 558 | struct Task { 559 | fut: Option, 560 | awake: bool, 561 | } 562 | 563 | struct TasksData { 564 | nodes: Slab>, 565 | next: list::List, 566 | } 567 | 568 | struct Tasks { 569 | data: RefCell, 570 | wakers: Vec, 571 | waker_strong_counts: Vec usize>>, 572 | } 573 | 574 | impl Tasks { 575 | fn new(tasks_max: usize, waker_factory: W) -> Rc 576 | where 577 | W: WakerFactory, 578 | { 579 | let data = TasksData { 580 | nodes: Slab::with_capacity(tasks_max), 581 | next: list::List::default(), 582 | }; 583 | 584 | let tasks = { 585 | let tasks = Rc::new(Self { 586 | data: RefCell::new(data), 587 | wakers: Vec::new(), 588 | waker_strong_counts: Vec::new(), 589 | }); 590 | 591 | let mut wakers = Vec::with_capacity(tasks_max); 592 | let mut strong_counts = Vec::with_capacity(tasks_max); 593 | 594 | for task_id in 0..wakers.capacity() { 595 | let (waker, c) = waker_factory.new_waker(TaskWaker { 596 | tasks: Rc::downgrade(&tasks), 597 | task_id, 598 | thread_id: thread::current().id(), 599 | }); 600 | 601 | wakers.push(waker); 602 | strong_counts.push(c); 603 | } 604 | 605 | // SAFETY: we can modify the content of the Rc here because 606 | // nothing is accessing it yet. we only just constructed the Rc 607 | // above, and the TaskWakers take refs but they don't access 608 | // the Rc content at rest 609 | unsafe { 610 | let tasks = Rc::into_raw(tasks) as *mut Tasks; 611 | (*tasks).wakers = wakers; 612 | (*tasks).waker_strong_counts = strong_counts; 613 | 614 | Rc::from_raw(tasks) 615 | } 616 | }; 617 | 618 | tasks 619 | } 620 | 621 | fn is_empty(&self) -> bool { 622 | self.data.borrow().nodes.is_empty() 623 | } 624 | 625 | fn add(&self, f: BoxFuture) -> Result<(), ()> { 626 | let data = &mut *self.data.borrow_mut(); 627 | 628 | if data.nodes.len() == data.nodes.capacity() { 629 | return Err(()); 630 | } 631 | 632 | let entry = data.nodes.vacant_entry(); 633 | let key = entry.key(); 634 | 635 | let task = Task { 636 | fut: Some(f), 637 | awake: true, 638 | }; 639 | 640 | entry.insert(list::Node::new(task)); 641 | 642 | data.next.push_back(&mut data.nodes, key); 643 | 644 | Ok(()) 645 | } 646 | 647 | fn wake(&self, task_id: usize) { 648 | let data = &mut *self.data.borrow_mut(); 649 | 650 | let task = &mut data.nodes[task_id].value; 651 | 652 | if !task.awake { 653 | task.awake = true; 654 | 655 | data.next.remove(&mut data.nodes, task_id); 656 | data.next.push_back(&mut data.nodes, task_id); 657 | } 658 | } 659 | 660 | fn process_next<'a>(&'a self) { 661 | loop { 662 | let (nkey, task_ptr) = { 663 | let tasks = &mut *self.data.borrow_mut(); 664 | 665 | let nkey = match tasks.next.head { 666 | Some(nkey) => nkey, 667 | None => break, 668 | }; 669 | 670 | tasks.next.remove(&mut tasks.nodes, nkey); 671 | 672 | let task = &mut tasks.nodes[nkey].value; 673 | 674 | task.awake = false; 675 | 676 | (nkey, task as *mut Task) 677 | }; 678 | 679 | // SAFETY: task won't move/drop while this pointer is in use. 680 | // we don't allow inserting into the slab beyond its capacity, 681 | // therefore its items never move. and the only place we remove 682 | // the pointed-to item is at the end of this function, after we 683 | // are no longer using the pointer 684 | let task = unsafe { task_ptr.as_mut().unwrap() }; 685 | 686 | let done = { 687 | let fut: &mut BoxFuture = task.fut.as_mut().unwrap(); 688 | 689 | let mut cx = Context::from_waker(&self.wakers[nkey]); 690 | 691 | match fut.as_mut().poll(&mut cx) { 692 | Poll::Ready(_) => true, 693 | Poll::Pending => false, 694 | } 695 | }; 696 | 697 | if done { 698 | task.fut = None; 699 | 700 | assert_eq!((self.waker_strong_counts[nkey])(), 1); 701 | 702 | let tasks = &mut *self.data.borrow_mut(); 703 | 704 | tasks.next.remove(&mut tasks.nodes, nkey); 705 | tasks.nodes.remove(nkey); 706 | } 707 | } 708 | } 709 | } 710 | 711 | pub struct BoxRcExecutor { 712 | tasks: Rc, 713 | } 714 | 715 | impl BoxRcExecutor { 716 | pub fn new(tasks_max: usize, waker_factory: W) -> Self 717 | where 718 | W: WakerFactory, 719 | { 720 | Self { 721 | tasks: Tasks::new(tasks_max, waker_factory), 722 | } 723 | } 724 | 725 | pub fn spawn(&self, f: F) -> Result<(), ()> 726 | where 727 | F: Future + 'static, 728 | { 729 | self.tasks.add(Box::pin(f)) 730 | } 731 | 732 | pub fn run

(&self, park: P) 733 | where 734 | P: Fn() -> Result<(), io::Error>, 735 | { 736 | loop { 737 | self.tasks.process_next(); 738 | 739 | if self.tasks.is_empty() { 740 | break; 741 | } 742 | 743 | park().unwrap(); 744 | } 745 | } 746 | } 747 | } 748 | 749 | pub use arg::{ArgExecutor, ArgSpawner}; 750 | pub use boxrc::BoxRcExecutor; 751 | pub use bx::{BoxExecutor, BoxSpawner}; 752 | -------------------------------------------------------------------------------- /src/fakeio.rs: -------------------------------------------------------------------------------- 1 | use slab::Slab; 2 | use std::cell::{Cell, RefCell}; 3 | use std::io; 4 | 5 | pub enum StatsType { 6 | Register, 7 | Unregister, 8 | Poll, 9 | Accept, 10 | Read, 11 | Write, 12 | } 13 | 14 | pub trait Stats { 15 | fn inc(&self, t: StatsType); 16 | } 17 | 18 | pub trait Evented { 19 | fn set_poll_index(&self, index: Option); 20 | fn get_poll_index(&self) -> Option; 21 | } 22 | 23 | pub struct FakeStream 24 | where 25 | T: Stats, 26 | { 27 | poll_index: Cell>, 28 | stats: T, 29 | calls: usize, 30 | } 31 | 32 | impl FakeStream 33 | where 34 | T: Stats, 35 | { 36 | fn new(stats: T) -> Self { 37 | Self { 38 | poll_index: Cell::new(None), 39 | stats, 40 | calls: 0, 41 | } 42 | } 43 | } 44 | 45 | impl io::Read for FakeStream 46 | where 47 | T: Stats, 48 | { 49 | fn read(&mut self, buf: &mut [u8]) -> Result { 50 | self.stats.inc(StatsType::Read); 51 | 52 | self.calls += 1; 53 | 54 | if self.calls % 2 == 1 { 55 | Err(io::Error::from(io::ErrorKind::WouldBlock)) 56 | } else { 57 | let data = &b"hello world\n"[..]; 58 | 59 | assert!(buf.len() >= data.len()); 60 | 61 | buf[..data.len()].copy_from_slice(&data); 62 | 63 | Ok(data.len()) 64 | } 65 | } 66 | } 67 | 68 | impl io::Write for FakeStream 69 | where 70 | T: Stats, 71 | { 72 | fn write(&mut self, buf: &[u8]) -> Result { 73 | self.stats.inc(StatsType::Write); 74 | 75 | self.calls += 1; 76 | 77 | if self.calls % 2 == 1 { 78 | Err(io::Error::from(io::ErrorKind::WouldBlock)) 79 | } else { 80 | Ok(buf.len()) 81 | } 82 | } 83 | 84 | fn flush(&mut self) -> Result<(), io::Error> { 85 | Ok(()) 86 | } 87 | } 88 | 89 | impl Evented for FakeStream 90 | where 91 | T: Stats, 92 | { 93 | fn set_poll_index(&self, index: Option) { 94 | self.poll_index.set(index); 95 | } 96 | 97 | fn get_poll_index(&self) -> Option { 98 | self.poll_index.get() 99 | } 100 | } 101 | 102 | pub struct FakeListener { 103 | poll_index: Cell>, 104 | stats: T, 105 | calls: RefCell, 106 | } 107 | 108 | impl FakeListener 109 | where 110 | T: Stats + Clone, 111 | { 112 | pub fn new(stats: T) -> Self { 113 | Self { 114 | poll_index: Cell::new(None), 115 | stats, 116 | calls: RefCell::new(0), 117 | } 118 | } 119 | 120 | pub fn accept(&self) -> Result, io::Error> { 121 | self.stats.inc(StatsType::Accept); 122 | 123 | *self.calls.borrow_mut() += 1; 124 | 125 | if *self.calls.borrow() % 2 == 1 { 126 | Err(io::Error::from(io::ErrorKind::WouldBlock)) 127 | } else { 128 | Ok(FakeStream::new(self.stats.clone())) 129 | } 130 | } 131 | } 132 | 133 | impl Evented for FakeListener { 134 | fn set_poll_index(&self, index: Option) { 135 | self.poll_index.set(index); 136 | } 137 | 138 | fn get_poll_index(&self) -> Option { 139 | self.poll_index.get() 140 | } 141 | } 142 | 143 | pub const READABLE: u8 = 1; 144 | pub const WRITABLE: u8 = 2; 145 | 146 | pub struct Poll { 147 | stats: T, 148 | items: RefCell>, 149 | } 150 | 151 | impl Poll 152 | where 153 | T: Stats, 154 | { 155 | pub fn new(capacity: usize, stats: T) -> Self { 156 | Self { 157 | stats, 158 | items: RefCell::new(Slab::with_capacity(capacity)), 159 | } 160 | } 161 | 162 | pub fn register(&self, handle: &E, key: usize, interest: u8) { 163 | self.stats.inc(StatsType::Register); 164 | 165 | let index = self.items.borrow_mut().insert((interest, key)); 166 | 167 | handle.set_poll_index(Some(index)); 168 | } 169 | 170 | pub fn unregister(&self, handle: &E) { 171 | self.stats.inc(StatsType::Unregister); 172 | 173 | if let Some(index) = handle.get_poll_index() { 174 | self.items.borrow_mut().remove(index); 175 | 176 | handle.set_poll_index(None); 177 | } 178 | } 179 | 180 | pub fn poll(&self, events: &mut Slab<(usize, u8)>) { 181 | self.stats.inc(StatsType::Poll); 182 | 183 | events.clear(); 184 | 185 | for (_, (interest, key)) in self.items.borrow().iter() { 186 | events.insert((*key, *interest)); 187 | } 188 | } 189 | } 190 | 191 | impl Drop for Poll { 192 | fn drop(&mut self) { 193 | // confirm all i/o objects were unregistered 194 | assert!(self.items.borrow().is_empty()); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/future.rs: -------------------------------------------------------------------------------- 1 | use crate::fakeio; 2 | use crate::fakeio::{Evented, FakeListener, FakeStream, Stats, READABLE, WRITABLE}; 3 | use slab::Slab; 4 | use std::cell::RefCell; 5 | use std::future::Future; 6 | use std::io; 7 | use std::io::{Read, Write}; 8 | use std::marker::PhantomData; 9 | use std::pin::Pin; 10 | use std::task::{Context, Poll, Waker}; 11 | 12 | pub trait FakeReactorRef: Clone 13 | where 14 | T: Stats, 15 | { 16 | fn get(&self) -> &FakeReactor; 17 | 18 | fn register<'a, E: Evented>( 19 | &'a self, 20 | handle: &E, 21 | interest: u8, 22 | ) -> Result, io::Error> { 23 | let r = self.get(); 24 | 25 | let data = &mut *r.data.borrow_mut(); 26 | 27 | if data.registrations.len() == data.registrations.capacity() { 28 | return Err(io::Error::from(io::ErrorKind::WriteZero)); 29 | } 30 | 31 | let key = data.registrations.insert(EventRegistration { 32 | ready: false, 33 | waker: None, 34 | }); 35 | 36 | r.poll.register(handle, key, interest); 37 | 38 | Ok(RegistrationHandle { 39 | reactor: self.clone(), 40 | key, 41 | _marker: PhantomData, 42 | }) 43 | } 44 | } 45 | 46 | pub struct RegistrationHandle 47 | where 48 | T: Stats, 49 | R: FakeReactorRef, 50 | { 51 | reactor: R, 52 | key: usize, 53 | _marker: PhantomData, 54 | } 55 | 56 | impl RegistrationHandle 57 | where 58 | T: Stats, 59 | R: FakeReactorRef, 60 | { 61 | fn is_ready(&self) -> bool { 62 | let data = &*self.reactor.get().data.borrow(); 63 | 64 | let event_reg = &data.registrations[self.key]; 65 | 66 | event_reg.ready 67 | } 68 | 69 | fn set_ready(&self, ready: bool) { 70 | let data = &mut *self.reactor.get().data.borrow_mut(); 71 | 72 | let event_reg = &mut data.registrations[self.key]; 73 | 74 | event_reg.ready = ready; 75 | } 76 | 77 | fn bind_waker(&self, waker: &Waker) { 78 | let data = &mut *self.reactor.get().data.borrow_mut(); 79 | 80 | let event_reg = &mut data.registrations[self.key]; 81 | 82 | if let Some(current_waker) = &event_reg.waker { 83 | if current_waker.will_wake(waker) { 84 | // keep the current waker 85 | return; 86 | } 87 | } 88 | 89 | event_reg.waker = Some(waker.clone()); 90 | } 91 | 92 | fn unbind_waker(&self) { 93 | let data = &mut *self.reactor.get().data.borrow_mut(); 94 | 95 | let event_reg = &mut data.registrations[self.key]; 96 | 97 | event_reg.waker = None; 98 | } 99 | } 100 | 101 | impl Drop for RegistrationHandle 102 | where 103 | T: Stats, 104 | R: FakeReactorRef, 105 | { 106 | fn drop(&mut self) { 107 | let data = &mut *self.reactor.get().data.borrow_mut(); 108 | 109 | data.registrations.remove(self.key); 110 | } 111 | } 112 | 113 | struct EventRegistration { 114 | ready: bool, 115 | waker: Option, 116 | } 117 | 118 | struct FakeReactorData { 119 | registrations: Slab, 120 | events: Slab<(usize, u8)>, 121 | } 122 | 123 | pub struct FakeReactor { 124 | data: RefCell, 125 | poll: fakeio::Poll, 126 | } 127 | 128 | impl FakeReactor 129 | where 130 | T: Stats, 131 | { 132 | pub fn new(registrations_max: usize, stats: T) -> Self { 133 | let data = FakeReactorData { 134 | registrations: Slab::with_capacity(registrations_max), 135 | events: Slab::with_capacity(128), 136 | }; 137 | 138 | Self { 139 | data: RefCell::new(data), 140 | poll: fakeio::Poll::new(128, stats), 141 | } 142 | } 143 | 144 | pub fn poll(&self) -> Result<(), io::Error> { 145 | let data = &mut *self.data.borrow_mut(); 146 | 147 | self.poll.poll(&mut data.events); 148 | 149 | for (_, (key, _)) in data.events.iter() { 150 | if let Some(event_reg) = data.registrations.get_mut(*key) { 151 | event_reg.ready = true; 152 | 153 | if let Some(waker) = event_reg.waker.take() { 154 | waker.wake(); 155 | } 156 | } 157 | } 158 | 159 | Ok(()) 160 | } 161 | 162 | fn unregister(&self, handle: &E) { 163 | self.poll.unregister(handle); 164 | } 165 | } 166 | 167 | pub struct AsyncFakeStream 168 | where 169 | T: Stats, 170 | R: FakeReactorRef, 171 | { 172 | inner: FakeStream, 173 | handle: RegistrationHandle, 174 | } 175 | 176 | impl AsyncFakeStream 177 | where 178 | T: Stats + Clone, 179 | R: FakeReactorRef, 180 | { 181 | pub fn new(s: FakeStream, reactor: R) -> Self { 182 | let handle = reactor.register(&s, READABLE | WRITABLE).unwrap(); 183 | 184 | handle.set_ready(true); 185 | 186 | Self { inner: s, handle } 187 | } 188 | 189 | pub fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadFuture<'a, T, R> { 190 | ReadFuture { s: self, buf } 191 | } 192 | 193 | pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> WriteFuture<'a, T, R> { 194 | WriteFuture { s: self, buf } 195 | } 196 | } 197 | 198 | impl Drop for AsyncFakeStream 199 | where 200 | T: Stats, 201 | R: FakeReactorRef, 202 | { 203 | fn drop(&mut self) { 204 | self.handle.reactor.get().unregister(&self.inner); 205 | } 206 | } 207 | 208 | pub struct AsyncFakeListener 209 | where 210 | T: Stats, 211 | R: FakeReactorRef, 212 | { 213 | inner: FakeListener, 214 | handle: RegistrationHandle, 215 | } 216 | 217 | impl AsyncFakeListener 218 | where 219 | T: Stats + Clone, 220 | R: FakeReactorRef, 221 | { 222 | pub fn new(reactor: R, stats: T) -> Self { 223 | let l = FakeListener::new(stats); 224 | 225 | let handle = reactor.register(&l, READABLE).unwrap(); 226 | 227 | handle.set_ready(true); 228 | 229 | Self { inner: l, handle } 230 | } 231 | 232 | pub fn accept<'a>(&'a self) -> AcceptFuture<'a, T, R> { 233 | AcceptFuture { l: self } 234 | } 235 | } 236 | 237 | impl Drop for AsyncFakeListener 238 | where 239 | T: Stats, 240 | R: FakeReactorRef, 241 | { 242 | fn drop(&mut self) { 243 | self.handle.reactor.get().unregister(&self.inner); 244 | } 245 | } 246 | 247 | pub struct ReadFuture<'a, T, R> 248 | where 249 | T: Stats, 250 | R: FakeReactorRef, 251 | { 252 | s: &'a mut AsyncFakeStream, 253 | buf: &'a mut [u8], 254 | } 255 | 256 | impl<'a, T, R> Future for ReadFuture<'a, T, R> 257 | where 258 | T: Stats, 259 | R: FakeReactorRef, 260 | { 261 | type Output = Result; 262 | 263 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 264 | let f = &mut *self; 265 | 266 | f.s.handle.bind_waker(cx.waker()); 267 | 268 | if !f.s.handle.is_ready() { 269 | return Poll::Pending; 270 | } 271 | 272 | match f.s.inner.read(f.buf) { 273 | Ok(size) => Poll::Ready(Ok(size)), 274 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 275 | f.s.handle.set_ready(false); 276 | 277 | Poll::Pending 278 | } 279 | Err(e) => Poll::Ready(Err(e)), 280 | } 281 | } 282 | } 283 | 284 | impl Drop for ReadFuture<'_, T, R> 285 | where 286 | T: Stats, 287 | R: FakeReactorRef, 288 | { 289 | fn drop(&mut self) { 290 | self.s.handle.unbind_waker(); 291 | } 292 | } 293 | 294 | pub struct WriteFuture<'a, T, R> 295 | where 296 | T: Stats, 297 | R: FakeReactorRef, 298 | { 299 | s: &'a mut AsyncFakeStream, 300 | buf: &'a [u8], 301 | } 302 | 303 | impl<'a, T, R> Future for WriteFuture<'a, T, R> 304 | where 305 | T: Stats, 306 | R: FakeReactorRef, 307 | { 308 | type Output = Result; 309 | 310 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 311 | let f = &mut *self; 312 | 313 | f.s.handle.bind_waker(cx.waker()); 314 | 315 | if !f.s.handle.is_ready() { 316 | return Poll::Pending; 317 | } 318 | 319 | match f.s.inner.write(f.buf) { 320 | Ok(size) => Poll::Ready(Ok(size)), 321 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 322 | f.s.handle.set_ready(false); 323 | 324 | Poll::Pending 325 | } 326 | Err(e) => Poll::Ready(Err(e)), 327 | } 328 | } 329 | } 330 | 331 | impl Drop for WriteFuture<'_, T, R> 332 | where 333 | T: Stats, 334 | R: FakeReactorRef, 335 | { 336 | fn drop(&mut self) { 337 | self.s.handle.unbind_waker(); 338 | } 339 | } 340 | 341 | pub struct AcceptFuture<'a, T, R> 342 | where 343 | T: Stats, 344 | R: FakeReactorRef, 345 | { 346 | l: &'a AsyncFakeListener, 347 | } 348 | 349 | impl<'a, T, R> Future for AcceptFuture<'a, T, R> 350 | where 351 | T: Stats + Clone, 352 | R: FakeReactorRef, 353 | { 354 | type Output = Result, io::Error>; 355 | 356 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 357 | let f = &mut *self; 358 | 359 | f.l.handle.bind_waker(cx.waker()); 360 | 361 | if !f.l.handle.is_ready() { 362 | return Poll::Pending; 363 | } 364 | 365 | match f.l.inner.accept() { 366 | Ok(stream) => Poll::Ready(Ok(AsyncFakeStream::new(stream, f.l.handle.reactor.clone()))), 367 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { 368 | f.l.handle.set_ready(false); 369 | 370 | Poll::Pending 371 | } 372 | Err(e) => Poll::Ready(Err(e)), 373 | } 374 | } 375 | } 376 | 377 | impl Drop for AcceptFuture<'_, T, R> 378 | where 379 | T: Stats, 380 | R: FakeReactorRef, 381 | { 382 | fn drop(&mut self) { 383 | self.l.handle.unbind_waker(); 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod list; 2 | pub mod run; 3 | 4 | mod executor; 5 | mod fakeio; 6 | mod future; 7 | mod waker; 8 | 9 | pub fn run() { 10 | println!("manual: {}", crate::run::run_manual(true, |r| r())); 11 | println!("async: {}", crate::run::run_nonbox(true, |r| r())); 12 | } 13 | -------------------------------------------------------------------------------- /src/list.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Fanout, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | use std::ops::IndexMut; 18 | 19 | pub struct Node { 20 | pub prev: Option, 21 | pub next: Option, 22 | pub value: T, 23 | } 24 | 25 | impl Node { 26 | pub fn new(value: T) -> Self { 27 | Self { 28 | prev: None, 29 | next: None, 30 | value: value, 31 | } 32 | } 33 | } 34 | 35 | #[derive(Default, Clone, Copy)] 36 | pub struct List { 37 | pub head: Option, 38 | pub tail: Option, 39 | } 40 | 41 | impl List { 42 | pub fn is_empty(&self) -> bool { 43 | !self.head.is_some() 44 | } 45 | 46 | pub fn insert(&mut self, nodes: &mut S, after: Option, key: usize) 47 | where 48 | S: IndexMut>, 49 | { 50 | let next = if let Some(pkey) = after { 51 | let pn = &mut nodes[pkey]; 52 | 53 | let next = pn.next; 54 | pn.next = Some(key); 55 | 56 | let n = &mut nodes[key]; 57 | n.prev = Some(pkey); 58 | 59 | next 60 | } else { 61 | let next = self.head; 62 | self.head = Some(key); 63 | 64 | let n = &mut nodes[key]; 65 | n.prev = None; 66 | 67 | next 68 | }; 69 | 70 | let n = &mut nodes[key]; 71 | n.next = next; 72 | 73 | if let Some(nkey) = next { 74 | let nn = &mut nodes[nkey]; 75 | 76 | nn.prev = Some(key); 77 | } else { 78 | self.tail = Some(key); 79 | } 80 | } 81 | 82 | pub fn remove(&mut self, nodes: &mut S, key: usize) 83 | where 84 | S: IndexMut>, 85 | { 86 | let n = &mut nodes[key]; 87 | 88 | let prev = n.prev.take(); 89 | let next = n.next.take(); 90 | 91 | if let Some(pkey) = prev { 92 | let pn = &mut nodes[pkey]; 93 | pn.next = next; 94 | } 95 | 96 | if let Some(nkey) = next { 97 | let nn = &mut nodes[nkey]; 98 | nn.prev = prev; 99 | } 100 | 101 | if let Some(hkey) = self.head { 102 | if hkey == key { 103 | self.head = next; 104 | } 105 | } 106 | 107 | if let Some(tkey) = self.tail { 108 | if tkey == key { 109 | self.tail = prev; 110 | } 111 | } 112 | } 113 | 114 | pub fn pop_front(&mut self, nodes: &mut S) -> Option 115 | where 116 | S: IndexMut>, 117 | { 118 | match self.head { 119 | Some(key) => { 120 | self.remove(nodes, key); 121 | 122 | Some(key) 123 | } 124 | None => None, 125 | } 126 | } 127 | 128 | pub fn push_back(&mut self, nodes: &mut S, key: usize) 129 | where 130 | S: IndexMut>, 131 | { 132 | self.insert(nodes, self.tail, key); 133 | } 134 | 135 | pub fn concat(&mut self, nodes: &mut S, other: &mut Self) 136 | where 137 | S: IndexMut>, 138 | { 139 | if other.is_empty() { 140 | // nothing to do 141 | return; 142 | } 143 | 144 | // other is non-empty so this is guaranteed to succeed 145 | let hkey = other.head.unwrap(); 146 | 147 | let next = nodes[hkey].next; 148 | 149 | // since we're inserting after the tail, this will set next=None 150 | self.insert(nodes, self.tail, hkey); 151 | 152 | // restore the node's next key 153 | nodes[hkey].next = next; 154 | 155 | self.tail = other.tail; 156 | 157 | other.head = None; 158 | other.tail = None; 159 | } 160 | } 161 | 162 | #[cfg(test)] 163 | mod tests { 164 | use super::*; 165 | use slab::Slab; 166 | 167 | #[test] 168 | fn test_list_push_pop() { 169 | let mut nodes = Slab::new(); 170 | let n1 = nodes.insert(Node::new("n1")); 171 | let n2 = nodes.insert(Node::new("n2")); 172 | let n3 = nodes.insert(Node::new("n3")); 173 | 174 | // prevent unused warning on data field 175 | assert_eq!(nodes[n1].value, "n1"); 176 | 177 | assert_eq!(nodes[n1].prev, None); 178 | assert_eq!(nodes[n1].next, None); 179 | assert_eq!(nodes[n2].prev, None); 180 | assert_eq!(nodes[n2].next, None); 181 | assert_eq!(nodes[n3].prev, None); 182 | assert_eq!(nodes[n3].next, None); 183 | 184 | let mut l = List::default(); 185 | assert_eq!(l.is_empty(), true); 186 | assert_eq!(l.head, None); 187 | assert_eq!(l.tail, None); 188 | assert_eq!(l.pop_front(&mut nodes), None); 189 | 190 | l.push_back(&mut nodes, n1); 191 | assert_eq!(l.is_empty(), false); 192 | assert_eq!(l.head, Some(n1)); 193 | assert_eq!(l.tail, Some(n1)); 194 | assert_eq!(nodes[n1].prev, None); 195 | assert_eq!(nodes[n1].next, None); 196 | 197 | l.push_back(&mut nodes, n2); 198 | assert_eq!(l.is_empty(), false); 199 | assert_eq!(l.head, Some(n1)); 200 | assert_eq!(l.tail, Some(n2)); 201 | assert_eq!(nodes[n1].prev, None); 202 | assert_eq!(nodes[n1].next, Some(n2)); 203 | assert_eq!(nodes[n2].prev, Some(n1)); 204 | assert_eq!(nodes[n2].next, None); 205 | 206 | l.push_back(&mut nodes, n3); 207 | assert_eq!(l.is_empty(), false); 208 | assert_eq!(l.head, Some(n1)); 209 | assert_eq!(l.tail, Some(n3)); 210 | assert_eq!(nodes[n1].prev, None); 211 | assert_eq!(nodes[n1].next, Some(n2)); 212 | assert_eq!(nodes[n2].prev, Some(n1)); 213 | assert_eq!(nodes[n2].next, Some(n3)); 214 | assert_eq!(nodes[n3].prev, Some(n2)); 215 | assert_eq!(nodes[n3].next, None); 216 | 217 | let key = l.pop_front(&mut nodes); 218 | assert_eq!(key, Some(n1)); 219 | assert_eq!(l.is_empty(), false); 220 | assert_eq!(l.head, Some(n2)); 221 | assert_eq!(l.tail, Some(n3)); 222 | assert_eq!(nodes[n2].prev, None); 223 | assert_eq!(nodes[n2].next, Some(n3)); 224 | assert_eq!(nodes[n3].prev, Some(n2)); 225 | assert_eq!(nodes[n3].next, None); 226 | 227 | let key = l.pop_front(&mut nodes); 228 | assert_eq!(key, Some(n2)); 229 | assert_eq!(l.is_empty(), false); 230 | assert_eq!(l.head, Some(n3)); 231 | assert_eq!(l.tail, Some(n3)); 232 | assert_eq!(nodes[n3].prev, None); 233 | assert_eq!(nodes[n3].next, None); 234 | 235 | let key = l.pop_front(&mut nodes); 236 | assert_eq!(key, Some(n3)); 237 | assert_eq!(l.is_empty(), true); 238 | assert_eq!(l.head, None); 239 | assert_eq!(l.tail, None); 240 | 241 | assert_eq!(l.pop_front(&mut nodes), None); 242 | } 243 | 244 | #[test] 245 | fn test_remove() { 246 | let mut nodes = Slab::new(); 247 | let n1 = nodes.insert(Node::new("n1")); 248 | 249 | assert_eq!(nodes[n1].prev, None); 250 | assert_eq!(nodes[n1].next, None); 251 | 252 | let mut l = List::default(); 253 | assert_eq!(l.is_empty(), true); 254 | assert_eq!(l.head, None); 255 | assert_eq!(l.tail, None); 256 | 257 | l.push_back(&mut nodes, n1); 258 | assert_eq!(l.is_empty(), false); 259 | assert_eq!(l.head, Some(n1)); 260 | assert_eq!(l.tail, Some(n1)); 261 | assert_eq!(nodes[n1].prev, None); 262 | assert_eq!(nodes[n1].next, None); 263 | 264 | l.remove(&mut nodes, n1); 265 | assert_eq!(l.is_empty(), true); 266 | assert_eq!(l.head, None); 267 | assert_eq!(l.tail, None); 268 | assert_eq!(nodes[n1].prev, None); 269 | assert_eq!(nodes[n1].next, None); 270 | 271 | // already removed 272 | l.remove(&mut nodes, n1); 273 | assert_eq!(l.is_empty(), true); 274 | assert_eq!(l.head, None); 275 | assert_eq!(l.tail, None); 276 | assert_eq!(nodes[n1].prev, None); 277 | assert_eq!(nodes[n1].next, None); 278 | } 279 | 280 | #[test] 281 | fn test_list_concat() { 282 | let mut nodes = Slab::new(); 283 | let n1 = nodes.insert(Node::new("n1")); 284 | let n2 = nodes.insert(Node::new("n2")); 285 | 286 | let mut a = List::default(); 287 | let mut b = List::default(); 288 | 289 | a.concat(&mut nodes, &mut b); 290 | assert_eq!(a.is_empty(), true); 291 | assert_eq!(a.head, None); 292 | assert_eq!(a.tail, None); 293 | assert_eq!(b.is_empty(), true); 294 | assert_eq!(b.head, None); 295 | assert_eq!(b.tail, None); 296 | 297 | a.push_back(&mut nodes, n1); 298 | b.push_back(&mut nodes, n2); 299 | 300 | a.concat(&mut nodes, &mut b); 301 | assert_eq!(a.is_empty(), false); 302 | assert_eq!(a.head, Some(n1)); 303 | assert_eq!(a.tail, Some(n2)); 304 | assert_eq!(b.is_empty(), true); 305 | assert_eq!(b.head, None); 306 | assert_eq!(b.tail, None); 307 | assert_eq!(nodes[n1].prev, None); 308 | assert_eq!(nodes[n1].next, Some(n2)); 309 | assert_eq!(nodes[n2].prev, Some(n1)); 310 | assert_eq!(nodes[n2].next, None); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | rust_async_bench::run(); 3 | } 4 | -------------------------------------------------------------------------------- /src/run.rs: -------------------------------------------------------------------------------- 1 | use crate::executor::{ArgExecutor, ArgSpawner, BoxExecutor, BoxRcExecutor, BoxSpawner}; 2 | use crate::fakeio; 3 | use crate::fakeio::{FakeListener, FakeStream, Poll, READABLE, WRITABLE}; 4 | use crate::future::{AsyncFakeListener, AsyncFakeStream, FakeReactor, FakeReactorRef}; 5 | use crate::list; 6 | use crate::waker::{ArcWakerFactory, CheckedRcWakerFactory, RcWakerFactory}; 7 | use slab::Slab; 8 | use std::cell::RefCell; 9 | use std::fmt; 10 | use std::io; 11 | use std::io::{Read, Write}; 12 | use std::rc::Rc; 13 | 14 | pub const CONNS_MAX: usize = 256; 15 | pub const SMALL_BUFSIZE: usize = 128; 16 | pub const LARGE_BUFSIZE: usize = 16_384; 17 | 18 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 19 | pub struct StatsMetrics { 20 | register: u32, 21 | unregister: u32, 22 | poll: u32, 23 | accept: u32, 24 | read: u32, 25 | write: u32, 26 | } 27 | 28 | impl fmt::Display for StatsMetrics { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | write!( 31 | f, 32 | "register={} unregister={} poll={} accept={} read={} write={}", 33 | self.register, self.unregister, self.poll, self.accept, self.read, self.write 34 | ) 35 | } 36 | } 37 | 38 | struct StatsData { 39 | metrics: StatsMetrics, 40 | pipe_fds: Option<[libc::c_int; 2]>, 41 | } 42 | 43 | impl StatsData { 44 | fn new(syscalls: bool) -> Self { 45 | let pipe_fds = if syscalls { 46 | let mut pipe_fds: [libc::c_int; 2] = [0; 2]; 47 | 48 | let ret = unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; 49 | assert_eq!(ret, 0); 50 | 51 | let ret = unsafe { libc::fcntl(pipe_fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; 52 | assert_eq!(ret, 0); 53 | 54 | Some(pipe_fds) 55 | } else { 56 | None 57 | }; 58 | 59 | Self { 60 | metrics: StatsMetrics { 61 | register: 0, 62 | unregister: 0, 63 | poll: 0, 64 | accept: 0, 65 | read: 0, 66 | write: 0, 67 | }, 68 | pipe_fds, 69 | } 70 | } 71 | 72 | fn do_call(&mut self) { 73 | if let Some(fds) = &self.pipe_fds { 74 | let mut dest: [u8; 1] = [0; 1]; 75 | 76 | let ret = unsafe { libc::read(fds[0], dest.as_mut_ptr() as *mut libc::c_void, 1) }; 77 | assert_eq!(ret, -1); 78 | } 79 | } 80 | } 81 | 82 | impl Drop for StatsData { 83 | fn drop(&mut self) { 84 | if let Some(fds) = &self.pipe_fds { 85 | let ret = unsafe { libc::close(fds[0]) }; 86 | assert_eq!(ret, 0); 87 | 88 | let ret = unsafe { libc::close(fds[1]) }; 89 | assert_eq!(ret, 0); 90 | } 91 | } 92 | } 93 | 94 | pub struct Stats { 95 | data: RefCell, 96 | } 97 | 98 | impl Stats { 99 | pub fn new(syscalls: bool) -> Self { 100 | Self { 101 | data: RefCell::new(StatsData::new(syscalls)), 102 | } 103 | } 104 | 105 | fn get(&self) -> StatsMetrics { 106 | self.data.borrow().metrics 107 | } 108 | } 109 | 110 | impl fakeio::Stats for Stats { 111 | fn inc(&self, t: fakeio::StatsType) { 112 | let data = &mut *self.data.borrow_mut(); 113 | 114 | data.do_call(); 115 | 116 | match t { 117 | fakeio::StatsType::Register => data.metrics.register += 1, 118 | fakeio::StatsType::Unregister => data.metrics.unregister += 1, 119 | fakeio::StatsType::Poll => data.metrics.poll += 1, 120 | fakeio::StatsType::Accept => data.metrics.accept += 1, 121 | fakeio::StatsType::Read => data.metrics.read += 1, 122 | fakeio::StatsType::Write => data.metrics.write += 1, 123 | } 124 | } 125 | } 126 | 127 | impl fakeio::Stats for &Stats { 128 | fn inc(&self, t: fakeio::StatsType) { 129 | Stats::inc(*self, t) 130 | } 131 | } 132 | 133 | impl fakeio::Stats for Rc { 134 | fn inc(&self, t: fakeio::StatsType) { 135 | Stats::inc(self.as_ref(), t) 136 | } 137 | } 138 | 139 | impl<'s> FakeReactorRef<&'s Stats> for &FakeReactor<&'s Stats> { 140 | fn get<'a>(&'a self) -> &'a FakeReactor<&'s Stats> { 141 | *self 142 | } 143 | } 144 | 145 | impl FakeReactorRef> for Rc>> { 146 | fn get(&self) -> &FakeReactor> { 147 | self.as_ref() 148 | } 149 | } 150 | 151 | enum ConnectionState { 152 | ReceivingRequest, 153 | SendingResponse, 154 | } 155 | 156 | struct Connection 157 | where 158 | T: fakeio::Stats, 159 | { 160 | stream: FakeStream, 161 | can_read: bool, 162 | can_write: bool, 163 | state: ConnectionState, 164 | buf: [u8; SMALL_BUFSIZE], 165 | buf_len: usize, 166 | sent: usize, 167 | } 168 | 169 | impl Connection 170 | where 171 | T: fakeio::Stats, 172 | { 173 | fn process(&mut self) -> bool { 174 | loop { 175 | match self.state { 176 | ConnectionState::ReceivingRequest => { 177 | let size = match self.stream.read(&mut self.buf[self.buf_len..]) { 178 | Ok(size) => size, 179 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 180 | self.can_read = false; 181 | return false; 182 | } 183 | Err(_) => unreachable!(), 184 | }; 185 | 186 | self.buf_len += size; 187 | 188 | if (&self.buf[..self.buf_len]).contains(&b'\n') { 189 | self.state = ConnectionState::SendingResponse; 190 | } 191 | } 192 | ConnectionState::SendingResponse => { 193 | let size = match self.stream.write(&self.buf[self.sent..self.buf_len]) { 194 | Ok(size) => size, 195 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 196 | self.can_write = false; 197 | return false; 198 | } 199 | Err(_) => unreachable!(), 200 | }; 201 | 202 | self.sent += size; 203 | 204 | if self.sent >= self.buf_len { 205 | return true; 206 | } 207 | } 208 | } 209 | } 210 | } 211 | } 212 | 213 | pub struct RunManual<'s> { 214 | stats: &'s Stats, 215 | poll: Poll<&'s Stats>, 216 | events: Slab<(usize, u8)>, 217 | conns: Slab>>, 218 | } 219 | 220 | impl<'s> RunManual<'s> { 221 | pub fn new(stats: &'s Stats) -> Self { 222 | let poll = Poll::new(CONNS_MAX + 1, stats); 223 | let events = Slab::with_capacity(CONNS_MAX + 1); 224 | let conns = Slab::with_capacity(CONNS_MAX); 225 | 226 | Self { 227 | stats, 228 | poll, 229 | events, 230 | conns, 231 | } 232 | } 233 | 234 | pub fn run(&mut self) { 235 | let poll = &mut self.poll; 236 | let events = &mut self.events; 237 | let conns = &mut self.conns; 238 | 239 | let mut needs_process = list::List::default(); 240 | 241 | let listener = FakeListener::new(self.stats); 242 | 243 | poll.register(&listener, 0, READABLE); 244 | 245 | let mut can_accept = true; 246 | 247 | let mut accept_left = CONNS_MAX; 248 | 249 | loop { 250 | while accept_left > 0 && can_accept { 251 | match listener.accept() { 252 | Ok(stream) => { 253 | accept_left -= 1; 254 | 255 | let key = conns.insert(list::Node::new(Connection { 256 | stream, 257 | can_read: true, 258 | can_write: true, 259 | state: ConnectionState::ReceivingRequest, 260 | buf: [0; 128], 261 | buf_len: 0, 262 | sent: 0, 263 | })); 264 | 265 | let c = &mut conns[key].value; 266 | 267 | poll.register(&c.stream, key + 1, READABLE | WRITABLE); 268 | 269 | needs_process.push_back(conns, key); 270 | } 271 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => can_accept = false, 272 | _ => unreachable!(), 273 | } 274 | } 275 | 276 | let mut next = needs_process.head; 277 | 278 | while let Some(key) = next { 279 | needs_process.remove(conns, key); 280 | 281 | let c = &mut conns[key].value; 282 | if c.process() { 283 | poll.unregister(&c.stream); 284 | 285 | conns.remove(key); 286 | } 287 | 288 | next = needs_process.head; 289 | } 290 | 291 | if accept_left == 0 && conns.len() == 0 { 292 | break; 293 | } 294 | 295 | poll.poll(events); 296 | 297 | for (_, &(key, state)) in events.iter() { 298 | if key == 0 { 299 | can_accept = true; 300 | } else { 301 | let key = key - 1; 302 | 303 | let c = &mut conns[key].value; 304 | 305 | let mut do_process = false; 306 | 307 | if state & READABLE != 0 { 308 | c.can_read = true; 309 | do_process = true; 310 | } 311 | 312 | if state & WRITABLE != 0 { 313 | c.can_write = true; 314 | do_process = true; 315 | } 316 | 317 | if do_process { 318 | needs_process.remove(conns, key); 319 | needs_process.push_back(conns, key); 320 | } 321 | } 322 | } 323 | } 324 | 325 | poll.unregister(&listener); 326 | } 327 | } 328 | 329 | async fn listen<'r, 's: 'r>( 330 | spawner: &'r ArgSpawner>, 331 | reactor: &'r FakeReactor<&'s Stats>, 332 | stats: &'s Stats, 333 | ) -> Result<(), io::Error> { 334 | let listener = AsyncFakeListener::new(reactor, stats); 335 | 336 | for _ in 0..CONNS_MAX { 337 | let stream = listener.accept().await?; 338 | 339 | spawner.spawn(AsyncInvoke::Connection(stream)).unwrap(); 340 | } 341 | 342 | Ok(()) 343 | } 344 | 345 | pub async fn listen_box( 346 | spawner: &BoxSpawner<'_>, 347 | reactor: Rc>>, 348 | stats: Rc, 349 | ) -> Result<(), io::Error> { 350 | let listener = AsyncFakeListener::new(reactor, stats); 351 | 352 | for _ in 0..CONNS_MAX { 353 | let stream = listener.accept().await?; 354 | 355 | spawner 356 | .spawn(async { connection_box::(stream).await.unwrap() }) 357 | .unwrap(); 358 | } 359 | 360 | Ok(()) 361 | } 362 | 363 | pub async fn listen_box_callerbox( 364 | spawner: &BoxSpawner<'_>, 365 | reactor: Rc>>, 366 | stats: Rc, 367 | ) -> Result<(), io::Error> { 368 | let listener = AsyncFakeListener::new(reactor, stats); 369 | 370 | for _ in 0..CONNS_MAX { 371 | let stream = listener.accept().await?; 372 | 373 | spawner 374 | .spawn_boxed(Box::pin(async { 375 | connection_box::(stream).await.unwrap() 376 | })) 377 | .unwrap(); 378 | } 379 | 380 | Ok(()) 381 | } 382 | 383 | pub async fn listen_rc( 384 | executor: Rc, 385 | reactor: Rc>>, 386 | stats: Rc, 387 | ) -> Result<(), io::Error> { 388 | let listener = AsyncFakeListener::new(reactor, stats); 389 | 390 | for _ in 0..CONNS_MAX { 391 | let stream = listener.accept().await?; 392 | 393 | executor 394 | .spawn(async { connection_box::(stream).await.unwrap() }) 395 | .unwrap(); 396 | } 397 | 398 | Ok(()) 399 | } 400 | 401 | async fn connection<'r, 's, const N: usize>( 402 | mut stream: AsyncFakeStream<&'s Stats, &'r FakeReactor<&'s Stats>>, 403 | ) -> Result<(), io::Error> { 404 | let mut buf = [0; N]; 405 | let mut buf_len = 0; 406 | 407 | while !(&buf[..buf_len]).contains(&b'\n') { 408 | let size = stream.read(&mut buf[buf_len..]).await?; 409 | buf_len += size; 410 | } 411 | 412 | let mut sent = 0; 413 | 414 | while sent < buf_len { 415 | let size = stream.write(&buf[sent..buf_len]).await?; 416 | sent += size; 417 | } 418 | 419 | Ok(()) 420 | } 421 | 422 | async fn connection_box( 423 | mut stream: AsyncFakeStream, Rc>>>, 424 | ) -> Result<(), io::Error> { 425 | let mut buf = [0; N]; 426 | let mut buf_len = 0; 427 | 428 | while !(&buf[..buf_len]).contains(&b'\n') { 429 | let size = stream.read(&mut buf[buf_len..]).await?; 430 | buf_len += size; 431 | } 432 | 433 | let mut sent = 0; 434 | 435 | while sent < buf_len { 436 | let size = stream.write(&buf[sent..buf_len]).await?; 437 | sent += size; 438 | } 439 | 440 | Ok(()) 441 | } 442 | 443 | pub enum AsyncInvoke<'r, 's> { 444 | Listen, 445 | Connection(AsyncFakeStream<&'s Stats, &'r FakeReactor<&'s Stats>>), 446 | } 447 | 448 | pub async fn server_task<'r, 's: 'r, const N: usize>( 449 | spawner: &'r ArgSpawner>, 450 | reactor: &'r FakeReactor<&'s Stats>, 451 | stats: &'s Stats, 452 | invoke: AsyncInvoke<'r, 's>, 453 | ) { 454 | match invoke { 455 | AsyncInvoke::Listen => listen(spawner, reactor, stats).await.unwrap(), 456 | AsyncInvoke::Connection(stream) => connection::(stream).await.unwrap(), 457 | } 458 | } 459 | 460 | pub fn run_manual(syscalls: bool, mut run_fn: R) -> StatsMetrics 461 | where 462 | R: FnMut(&mut dyn FnMut()), 463 | { 464 | let stats = Stats::new(syscalls); 465 | let mut r = RunManual::new(&stats); 466 | 467 | run_fn(&mut || r.run()); 468 | 469 | stats.get() 470 | } 471 | 472 | pub fn run_nonbox(syscalls: bool, mut run_fn: R) -> StatsMetrics 473 | where 474 | R: FnMut(&mut dyn FnMut()), 475 | { 476 | let stats = Stats::new(syscalls); 477 | let reactor = FakeReactor::new(CONNS_MAX + 1, &stats); 478 | let spawner = ArgSpawner::new(); 479 | let executor = ArgExecutor::new(CONNS_MAX + 1, |invoke, dest| { 480 | dest.write(server_task::( 481 | &spawner, &reactor, &stats, invoke, 482 | )); 483 | }); 484 | 485 | executor.set_spawner(&spawner); 486 | 487 | run_fn(&mut || { 488 | spawner.spawn(AsyncInvoke::Listen).unwrap(); 489 | executor.run(|| reactor.poll()); 490 | }); 491 | 492 | stats.get() 493 | } 494 | 495 | pub fn run_callerbox(syscalls: bool, mut run_fn: R) -> StatsMetrics 496 | where 497 | R: FnMut(&mut dyn FnMut()), 498 | { 499 | let stats = Stats::new(syscalls); 500 | let reactor = FakeReactor::new(CONNS_MAX + 1, &stats); 501 | let spawner = ArgSpawner::new(); 502 | let executor = ArgExecutor::new(CONNS_MAX + 1, |invoke, dest| { 503 | dest.write(Box::pin(server_task::( 504 | &spawner, &reactor, &stats, invoke, 505 | ))); 506 | }); 507 | 508 | executor.set_spawner(&spawner); 509 | 510 | run_fn(&mut || { 511 | spawner.spawn(AsyncInvoke::Listen).unwrap(); 512 | executor.run(|| reactor.poll()); 513 | }); 514 | 515 | stats.get() 516 | } 517 | 518 | pub fn run_large_nonbox(syscalls: bool, mut run_fn: R) -> StatsMetrics 519 | where 520 | R: FnMut(&mut dyn FnMut()), 521 | { 522 | let stats = Stats::new(syscalls); 523 | let reactor = FakeReactor::new(CONNS_MAX + 1, &stats); 524 | let spawner = ArgSpawner::new(); 525 | let executor = ArgExecutor::new(CONNS_MAX + 1, |invoke, dest| { 526 | dest.write(server_task::( 527 | &spawner, &reactor, &stats, invoke, 528 | )); 529 | }); 530 | 531 | executor.set_spawner(&spawner); 532 | 533 | run_fn(&mut || { 534 | spawner.spawn(AsyncInvoke::Listen).unwrap(); 535 | executor.run(|| reactor.poll()); 536 | }); 537 | 538 | stats.get() 539 | } 540 | 541 | pub fn run_box(syscalls: bool, mut run_fn: R) -> StatsMetrics 542 | where 543 | R: FnMut(&mut dyn FnMut()), 544 | { 545 | let stats = Rc::new(Stats::new(syscalls)); 546 | let reactor = Rc::new(FakeReactor::new(CONNS_MAX + 1, stats.clone())); 547 | let spawner = BoxSpawner::new(); 548 | let executor = BoxExecutor::new(CONNS_MAX + 1); 549 | 550 | executor.set_spawner(&spawner); 551 | 552 | run_fn(&mut || { 553 | { 554 | let stats = stats.clone(); 555 | let reactor = reactor.clone(); 556 | 557 | spawner 558 | .spawn(async { 559 | listen_box::(&spawner, reactor, stats) 560 | .await 561 | .unwrap() 562 | }) 563 | .unwrap(); 564 | } 565 | 566 | executor.run(|| reactor.poll()); 567 | }); 568 | 569 | stats.get() 570 | } 571 | 572 | pub fn run_box_callerbox(syscalls: bool, mut run_fn: R) -> StatsMetrics 573 | where 574 | R: FnMut(&mut dyn FnMut()), 575 | { 576 | let stats = Rc::new(Stats::new(syscalls)); 577 | let reactor = Rc::new(FakeReactor::new(CONNS_MAX + 1, stats.clone())); 578 | let spawner = BoxSpawner::new(); 579 | let executor = BoxExecutor::new(CONNS_MAX + 1); 580 | 581 | executor.set_spawner(&spawner); 582 | 583 | run_fn(&mut || { 584 | { 585 | let stats = stats.clone(); 586 | let reactor = reactor.clone(); 587 | 588 | spawner 589 | .spawn_boxed(Box::pin(async { 590 | listen_box_callerbox::(&spawner, reactor, stats) 591 | .await 592 | .unwrap() 593 | })) 594 | .unwrap(); 595 | } 596 | 597 | executor.run(|| reactor.poll()); 598 | }); 599 | 600 | stats.get() 601 | } 602 | 603 | pub fn run_large_box(syscalls: bool, mut run_fn: R) -> StatsMetrics 604 | where 605 | R: FnMut(&mut dyn FnMut()), 606 | { 607 | let stats = Rc::new(Stats::new(syscalls)); 608 | let reactor = Rc::new(FakeReactor::new(CONNS_MAX + 1, stats.clone())); 609 | let spawner = BoxSpawner::new(); 610 | let executor = BoxExecutor::new(CONNS_MAX + 1); 611 | 612 | executor.set_spawner(&spawner); 613 | 614 | run_fn(&mut || { 615 | { 616 | let stats = stats.clone(); 617 | let reactor = reactor.clone(); 618 | 619 | spawner 620 | .spawn(async { 621 | listen_box::(&spawner, reactor, stats) 622 | .await 623 | .unwrap() 624 | }) 625 | .unwrap(); 626 | } 627 | 628 | executor.run(|| reactor.poll()); 629 | }); 630 | 631 | stats.get() 632 | } 633 | 634 | pub enum BoxRcMode { 635 | RcWaker, 636 | CheckedRcWaker, 637 | ArcWaker, 638 | } 639 | 640 | pub fn run_box_rc(syscalls: bool, mode: BoxRcMode, mut run_fn: R) -> StatsMetrics 641 | where 642 | R: FnMut(&mut dyn FnMut()), 643 | { 644 | let stats = Rc::new(Stats::new(syscalls)); 645 | let reactor = Rc::new(FakeReactor::new(CONNS_MAX + 1, stats.clone())); 646 | 647 | let executor = Rc::new(match mode { 648 | BoxRcMode::RcWaker => BoxRcExecutor::new(CONNS_MAX + 1, RcWakerFactory::default()), 649 | BoxRcMode::CheckedRcWaker => { 650 | BoxRcExecutor::new(CONNS_MAX + 1, CheckedRcWakerFactory::default()) 651 | } 652 | BoxRcMode::ArcWaker => BoxRcExecutor::new(CONNS_MAX + 1, ArcWakerFactory::default()), 653 | }); 654 | 655 | run_fn(&mut || { 656 | { 657 | let stats = stats.clone(); 658 | let reactor = reactor.clone(); 659 | let executor_copy = executor.clone(); 660 | 661 | executor 662 | .spawn(async { listen_rc(executor_copy, reactor, stats).await.unwrap() }) 663 | .unwrap(); 664 | } 665 | 666 | executor.run(|| reactor.poll()); 667 | }); 668 | 669 | stats.get() 670 | } 671 | 672 | #[cfg(test)] 673 | mod tests { 674 | use super::*; 675 | 676 | const EXPECTED_STATS: StatsMetrics = StatsMetrics { 677 | register: 257, 678 | unregister: 257, 679 | poll: 258, 680 | accept: 512, 681 | read: 512, 682 | write: 512, 683 | }; 684 | 685 | #[test] 686 | fn test_manual() { 687 | assert_eq!(run_manual(false, |r| r()), EXPECTED_STATS); 688 | } 689 | 690 | #[test] 691 | fn test_nonbox() { 692 | assert_eq!(run_nonbox(false, |r| r()), EXPECTED_STATS); 693 | } 694 | 695 | #[test] 696 | fn test_callerbox() { 697 | assert_eq!(run_callerbox(false, |r| r()), EXPECTED_STATS); 698 | } 699 | 700 | #[test] 701 | fn test_large_nonbox() { 702 | assert_eq!(run_large_nonbox(false, |r| r()), EXPECTED_STATS); 703 | } 704 | 705 | #[test] 706 | fn test_box() { 707 | assert_eq!(run_box(false, |r| r()), EXPECTED_STATS); 708 | } 709 | 710 | #[test] 711 | fn test_box_callerbox() { 712 | assert_eq!(run_box_callerbox(false, |r| r()), EXPECTED_STATS); 713 | } 714 | 715 | #[test] 716 | fn test_large_box() { 717 | assert_eq!(run_large_box(false, |r| r()), EXPECTED_STATS); 718 | } 719 | 720 | #[test] 721 | fn test_box_rc() { 722 | assert_eq!( 723 | run_box_rc(false, BoxRcMode::RcWaker, |r| r()), 724 | EXPECTED_STATS 725 | ); 726 | } 727 | 728 | #[test] 729 | fn test_box_chkrc() { 730 | assert_eq!( 731 | run_box_rc(false, BoxRcMode::CheckedRcWaker, |r| r()), 732 | EXPECTED_STATS 733 | ); 734 | } 735 | 736 | #[test] 737 | fn test_box_arc() { 738 | assert_eq!( 739 | run_box_rc(false, BoxRcMode::ArcWaker, |r| r()), 740 | EXPECTED_STATS 741 | ); 742 | } 743 | } 744 | -------------------------------------------------------------------------------- /src/waker.rs: -------------------------------------------------------------------------------- 1 | // adapted from alloc::task::Wake 2 | 3 | use std::cell::{Cell, RefCell}; 4 | use std::mem::{ManuallyDrop, MaybeUninit}; 5 | use std::pin::Pin; 6 | use std::rc::Rc; 7 | use std::sync::Arc; 8 | use std::task::Wake; 9 | use std::task::{RawWaker, RawWakerVTable, Waker}; 10 | use std::thread::{self, ThreadId}; 11 | 12 | pub trait EmbedWake { 13 | fn wake(&self, task_id: usize); 14 | } 15 | 16 | pub struct EmbedWaker<'a, W> { 17 | refs: Cell, 18 | wake: &'a W, 19 | task_id: usize, 20 | } 21 | 22 | impl<'a, W> EmbedWaker<'a, W> 23 | where 24 | W: EmbedWake + 'a, 25 | { 26 | pub fn new(wake: &'a W, task_id: usize) -> Self { 27 | Self { 28 | refs: Cell::new(1), 29 | wake, 30 | task_id, 31 | } 32 | } 33 | 34 | pub fn ref_count(&self) -> usize { 35 | self.refs.get() 36 | } 37 | 38 | pub fn as_std<'out>( 39 | self: Pin<&mut Self>, 40 | output_mem: &'out mut MaybeUninit, 41 | ) -> &'out Waker { 42 | let s = &*self; 43 | 44 | let rw = RawWaker::new( 45 | s as *const Self as *const (), 46 | &RawWakerVTable::new(Self::clone, Self::wake, Self::wake_by_ref, Self::drop), 47 | ); 48 | 49 | output_mem.write(unsafe { Waker::from_raw(rw) }); 50 | 51 | unsafe { output_mem.assume_init_mut() } 52 | } 53 | 54 | unsafe fn clone(data: *const ()) -> RawWaker { 55 | let s = (data as *const Self).as_ref().unwrap(); 56 | 57 | s.refs.set(s.refs.get() + 1); 58 | 59 | RawWaker::new( 60 | data, 61 | &RawWakerVTable::new(Self::clone, Self::wake, Self::wake_by_ref, Self::drop), 62 | ) 63 | } 64 | 65 | unsafe fn wake(data: *const ()) { 66 | Self::wake_by_ref(data); 67 | 68 | Self::drop(data); 69 | } 70 | 71 | unsafe fn wake_by_ref(data: *const ()) { 72 | let s = (data as *const Self).as_ref().unwrap(); 73 | 74 | s.wake.wake(s.task_id); 75 | } 76 | 77 | unsafe fn drop(data: *const ()) { 78 | let s = (data as *const Self).as_ref().unwrap(); 79 | 80 | let refs = s.refs.get(); 81 | 82 | assert!(refs > 1); 83 | 84 | s.refs.set(refs - 1); 85 | } 86 | } 87 | 88 | pub trait LocalWake { 89 | fn wake(self: Rc); 90 | 91 | fn wake_by_ref(self: &Rc) { 92 | self.clone().wake(); 93 | } 94 | } 95 | 96 | #[inline(always)] 97 | fn is_current_thread(id: ThreadId) -> bool { 98 | thread_local! { 99 | static CURRENT_THREAD: RefCell> = RefCell::new(None); 100 | } 101 | 102 | CURRENT_THREAD.with(|v| id == *v.borrow_mut().get_or_insert_with(|| thread::current().id())) 103 | } 104 | 105 | pub trait CheckedLocalWake { 106 | fn thread_id(self: &Rc) -> ThreadId; 107 | 108 | fn wake(self: Rc); 109 | 110 | fn wake_by_ref(self: &Rc) { 111 | self.clone().wake(); 112 | } 113 | } 114 | 115 | pub fn local_wake_into_std(waker: Rc) -> Waker { 116 | // SAFETY: This is safe because raw_waker safely constructs 117 | // a RawWaker from Rc. 118 | unsafe { Waker::from_raw(raw_waker(waker)) } 119 | } 120 | 121 | pub fn checked_local_wake_into_std(waker: Rc) -> Waker { 122 | // SAFETY: This is safe because raw_waker safely constructs 123 | // a RawWaker from Rc. 124 | unsafe { Waker::from_raw(checked_raw_waker(waker)) } 125 | } 126 | 127 | #[inline(always)] 128 | fn raw_waker(waker: Rc) -> RawWaker { 129 | // Increment the reference count of the rc to clone it. 130 | unsafe fn clone_waker(waker: *const ()) -> RawWaker { 131 | Rc::increment_strong_count(waker as *const W); 132 | RawWaker::new( 133 | waker as *const (), 134 | &RawWakerVTable::new( 135 | clone_waker::, 136 | wake::, 137 | wake_by_ref::, 138 | drop_waker::, 139 | ), 140 | ) 141 | } 142 | 143 | // Wake by value, moving the Rc into the Wake::wake function 144 | unsafe fn wake(waker: *const ()) { 145 | let waker = Rc::from_raw(waker as *const W); 146 | ::wake(waker); 147 | } 148 | 149 | // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it 150 | unsafe fn wake_by_ref(waker: *const ()) { 151 | let waker = ManuallyDrop::new(Rc::from_raw(waker as *const W)); 152 | ::wake_by_ref(&waker); 153 | } 154 | 155 | // Decrement the reference count of the Rc on drop 156 | unsafe fn drop_waker(waker: *const ()) { 157 | Rc::decrement_strong_count(waker as *const W); 158 | } 159 | 160 | RawWaker::new( 161 | Rc::into_raw(waker) as *const (), 162 | &RawWakerVTable::new( 163 | clone_waker::, 164 | wake::, 165 | wake_by_ref::, 166 | drop_waker::, 167 | ), 168 | ) 169 | } 170 | 171 | #[inline(always)] 172 | fn checked_raw_waker(waker: Rc) -> RawWaker { 173 | #[inline(always)] 174 | fn check_thread(waker: &Rc) { 175 | if !is_current_thread(waker.thread_id()) { 176 | panic!("local waker used from another thread"); 177 | } 178 | } 179 | 180 | // Increment the reference count of the rc to clone it. 181 | unsafe fn clone_waker(waker: *const ()) -> RawWaker { 182 | let waker = ManuallyDrop::new(Rc::from_raw(waker as *const W)); 183 | 184 | check_thread(&waker); 185 | 186 | let waker = Rc::clone(&waker); 187 | 188 | RawWaker::new( 189 | Rc::into_raw(waker) as *const (), 190 | &RawWakerVTable::new( 191 | clone_waker::, 192 | wake::, 193 | wake_by_ref::, 194 | drop_waker::, 195 | ), 196 | ) 197 | } 198 | 199 | // Wake by value, moving the Rc into the Wake::wake function 200 | unsafe fn wake(waker: *const ()) { 201 | let waker = Rc::from_raw(waker as *const W); 202 | 203 | check_thread(&waker); 204 | 205 | ::wake(waker); 206 | } 207 | 208 | // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it 209 | unsafe fn wake_by_ref(waker: *const ()) { 210 | let waker = ManuallyDrop::new(Rc::from_raw(waker as *const W)); 211 | 212 | check_thread(&waker); 213 | 214 | ::wake_by_ref(&waker); 215 | } 216 | 217 | // Decrement the reference count of the Rc on drop 218 | unsafe fn drop_waker(waker: *const ()) { 219 | let waker = Rc::from_raw(waker as *const W); 220 | 221 | check_thread(&waker); 222 | } 223 | 224 | RawWaker::new( 225 | Rc::into_raw(waker) as *const (), 226 | &RawWakerVTable::new( 227 | clone_waker::, 228 | wake::, 229 | wake_by_ref::, 230 | drop_waker::, 231 | ), 232 | ) 233 | } 234 | 235 | pub trait WakerFactory { 236 | fn new_waker(&self, inner: T) -> (Waker, Box usize>) 237 | where 238 | T: Send + Sync + Wake + LocalWake + CheckedLocalWake + 'static; 239 | } 240 | 241 | #[derive(Default)] 242 | pub struct RcWakerFactory {} 243 | 244 | impl WakerFactory for RcWakerFactory { 245 | fn new_waker(&self, inner: T) -> (Waker, Box usize>) 246 | where 247 | T: Send + Sync + Wake + LocalWake + CheckedLocalWake + 'static, 248 | { 249 | let r = Rc::new(inner); 250 | 251 | let strong_count = { 252 | let r = Rc::downgrade(&r); 253 | 254 | Box::new(move || std::rc::Weak::strong_count(&r)) 255 | }; 256 | 257 | (local_wake_into_std(r), strong_count) 258 | } 259 | } 260 | 261 | #[derive(Default)] 262 | pub struct CheckedRcWakerFactory {} 263 | 264 | impl WakerFactory for CheckedRcWakerFactory { 265 | fn new_waker(&self, inner: T) -> (Waker, Box usize>) 266 | where 267 | T: Send + Sync + Wake + CheckedLocalWake + 'static, 268 | { 269 | let r = Rc::new(inner); 270 | 271 | let strong_count = { 272 | let r = Rc::downgrade(&r); 273 | 274 | Box::new(move || std::rc::Weak::strong_count(&r)) 275 | }; 276 | 277 | (checked_local_wake_into_std(r), strong_count) 278 | } 279 | } 280 | 281 | #[derive(Default)] 282 | pub struct ArcWakerFactory {} 283 | 284 | impl WakerFactory for ArcWakerFactory { 285 | fn new_waker(&self, inner: T) -> (Waker, Box usize>) 286 | where 287 | T: Send + Sync + Wake + LocalWake + CheckedLocalWake + 'static, 288 | { 289 | let r = Arc::new(inner); 290 | 291 | let strong_count = { 292 | let r = Arc::downgrade(&r); 293 | 294 | Box::new(move || std::sync::Weak::strong_count(&r)) 295 | }; 296 | 297 | (r.into(), strong_count) 298 | } 299 | } 300 | --------------------------------------------------------------------------------