├── .gitignore ├── .vscode └── c_cpp_properties.json ├── Cargo.lock ├── Cargo.toml ├── async_await.zig ├── await.cpp ├── count.cpp ├── count.rs ├── count.zig ├── count2.cpp ├── count3.cpp ├── exec.hpp ├── exec.rs ├── exec.zig ├── exec ├── event_loop.hpp ├── gather.hpp ├── kuro.hpp ├── promise.hpp ├── sleep.hpp ├── task.hpp ├── task_executor.hpp ├── unique_coroutine_handle.hpp ├── unique_fd.hpp └── util.hpp └── fib.zig /.gitignore: -------------------------------------------------------------------------------- 1 | await 2 | count 3 | count2 4 | env.sh 5 | notes.txt 6 | target/ 7 | zig-cache/ 8 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder:kuro}/include", 7 | "${workspaceFolder:libunifex}/build/include", 8 | "${workspaceFolder:libunifex}/include", 9 | "${default}" 10 | ], 11 | "defines": [], 12 | "cStandard": "c17", 13 | "cppStandard": "c++20", 14 | "intelliSenseMode": "linux-clang-x64" 15 | } 16 | ], 17 | "version": 4 18 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "async-attributes" 5 | version = "1.1.2" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" 8 | dependencies = [ 9 | "quote", 10 | "syn", 11 | ] 12 | 13 | [[package]] 14 | name = "async-channel" 15 | version = "1.6.1" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" 18 | dependencies = [ 19 | "concurrent-queue", 20 | "event-listener", 21 | "futures-core", 22 | ] 23 | 24 | [[package]] 25 | name = "async-executor" 26 | version = "1.4.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" 29 | dependencies = [ 30 | "async-task", 31 | "concurrent-queue", 32 | "fastrand", 33 | "futures-lite", 34 | "once_cell", 35 | "slab", 36 | ] 37 | 38 | [[package]] 39 | name = "async-global-executor" 40 | version = "2.0.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" 43 | dependencies = [ 44 | "async-channel", 45 | "async-executor", 46 | "async-io", 47 | "async-mutex", 48 | "blocking", 49 | "futures-lite", 50 | "num_cpus", 51 | "once_cell", 52 | ] 53 | 54 | [[package]] 55 | name = "async-io" 56 | version = "1.6.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" 59 | dependencies = [ 60 | "concurrent-queue", 61 | "futures-lite", 62 | "libc", 63 | "log", 64 | "once_cell", 65 | "parking", 66 | "polling", 67 | "slab", 68 | "socket2", 69 | "waker-fn", 70 | "winapi", 71 | ] 72 | 73 | [[package]] 74 | name = "async-lock" 75 | version = "2.4.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" 78 | dependencies = [ 79 | "event-listener", 80 | ] 81 | 82 | [[package]] 83 | name = "async-mutex" 84 | version = "1.4.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" 87 | dependencies = [ 88 | "event-listener", 89 | ] 90 | 91 | [[package]] 92 | name = "async-std" 93 | version = "1.9.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "d9f06685bad74e0570f5213741bea82158279a4103d988e57bfada11ad230341" 96 | dependencies = [ 97 | "async-attributes", 98 | "async-channel", 99 | "async-global-executor", 100 | "async-io", 101 | "async-lock", 102 | "crossbeam-utils", 103 | "futures-channel", 104 | "futures-core", 105 | "futures-io", 106 | "futures-lite", 107 | "gloo-timers", 108 | "kv-log-macro", 109 | "log", 110 | "memchr", 111 | "num_cpus", 112 | "once_cell", 113 | "pin-project-lite", 114 | "pin-utils", 115 | "slab", 116 | "wasm-bindgen-futures", 117 | ] 118 | 119 | [[package]] 120 | name = "async-task" 121 | version = "4.0.3" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" 124 | 125 | [[package]] 126 | name = "atomic-waker" 127 | version = "1.0.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 130 | 131 | [[package]] 132 | name = "autocfg" 133 | version = "1.0.1" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 136 | 137 | [[package]] 138 | name = "blocking" 139 | version = "1.0.2" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" 142 | dependencies = [ 143 | "async-channel", 144 | "async-task", 145 | "atomic-waker", 146 | "fastrand", 147 | "futures-lite", 148 | "once_cell", 149 | ] 150 | 151 | [[package]] 152 | name = "bumpalo" 153 | version = "3.7.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 156 | 157 | [[package]] 158 | name = "cache-padded" 159 | version = "1.1.1" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" 162 | 163 | [[package]] 164 | name = "cc" 165 | version = "1.0.69" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" 168 | 169 | [[package]] 170 | name = "cfg-if" 171 | version = "1.0.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 174 | 175 | [[package]] 176 | name = "chrono" 177 | version = "0.4.19" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 180 | dependencies = [ 181 | "libc", 182 | "num-integer", 183 | "num-traits", 184 | "time", 185 | "winapi", 186 | ] 187 | 188 | [[package]] 189 | name = "concurrent-queue" 190 | version = "1.2.2" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" 193 | dependencies = [ 194 | "cache-padded", 195 | ] 196 | 197 | [[package]] 198 | name = "crossbeam-utils" 199 | version = "0.8.5" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 202 | dependencies = [ 203 | "cfg-if", 204 | "lazy_static", 205 | ] 206 | 207 | [[package]] 208 | name = "ctor" 209 | version = "0.1.20" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" 212 | dependencies = [ 213 | "quote", 214 | "syn", 215 | ] 216 | 217 | [[package]] 218 | name = "event-listener" 219 | version = "2.5.1" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" 222 | 223 | [[package]] 224 | name = "exec" 225 | version = "0.1.0" 226 | dependencies = [ 227 | "async-std", 228 | "chrono", 229 | "futures", 230 | ] 231 | 232 | [[package]] 233 | name = "fastrand" 234 | version = "1.4.1" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" 237 | dependencies = [ 238 | "instant", 239 | ] 240 | 241 | [[package]] 242 | name = "futures" 243 | version = "0.3.13" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" 246 | dependencies = [ 247 | "futures-channel", 248 | "futures-core", 249 | "futures-executor", 250 | "futures-io", 251 | "futures-sink", 252 | "futures-task", 253 | "futures-util", 254 | ] 255 | 256 | [[package]] 257 | name = "futures-channel" 258 | version = "0.3.13" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" 261 | dependencies = [ 262 | "futures-core", 263 | "futures-sink", 264 | ] 265 | 266 | [[package]] 267 | name = "futures-core" 268 | version = "0.3.13" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" 271 | 272 | [[package]] 273 | name = "futures-executor" 274 | version = "0.3.13" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" 277 | dependencies = [ 278 | "futures-core", 279 | "futures-task", 280 | "futures-util", 281 | ] 282 | 283 | [[package]] 284 | name = "futures-io" 285 | version = "0.3.13" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" 288 | 289 | [[package]] 290 | name = "futures-lite" 291 | version = "1.12.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" 294 | dependencies = [ 295 | "fastrand", 296 | "futures-core", 297 | "futures-io", 298 | "memchr", 299 | "parking", 300 | "pin-project-lite", 301 | "waker-fn", 302 | ] 303 | 304 | [[package]] 305 | name = "futures-macro" 306 | version = "0.3.13" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" 309 | dependencies = [ 310 | "proc-macro-hack", 311 | "proc-macro2", 312 | "quote", 313 | "syn", 314 | ] 315 | 316 | [[package]] 317 | name = "futures-sink" 318 | version = "0.3.13" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" 321 | 322 | [[package]] 323 | name = "futures-task" 324 | version = "0.3.13" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" 327 | 328 | [[package]] 329 | name = "futures-util" 330 | version = "0.3.13" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" 333 | dependencies = [ 334 | "futures-channel", 335 | "futures-core", 336 | "futures-io", 337 | "futures-macro", 338 | "futures-sink", 339 | "futures-task", 340 | "memchr", 341 | "pin-project-lite", 342 | "pin-utils", 343 | "proc-macro-hack", 344 | "proc-macro-nested", 345 | "slab", 346 | ] 347 | 348 | [[package]] 349 | name = "gloo-timers" 350 | version = "0.2.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" 353 | dependencies = [ 354 | "futures-channel", 355 | "futures-core", 356 | "js-sys", 357 | "wasm-bindgen", 358 | "web-sys", 359 | ] 360 | 361 | [[package]] 362 | name = "hermit-abi" 363 | version = "0.1.19" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 366 | dependencies = [ 367 | "libc", 368 | ] 369 | 370 | [[package]] 371 | name = "instant" 372 | version = "0.1.10" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" 375 | dependencies = [ 376 | "cfg-if", 377 | ] 378 | 379 | [[package]] 380 | name = "js-sys" 381 | version = "0.3.51" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 384 | dependencies = [ 385 | "wasm-bindgen", 386 | ] 387 | 388 | [[package]] 389 | name = "kv-log-macro" 390 | version = "1.0.7" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 393 | dependencies = [ 394 | "log", 395 | ] 396 | 397 | [[package]] 398 | name = "lazy_static" 399 | version = "1.4.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 402 | 403 | [[package]] 404 | name = "libc" 405 | version = "0.2.97" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" 408 | 409 | [[package]] 410 | name = "log" 411 | version = "0.4.14" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 414 | dependencies = [ 415 | "cfg-if", 416 | "value-bag", 417 | ] 418 | 419 | [[package]] 420 | name = "memchr" 421 | version = "2.3.4" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 424 | 425 | [[package]] 426 | name = "num-integer" 427 | version = "0.1.44" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 430 | dependencies = [ 431 | "autocfg", 432 | "num-traits", 433 | ] 434 | 435 | [[package]] 436 | name = "num-traits" 437 | version = "0.2.14" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 440 | dependencies = [ 441 | "autocfg", 442 | ] 443 | 444 | [[package]] 445 | name = "num_cpus" 446 | version = "1.13.0" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 449 | dependencies = [ 450 | "hermit-abi", 451 | "libc", 452 | ] 453 | 454 | [[package]] 455 | name = "once_cell" 456 | version = "1.8.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 459 | 460 | [[package]] 461 | name = "parking" 462 | version = "2.0.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" 465 | 466 | [[package]] 467 | name = "pin-project-lite" 468 | version = "0.2.6" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 471 | 472 | [[package]] 473 | name = "pin-utils" 474 | version = "0.1.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 477 | 478 | [[package]] 479 | name = "polling" 480 | version = "2.1.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" 483 | dependencies = [ 484 | "cfg-if", 485 | "libc", 486 | "log", 487 | "wepoll-ffi", 488 | "winapi", 489 | ] 490 | 491 | [[package]] 492 | name = "proc-macro-hack" 493 | version = "0.5.19" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 496 | 497 | [[package]] 498 | name = "proc-macro-nested" 499 | version = "0.1.7" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" 502 | 503 | [[package]] 504 | name = "proc-macro2" 505 | version = "1.0.26" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" 508 | dependencies = [ 509 | "unicode-xid", 510 | ] 511 | 512 | [[package]] 513 | name = "quote" 514 | version = "1.0.9" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 517 | dependencies = [ 518 | "proc-macro2", 519 | ] 520 | 521 | [[package]] 522 | name = "slab" 523 | version = "0.4.2" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 526 | 527 | [[package]] 528 | name = "socket2" 529 | version = "0.4.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" 532 | dependencies = [ 533 | "libc", 534 | "winapi", 535 | ] 536 | 537 | [[package]] 538 | name = "syn" 539 | version = "1.0.68" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" 542 | dependencies = [ 543 | "proc-macro2", 544 | "quote", 545 | "unicode-xid", 546 | ] 547 | 548 | [[package]] 549 | name = "time" 550 | version = "0.1.44" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 553 | dependencies = [ 554 | "libc", 555 | "wasi", 556 | "winapi", 557 | ] 558 | 559 | [[package]] 560 | name = "unicode-xid" 561 | version = "0.2.1" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 564 | 565 | [[package]] 566 | name = "value-bag" 567 | version = "1.0.0-alpha.7" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae" 570 | dependencies = [ 571 | "ctor", 572 | "version_check", 573 | ] 574 | 575 | [[package]] 576 | name = "version_check" 577 | version = "0.9.3" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 580 | 581 | [[package]] 582 | name = "waker-fn" 583 | version = "1.1.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 586 | 587 | [[package]] 588 | name = "wasi" 589 | version = "0.10.0+wasi-snapshot-preview1" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 592 | 593 | [[package]] 594 | name = "wasm-bindgen" 595 | version = "0.2.74" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 598 | dependencies = [ 599 | "cfg-if", 600 | "wasm-bindgen-macro", 601 | ] 602 | 603 | [[package]] 604 | name = "wasm-bindgen-backend" 605 | version = "0.2.74" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 608 | dependencies = [ 609 | "bumpalo", 610 | "lazy_static", 611 | "log", 612 | "proc-macro2", 613 | "quote", 614 | "syn", 615 | "wasm-bindgen-shared", 616 | ] 617 | 618 | [[package]] 619 | name = "wasm-bindgen-futures" 620 | version = "0.4.24" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" 623 | dependencies = [ 624 | "cfg-if", 625 | "js-sys", 626 | "wasm-bindgen", 627 | "web-sys", 628 | ] 629 | 630 | [[package]] 631 | name = "wasm-bindgen-macro" 632 | version = "0.2.74" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 635 | dependencies = [ 636 | "quote", 637 | "wasm-bindgen-macro-support", 638 | ] 639 | 640 | [[package]] 641 | name = "wasm-bindgen-macro-support" 642 | version = "0.2.74" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 645 | dependencies = [ 646 | "proc-macro2", 647 | "quote", 648 | "syn", 649 | "wasm-bindgen-backend", 650 | "wasm-bindgen-shared", 651 | ] 652 | 653 | [[package]] 654 | name = "wasm-bindgen-shared" 655 | version = "0.2.74" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 658 | 659 | [[package]] 660 | name = "web-sys" 661 | version = "0.3.51" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 664 | dependencies = [ 665 | "js-sys", 666 | "wasm-bindgen", 667 | ] 668 | 669 | [[package]] 670 | name = "wepoll-ffi" 671 | version = "0.1.2" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" 674 | dependencies = [ 675 | "cc", 676 | ] 677 | 678 | [[package]] 679 | name = "winapi" 680 | version = "0.3.9" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 683 | dependencies = [ 684 | "winapi-i686-pc-windows-gnu", 685 | "winapi-x86_64-pc-windows-gnu", 686 | ] 687 | 688 | [[package]] 689 | name = "winapi-i686-pc-windows-gnu" 690 | version = "0.4.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 693 | 694 | [[package]] 695 | name = "winapi-x86_64-pc-windows-gnu" 696 | version = "0.4.0" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 699 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exec" 3 | version = "0.1.0" 4 | authors = ["Tom "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | async-std = { version = "1.9.0", features = ["attributes"] } 9 | chrono = "0.4" 10 | futures = "0.3" 11 | 12 | [[bin]] 13 | name = "count" 14 | path = "count.rs" 15 | -------------------------------------------------------------------------------- /async_await.zig: -------------------------------------------------------------------------------- 1 | // Async Await in 60 LOC by MasterQ32 2 | // https://gist.github.com/MasterQ32/ff534f912c5faedbbb57974317e79778 3 | 4 | const std = @import("std"); 5 | 6 | // usage: 7 | fn asyncMain() !void { 8 | // Start two interleaving tasks 9 | var task_a = async waitUntilAndPrint(start + 1000, start + 1200, "task a"); 10 | var task_b = async waitUntilAndPrint(start + 500, start + 1300, "task b"); 11 | 12 | await task_a; 13 | await task_b; 14 | } 15 | 16 | fn waitUntilAndPrint(time1: i64, time2: i64, name: []const u8) void { 17 | waitForTime(time1); 18 | std.log.info("[{s}] it is now {} ms since start!", .{ name, std.time.milliTimestamp() - start }); 19 | 20 | waitForTime(time2); 21 | std.log.info("[{s}] it is now {} ms since start!", .{ name, std.time.milliTimestamp() - start }); 22 | } 23 | 24 | // impementation 25 | const Task = struct { frame: anyframe, time: i64 }; 26 | var task_list = [1]?Task{null} ** 10; 27 | var start: i64 = 0; 28 | 29 | fn waitForTime(time: i64) void { 30 | suspend { 31 | // append the task into a list structure "tasks to be resumed later" and store the @frame() 32 | // as well as a condition (in this case: time) 33 | const slot = for (task_list) |*task| { 34 | if (task.* == null) break task; 35 | } else unreachable; 36 | slot.* = Task{ .frame = @frame(), .time = time }; 37 | } 38 | } 39 | 40 | pub fn main() !void { 41 | start = std.time.milliTimestamp(); 42 | 43 | var main_task = async asyncMain(); 44 | 45 | while (true) // this is "the event loop" 46 | { 47 | const now = std.time.milliTimestamp(); 48 | 49 | var any = false; // store if we have any tasks left 50 | for (task_list) |*task| { 51 | if (task.* != null) { 52 | any = true; 53 | if (task.*.?.time <= now) { // resume condition 54 | var frame = task.*.?.frame; // resume location 55 | task.* = null; // make task slot available again 56 | resume frame; 57 | } 58 | } 59 | } 60 | if (!any) break; // all tasks done, we can now exit the program 61 | } 62 | 63 | // finalize the tasks properly (we know they finished as they are no more tasks left) 64 | // but main might have returned a error! 65 | try nosuspend await main_task; 66 | } 67 | -------------------------------------------------------------------------------- /await.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | requires unifex::scheduler 9 | auto sleep(Scheduler scheduler, int n) -> unifex::task { 10 | using namespace std::chrono_literals; 11 | for (size_t i = 0; i < n; i += 1) { 12 | co_await unifex::schedule_after(scheduler, 300ms); 13 | } 14 | } 15 | 16 | template 17 | requires unifex::scheduler 18 | auto await_sleep(Scheduler scheduler) -> unifex::task { 19 | auto task1 = sleep(scheduler, 1); 20 | co_await std::move(task1); 21 | co_await unifex::when_all(sleep(scheduler, 2), sleep(scheduler, 1)); 22 | } 23 | 24 | auto main() -> int { 25 | unifex::timed_single_thread_context context; 26 | auto scheduler = context.get_scheduler(); 27 | // auto task = sleep(scheduler); 28 | auto task = await_sleep(scheduler); 29 | unifex::sync_wait(std::move(task)); 30 | } 31 | -------------------------------------------------------------------------------- /count.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | auto report(Values... values) { 8 | std::cout << std::this_thread::get_id(); 9 | ((std::cout << " " << values), ...); 10 | std::cout << std::endl; 11 | } 12 | 13 | // #include "exec/kuro.hpp" 14 | // #include "exec.hpp" 15 | 16 | // namespace kuro = exec; 17 | 18 | auto count(size_t n, double interval) -> kuro::task { 19 | report("before loop", interval); 20 | auto start = std::chrono::high_resolution_clock::now(); 21 | for (size_t i = 0; i < n; i += 1) { 22 | co_await kuro::sleep_for(interval); 23 | report("slept", interval); 24 | } 25 | auto elapsed = std::chrono::high_resolution_clock::now() - start; 26 | co_return elapsed.count() * 1e-9; 27 | } 28 | 29 | auto run() -> kuro::task { 30 | report("begin"); 31 | auto task1 = count(2, 1.0); 32 | auto task2 = count(3, 0.6); 33 | report("count size:", sizeof(task1)); 34 | auto [elapsed1, elapsed2] = co_await kuro::gather(task1, task2); 35 | report("end"); 36 | co_return elapsed1 + elapsed2; 37 | } 38 | 39 | auto main() -> int { 40 | auto total = kuro::event_loop::run(run()); 41 | report("total:", total); 42 | } 43 | -------------------------------------------------------------------------------- /count.rs: -------------------------------------------------------------------------------- 1 | pub mod exec; 2 | 3 | use std::time::{Duration, Instant}; 4 | 5 | #[macro_export] 6 | macro_rules! report { 7 | ($($arg:tt)*) => { 8 | print!("{:?} ", std::thread::current().id()); 9 | println!($($arg)*); 10 | } 11 | } 12 | 13 | async fn count(n: usize, interval: f64) -> f64 { 14 | let start = Instant::now(); 15 | use async_std::task::sleep; 16 | // use exec::sleep; 17 | report!("before loop {}", interval); 18 | for _ in 0..n { 19 | sleep(Duration::from_secs_f64(interval)).await; 20 | report!("slept {}", interval); 21 | } 22 | return start.elapsed().as_secs_f64(); 23 | } 24 | 25 | async fn run() -> f64 { 26 | report!("begin"); 27 | let futures = vec![count(2, 1.0), count(3, 0.6)]; 28 | report!("count size: {}", std::mem::size_of_val(&futures[0])); 29 | let total = futures::future::join_all(futures).await.iter().sum::(); 30 | report!("end"); 31 | total 32 | } 33 | 34 | #[async_std::main] 35 | async fn main() { 36 | let total = run().await; 37 | report!("total: {}", total); 38 | } 39 | 40 | // fn main() { 41 | // use async_std::task::block_on; 42 | // // use exec::block_on; 43 | // let future = run(); 44 | // report!("run size: {}", std::mem::size_of_val(&future)); 45 | // let total = block_on(future); 46 | // report!("total: {}", total); 47 | // // println!("--------------"); 48 | // // let total = async_std::task::block_on(run()); 49 | // // report!("total: {}", total); 50 | // } 51 | -------------------------------------------------------------------------------- /count.zig: -------------------------------------------------------------------------------- 1 | // See: 2 | // https://github.com/ziglang/zig/blob/c6844072ce440f581787bf97909261084a9edc6c/lib/std/io.zig#L8 3 | // https://github.com/ziglang/zig/blob/ade85471e2cdab466ba685a38c2c7949c9dd1632/lib/std/start.zig#L428 4 | pub const io_mode = .evented; 5 | 6 | const std = @import("std"); 7 | const exec = @import("./exec.zig"); 8 | 9 | pub fn report(comptime format: []const u8, args: anytype) void { 10 | std.debug.print("{} ", .{std.Thread.getCurrentId()}); 11 | std.debug.print(format, args); 12 | std.debug.print("\n", .{}); 13 | } 14 | 15 | fn timerSeconds(timer: std.time.Timer) f64 { 16 | return @intToFloat(f64, timer.read()) / @intToFloat(f64, std.time.ns_per_s); 17 | } 18 | 19 | fn count(n: usize, interval: f64) f64 { 20 | const timer = std.time.Timer.start() catch unreachable; 21 | // const sleep = std.event.Loop.instance.?.sleep; 22 | const sleep = std.time.sleep; 23 | // const sleep = exec.sleep; 24 | report("before loop {}", .{interval}); 25 | var i: usize = 0; 26 | const wait_ns = @floatToInt(u64, interval * std.time.ns_per_s); 27 | while (i < n) : (i += 1) { 28 | sleep(wait_ns); 29 | report("slept {}", .{interval}); 30 | } 31 | return timerSeconds(timer); 32 | } 33 | 34 | fn run() f64 { 35 | report("begin", .{}); 36 | var frames = [_]@Frame(count){ 37 | async count(2, 1.0), 38 | async count(3, 0.6), 39 | }; 40 | report("count size: {}", .{@sizeOf(@TypeOf(frames[0]))}); 41 | var total = @as(f64, 0); 42 | for (frames) |*frame| { 43 | total += await frame; 44 | } 45 | report("end", .{}); 46 | return total; 47 | } 48 | 49 | pub fn main() !void { 50 | // _ = await async hi(); 51 | // var task = async run(); 52 | // report("run size: {}", .{@sizeOf(@TypeOf(task))}); 53 | // exec.runLoop(false); 54 | // const total = await task; 55 | // _ = try std.Thread.spawn(exec.runLoop, true); 56 | const total = run(); 57 | report("total: {}", .{total}); 58 | } 59 | 60 | // fn hi() f64 { 61 | // return 1.0; 62 | // } 63 | -------------------------------------------------------------------------------- /count2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | auto report(Values... values) { 13 | std::cout << std::this_thread::get_id(); 14 | ((std::cout << " " << values), ...); 15 | std::cout << std::endl; 16 | } 17 | 18 | struct EPollContext { 19 | ~EPollContext() { 20 | stop_source.request_stop(); 21 | thread.join(); 22 | } 23 | 24 | auto get_scheduler() { return context.get_scheduler(); } 25 | 26 | private: 27 | unifex::linuxos::io_epoll_context context; 28 | unifex::inplace_stop_source stop_source; 29 | std::thread thread{[&] { context.run(stop_source.get_token()); }}; 30 | }; 31 | 32 | template 33 | requires unifex::scheduler 34 | auto count(Scheduler scheduler, int n, double interval) 35 | -> unifex::task { 36 | report("before loop", interval); 37 | auto start = std::chrono::high_resolution_clock::now(); 38 | auto duration = std::chrono::nanoseconds(std::int64_t(interval * 1e9)); 39 | for (size_t i = 0; i < n; i += 1) { 40 | // co_await scheduler.schedule_at(unifex::now(scheduler) + duration); 41 | co_await unifex::schedule_after(scheduler, duration); 42 | report("slept", interval); 43 | } 44 | auto elapsed = std::chrono::high_resolution_clock::now() - start; 45 | co_return elapsed.count() * 1e-9; 46 | } 47 | 48 | template 49 | auto get(std::variant> wrapped) -> Value { 50 | return std::get<0>(std::get>(wrapped)); 51 | } 52 | 53 | template 54 | requires unifex::scheduler 55 | auto run(Scheduler scheduler) -> unifex::task { 56 | report("begin"); 57 | auto task1 = count(scheduler, 2, 1.0); 58 | auto task2 = count(scheduler, 3, 0.6); 59 | report("count size:", sizeof(task1)); 60 | // See also: https://github.com/facebookexperimental/libunifex/issues/251 61 | auto [elapsed1, elapsed2] = 62 | co_await unifex::when_all(std::move(task1), std::move(task2)); 63 | report("end"); 64 | co_return get(elapsed1) + get(elapsed2); 65 | } 66 | 67 | auto main() -> int { 68 | // auto context = EPollContext{}; 69 | auto context = unifex::timed_single_thread_context{}; 70 | auto scheduler = context.get_scheduler(); 71 | auto task = run(scheduler); 72 | auto total = unifex::sync_wait(std::move(task)); 73 | report("total:", *total); 74 | } 75 | -------------------------------------------------------------------------------- /count3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // #include 4 | #include 5 | 6 | // #include "exec/kuro.hpp" 7 | #include "exec.hpp" 8 | 9 | namespace kuro = exec; 10 | 11 | auto thread_id() { return std::this_thread::get_id(); } 12 | 13 | auto count(size_t n, double interval) -> kuro::task { 14 | std::cout << thread_id() << " before loop " << interval << std::endl; 15 | auto start = std::chrono::high_resolution_clock::now(); 16 | for (size_t i = 0; i < n; i += 1) { 17 | co_await kuro::sleep_for(interval); 18 | std::cout << thread_id() << " slept " << interval << std::endl; 19 | } 20 | auto elapsed = std::chrono::high_resolution_clock::now() - start; 21 | co_return elapsed.count() * 1e-9; 22 | } 23 | 24 | auto run() -> kuro::task { 25 | auto task1 = count(2, 0.25); 26 | // auto task2 = count(3, 0.2); 27 | auto elapsed = co_await task1; 28 | co_return elapsed; 29 | } 30 | 31 | auto main() -> int { 32 | auto total = kuro::event_loop::run(run()); 33 | std::cout << thread_id() << " total: " << total << "\n"; 34 | } 35 | -------------------------------------------------------------------------------- /exec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace exec { 10 | 11 | using TimePoint = std::chrono::time_point; 12 | 13 | struct SleepHandle { 14 | TimePoint end; 15 | std::coroutine_handle<> handle; 16 | }; 17 | 18 | auto sleeps = std::vector{}; 19 | 20 | auto sleep_ready(TimePoint end) -> bool { 21 | return std::chrono::steady_clock::now() >= end; 22 | } 23 | 24 | template 25 | struct Task { 26 | struct promise_type { 27 | Value value; 28 | std::coroutine_handle<> parent; 29 | 30 | auto get_return_object() -> std::coroutine_handle { 31 | return std::coroutine_handle::from_promise(*this); 32 | } 33 | 34 | auto final_suspend() noexcept { 35 | struct Awaitable { 36 | auto await_ready() noexcept -> bool { return false; } 37 | auto await_resume() noexcept {} 38 | auto await_suspend(std::coroutine_handle handle) noexcept 39 | -> std::coroutine_handle<> { 40 | auto parent = handle.promise().parent; 41 | return parent ? parent : std::noop_coroutine(); 42 | } 43 | }; 44 | return Awaitable{}; 45 | } 46 | 47 | auto initial_suspend() -> std::suspend_never { return {}; } 48 | auto return_value(Value value) -> void { this->value = value; } 49 | auto unhandled_exception() -> void {} 50 | }; 51 | 52 | std::coroutine_handle handle; 53 | 54 | auto await_ready() -> bool { return handle.done(); } 55 | auto await_resume() -> Value { return handle.promise().value; } 56 | auto await_suspend(std::coroutine_handle<> parent) { 57 | handle.promise().parent = parent; 58 | } 59 | }; 60 | 61 | template 62 | using task = Task; 63 | 64 | struct Sleep { 65 | TimePoint end; 66 | 67 | auto await_ready() -> bool { return sleep_ready(end); } 68 | auto await_resume() {} 69 | auto await_suspend(std::coroutine_handle<> handle) { 70 | sleeps.push_back({.end = end, .handle = handle}); 71 | } 72 | }; 73 | 74 | auto sleep_for(double seconds) -> Sleep { 75 | using namespace std; 76 | auto duration = chrono::nanoseconds(int64_t(seconds * 1e9)); 77 | return {.end = chrono::steady_clock::now() + duration}; 78 | } 79 | 80 | namespace event_loop { 81 | 82 | template 83 | auto run(Task root) -> Value { 84 | report("looping events y'all"); 85 | while (sleeps.size()) { 86 | for (auto sleep = sleeps.begin(); sleep < sleeps.end(); sleep += 1) { 87 | if (sleep_ready(sleep->end)) { 88 | report("sleep over y'all"); 89 | // Remove first because sleeps might be invalidated after resume. 90 | sleeps.erase(sleep); 91 | sleep->handle.resume(); 92 | // With invalidated iters, just sloppily wait for the next pass. 93 | break; 94 | } 95 | } 96 | } 97 | auto result = root.handle.promise().value; 98 | root.handle.destroy(); 99 | return result; 100 | } 101 | 102 | } // namespace event_loop 103 | 104 | } // namespace exec 105 | -------------------------------------------------------------------------------- /exec.rs: -------------------------------------------------------------------------------- 1 | // Mostly from: 2 | // https://rust-lang.github.io/async-book/02_execution/01_chapter.html 3 | 4 | use { 5 | crate::report, 6 | futures::{ 7 | future::{BoxFuture, Future, FutureExt}, 8 | task::{waker_ref, ArcWake}, 9 | }, 10 | std::{ 11 | pin::Pin, 12 | sync::mpsc::{sync_channel, Receiver, SyncSender}, 13 | sync::{Arc, Mutex}, 14 | task::{Context, Poll, Waker}, 15 | thread, 16 | time::Duration, 17 | }, 18 | }; 19 | 20 | pub fn block_on(future: F) -> Output 21 | where 22 | F: Future + 'static + Send, 23 | { 24 | let (executor, spawner) = new_executor_and_spawner(); 25 | spawner.spawn(future); 26 | // Drop the spawner so that our executor knows it has all the tasks. 27 | drop(spawner); 28 | // Run the executor until the task queue is empty. 29 | let value = executor.run(); 30 | value.unwrap() 31 | } 32 | 33 | pub fn sleep(duration: Duration) -> SleepFuture { 34 | SleepFuture::new(duration) 35 | } 36 | 37 | pub struct SleepFuture { 38 | shared_state: Arc>, 39 | } 40 | 41 | impl SleepFuture { 42 | pub fn new(duration: Duration) -> Self { 43 | let shared_state = Arc::new(Mutex::new(SharedState { 44 | completed: false, 45 | waker: None, 46 | })); 47 | let thread_shared_state = shared_state.clone(); 48 | thread::spawn(move || { 49 | thread::sleep(duration); 50 | let mut shared_state = thread_shared_state.lock().unwrap(); 51 | shared_state.completed = true; 52 | if let Some(waker) = shared_state.waker.take() { 53 | report!("sleep over y'all"); 54 | waker.wake() 55 | } 56 | }); 57 | SleepFuture { shared_state } 58 | } 59 | } 60 | 61 | struct SharedState { 62 | completed: bool, 63 | waker: Option, 64 | } 65 | 66 | impl Future for SleepFuture { 67 | type Output = (); 68 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 69 | let mut shared_state = self.shared_state.lock().unwrap(); 70 | if shared_state.completed { 71 | // report!("poll ready {:p}", &shared_state.completed); 72 | Poll::Ready(()) 73 | } else { 74 | // report!("poll pending {:p}", &shared_state.completed); 75 | shared_state.waker = Some(cx.waker().clone()); 76 | Poll::Pending 77 | } 78 | } 79 | } 80 | 81 | pub struct Executor { 82 | ready_queue: Receiver>>, 83 | } 84 | 85 | #[derive(Clone)] 86 | pub struct Spawner { 87 | task_sender: SyncSender>>, 88 | } 89 | 90 | struct Task { 91 | future: Mutex>>, 92 | task_sender: SyncSender>>, 93 | } 94 | 95 | pub fn new_executor_and_spawner() -> (Executor, Spawner) { 96 | // Maximum number of tasks to allow queueing in the channel at once. 97 | // This is just to make `sync_channel` happy, and wouldn't be present in 98 | // a real executor. 99 | const MAX_QUEUED_TASKS: usize = 10_000; 100 | let (task_sender, ready_queue) = sync_channel(MAX_QUEUED_TASKS); 101 | (Executor { ready_queue }, Spawner { task_sender }) 102 | } 103 | 104 | impl Spawner { 105 | pub fn spawn(&self, future: impl Future + 'static + Send) { 106 | let future = future.boxed(); 107 | let task = Arc::new(Task { 108 | future: Mutex::new(Some(future)), 109 | task_sender: self.task_sender.clone(), 110 | }); 111 | self.task_sender.send(task).expect("too many tasks queued"); 112 | } 113 | } 114 | 115 | impl ArcWake for Task { 116 | fn wake_by_ref(arc_self: &Arc) { 117 | let cloned = arc_self.clone(); 118 | arc_self 119 | .task_sender 120 | .send(cloned) 121 | .expect("too many tasks queued"); 122 | } 123 | } 124 | 125 | impl Executor { 126 | pub fn run(&self) -> Option { 127 | report!("looping events y'all"); 128 | while let Ok(task) = self.ready_queue.recv() { 129 | let mut future_slot = task.future.lock().unwrap(); 130 | if let Some(mut future) = future_slot.take() { 131 | let waker = waker_ref(&task); 132 | let context = &mut Context::from_waker(&*waker); 133 | // `BoxFuture` is a type alias for 134 | // `Pin + Send + 'static>>`. 135 | // We can get a `Pin<&mut dyn Future + Send + 'static>` 136 | // from it by calling the `Pin::as_mut` method. 137 | match future.as_mut().poll(context) { 138 | Poll::Pending => *future_slot = Some(future), 139 | Poll::Ready(value) => return Some(value), 140 | } 141 | } 142 | } 143 | None 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /exec.zig: -------------------------------------------------------------------------------- 1 | const report = @import("root").report; 2 | const std = @import("std"); 3 | 4 | const Task = struct { frame: anyframe, time: i128 }; 5 | var task_list = [1]?Task{null} ** 10; 6 | var task_mutex = std.Thread.Mutex{}; 7 | 8 | pub fn runLoop(endless: bool) void { 9 | report("looping events y'all", .{}); 10 | while (true) { 11 | const lock = task_mutex.acquire(); 12 | const now = std.time.nanoTimestamp(); 13 | var any = false; 14 | var frame = for (task_list) |*task| { 15 | if (task.* != null) { 16 | any = true; 17 | if (task.*.?.time <= now) { 18 | report("sleep over y'all", .{}); 19 | var frame = task.*.?.frame; 20 | task.* = null; 21 | break frame; 22 | } 23 | } 24 | } else null; 25 | lock.release(); 26 | if (frame != null) resume frame.?; 27 | if (!(endless or any)) break; 28 | } 29 | } 30 | 31 | pub fn sleep(ns: u64) void { 32 | suspend { 33 | const lock = task_mutex.acquire(); 34 | defer lock.release(); 35 | const slot = findSlot(); 36 | const time = std.time.nanoTimestamp() + ns; 37 | slot.* = Task{ .frame = @frame(), .time = time }; 38 | } 39 | } 40 | 41 | fn findSlot() *?Task { 42 | return for (task_list) |*task| { 43 | if (task.* == null) break task; 44 | } else unreachable; 45 | } 46 | -------------------------------------------------------------------------------- /exec/event_loop.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "promise.hpp" 14 | #include "task.hpp" 15 | #include "task_executor.hpp" 16 | #include "unique_fd.hpp" 17 | #include "util.hpp" 18 | 19 | namespace kuro { 20 | 21 | class event_loop { 22 | public: 23 | template 24 | static T run(task root_task) { 25 | auto exec = [](task& t) -> detail::task_executor_return { 26 | co_return co_await t; 27 | }(root_task); 28 | io_loop(root_task); 29 | if constexpr (std::is_void_v) { 30 | exec.handle.destroy(); 31 | } else { 32 | T result = exec.handle.promise().result(); 33 | exec.handle.destroy(); 34 | return result; 35 | } 36 | } 37 | 38 | static void add_reader(int fd, std::coroutine_handle<> handle) { 39 | epoll_event ev{.events = EPOLLIN, .data = {.fd = fd}}; 40 | epoll_ctl(instance().m_epoll_fd.get(), EPOLL_CTL_ADD, fd, &ev); 41 | instance().m_handles[fd] = handle; 42 | } 43 | 44 | static void remove_fd(int fd) { 45 | instance().m_handles.erase(fd); 46 | epoll_ctl(instance().m_epoll_fd.get(), EPOLL_CTL_DEL, fd, nullptr); 47 | } 48 | 49 | private: 50 | event_loop() { 51 | m_epoll_fd = epoll_create1(0); 52 | m_events.resize(32); 53 | } 54 | 55 | template 56 | static void io_loop(const task& root_task) { 57 | while (!root_task.done()) { 58 | int n_ev = 59 | epoll_wait(instance().m_epoll_fd.get(), instance().m_events.data(), 60 | instance().m_events.size(), -1); 61 | for (int i = 0; i < n_ev; ++i) { 62 | int fd = instance().m_events[i].data.fd; 63 | auto h = instance().m_handles[fd]; 64 | remove_fd(fd); 65 | h.resume(); 66 | } 67 | } 68 | } 69 | 70 | static event_loop& instance() { 71 | static event_loop inst; 72 | return inst; 73 | } 74 | 75 | detail::unique_fd m_epoll_fd; 76 | std::vector m_events; 77 | std::unordered_map> m_handles; 78 | }; 79 | 80 | } // namespace kuro 81 | -------------------------------------------------------------------------------- /exec/gather.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "util.hpp" 7 | 8 | namespace kuro { 9 | 10 | namespace detail { 11 | 12 | template 13 | class gather_impl { 14 | public: 15 | gather_impl(T... args) 16 | : m_await(detail::awaitable_container(std::forward(args))...) {} 17 | 18 | bool await_ready() noexcept { 19 | std::size_t n_ready = 0; 20 | constexpr_for<0UL, std::tuple_size_v, 1UL>( 21 | [this, n_ready](auto i) mutable { 22 | n_ready += std::get(m_await).get().await_ready(); 23 | }); 24 | 25 | return n_ready == std::tuple_size_v; 26 | } 27 | 28 | void await_suspend(std::coroutine_handle<> handle) noexcept { 29 | std::coroutine_handle<> inc_handle = 30 | [this](std::coroutine_handle<> resume) -> detail::task_executor { 31 | for (auto i = 0UL; i < std::tuple_size_v; ++i) { 32 | co_await std::suspend_always{}; 33 | } 34 | resume.resume(); 35 | }(handle).handle; 36 | constexpr_for<0UL, std::tuple_size_v, 1UL>( 37 | [this, inc_handle](auto i) { 38 | using S = decltype( 39 | std::get(m_await).get().await_suspend(inc_handle)); 40 | if constexpr (std::is_void_v) { 41 | std::get(m_await).get().await_suspend(inc_handle); 42 | } else if constexpr (std::is_same_v) { 43 | if (!std::get(m_await).get().await_suspend(inc_handle)) { 44 | inc_handle.resume(); 45 | } 46 | } else { 47 | std::get(m_await).get().await_suspend(inc_handle).resume(); 48 | } 49 | }); 50 | } 51 | 52 | auto await_resume() noexcept { 53 | return std::apply( 54 | [](auto&&... args) -> std::tuple...> { 55 | return {non_void_resume(args.get())...}; 56 | }, 57 | m_await); 58 | } 59 | 60 | protected: 61 | template 62 | static decltype(auto) non_void_resume(U& await) { 63 | if constexpr (std::is_void_v) { 64 | await.await_resume(); 65 | return void_t{}; 66 | } else { 67 | return await.await_resume(); 68 | } 69 | } 70 | 71 | template 72 | static constexpr void constexpr_for(F&& f) { 73 | if constexpr (Start < End) { 74 | f(std::integral_constant()); 75 | constexpr_for(f); 76 | } 77 | } 78 | 79 | std::tuple...> m_await; 80 | }; 81 | 82 | } // namespace detail 83 | 84 | template 85 | auto gather(T&&... args) { 86 | return detail::gather_impl(std::forward(args)...); 87 | } 88 | 89 | } // namespace kuro 90 | -------------------------------------------------------------------------------- /exec/kuro.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "event_loop.hpp" 4 | #include "gather.hpp" 5 | #include "sleep.hpp" 6 | -------------------------------------------------------------------------------- /exec/promise.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace kuro::detail { 8 | 9 | template 10 | class return_value_promise { 11 | enum class promise_state { empty, data, exception }; 12 | 13 | public: 14 | return_value_promise() {} 15 | 16 | ~return_value_promise() { 17 | if (m_state == promise_state::data) { 18 | m_data.~T(); 19 | } else if (m_state == promise_state::exception) { 20 | m_except.~exception_ptr(); 21 | } 22 | } 23 | 24 | void unhandled_exception() { 25 | m_except = std::current_exception(); 26 | m_state = promise_state::exception; 27 | } 28 | 29 | protected: 30 | T& get_data() { 31 | if (m_state == promise_state::data) { 32 | return m_data; 33 | } else if (m_state == promise_state::exception) { 34 | std::rethrow_exception(m_except); 35 | } else { 36 | throw std::exception{}; 37 | } 38 | } 39 | 40 | void set_data(T data) { 41 | new (&m_data) T(std::move(data)); 42 | m_state = promise_state::data; 43 | } 44 | 45 | private: 46 | promise_state m_state = promise_state::empty; 47 | union { 48 | T m_data; 49 | std::exception_ptr m_except; 50 | }; 51 | }; 52 | 53 | template 54 | class value_promise : public return_value_promise { 55 | public: 56 | T result() { return std::move(this->get_data()); } 57 | void return_value(T value) noexcept { this->set_data(std::move(value)); } 58 | }; 59 | 60 | template 61 | class reference_promise : public return_value_promise { 62 | public: 63 | T& result() { return *this->get_data(); } 64 | void return_value(T& value) noexcept { this->set_data(&value); } 65 | }; 66 | 67 | class void_promise { 68 | public: 69 | void result() const { 70 | if (m_except) { 71 | std::rethrow_exception(m_except); 72 | } 73 | } 74 | void return_void() noexcept {} 75 | void unhandled_exception() { m_except = std::current_exception(); } 76 | 77 | private: 78 | std::exception_ptr m_except = nullptr; 79 | }; 80 | 81 | template 82 | struct base_promise { 83 | using type = value_promise; 84 | }; 85 | 86 | template 87 | struct base_promise { 88 | using type = reference_promise; 89 | }; 90 | 91 | template <> 92 | struct base_promise { 93 | using type = void_promise; 94 | }; 95 | 96 | template 97 | using base_promise_t = base_promise::type; 98 | 99 | } // namespace kuro::detail -------------------------------------------------------------------------------- /exec/sleep.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "event_loop.hpp" 10 | #include "unique_fd.hpp" 11 | 12 | namespace kuro { 13 | 14 | class duration { 15 | public: 16 | duration(double d) { 17 | m_sec = long(d); 18 | m_nanosec = long((d - long(d)) * 1e9); 19 | } 20 | 21 | explicit operator timespec() const { 22 | return timespec{.tv_sec = m_sec, .tv_nsec = m_nanosec}; 23 | } 24 | 25 | private: 26 | long m_sec; 27 | long m_nanosec; 28 | }; 29 | 30 | class sleep_for { 31 | public: 32 | sleep_for(duration d) 33 | : m_fd(timerfd_create(CLOCK_MONOTONIC, 0)), 34 | m_time(static_cast(d)) {} 35 | 36 | bool await_ready() const noexcept { 37 | return m_time.tv_sec < 0 || (m_time.tv_sec == 0 && m_time.tv_nsec == 0); 38 | } 39 | 40 | void await_resume() const noexcept {} 41 | 42 | void await_suspend(std::coroutine_handle<> handle) noexcept { 43 | itimerspec tspec{.it_value = m_time}; 44 | timerfd_settime(m_fd.get(), 0, &tspec, nullptr); 45 | event_loop::add_reader(m_fd.get(), handle); 46 | } 47 | 48 | private: 49 | detail::unique_fd m_fd; 50 | timespec m_time; 51 | }; 52 | 53 | } // namespace kuro 54 | -------------------------------------------------------------------------------- /exec/task.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "promise.hpp" 9 | #include "unique_coroutine_handle.hpp" 10 | 11 | namespace kuro { 12 | 13 | template 14 | class task { 15 | public: 16 | class promise_type final : public detail::base_promise_t { 17 | public: 18 | task get_return_object() noexcept { 19 | return task(std::coroutine_handle::from_promise(*this)); 20 | } 21 | 22 | std::suspend_always initial_suspend() const noexcept { return {}; } 23 | 24 | auto final_suspend() const noexcept { 25 | struct awaitable { 26 | bool await_ready() noexcept { return false; } 27 | std::coroutine_handle<> await_suspend( 28 | std::coroutine_handle handle) noexcept { 29 | auto continuation = handle.promise().m_continuation; 30 | if (continuation) { 31 | return continuation; 32 | } 33 | 34 | return std::noop_coroutine(); 35 | } 36 | void await_resume() noexcept {} 37 | }; 38 | return awaitable{}; 39 | } 40 | 41 | void set_continuation(std::coroutine_handle<> continuation) { 42 | m_continuation = continuation; 43 | } 44 | 45 | private: 46 | std::coroutine_handle<> m_continuation; 47 | }; 48 | 49 | auto operator co_await() { 50 | struct awaitable { 51 | awaitable(std::coroutine_handle handle) 52 | : m_handle(handle) {} 53 | T await_resume() { return m_handle.promise().result(); } 54 | bool await_ready() const noexcept { return m_handle.done(); } 55 | std::coroutine_handle<> await_suspend( 56 | std::coroutine_handle<> parent_handle) noexcept { 57 | m_handle.promise().set_continuation(parent_handle); 58 | return m_handle; 59 | } 60 | 61 | private: 62 | std::coroutine_handle m_handle; 63 | }; 64 | 65 | return awaitable{m_handle}; 66 | } 67 | 68 | bool done() const noexcept { return m_handle.done(); } 69 | 70 | private: 71 | task(std::coroutine_handle handle) : m_handle(handle) {} 72 | 73 | detail::unique_coroutine_handle m_handle; 74 | }; 75 | 76 | } // namespace kuro 77 | -------------------------------------------------------------------------------- /exec/task_executor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "promise.hpp" 6 | 7 | namespace kuro::detail { 8 | 9 | class task_executor { 10 | public: 11 | class promise_type { 12 | public: 13 | task_executor get_return_object() noexcept { 14 | return task_executor{ 15 | std::coroutine_handle::from_promise(*this)}; 16 | } 17 | std::suspend_never initial_suspend() const noexcept { return {}; } 18 | std::suspend_never final_suspend() const noexcept { return {}; } 19 | void return_void() noexcept {} 20 | void unhandled_exception() {} 21 | }; 22 | 23 | std::coroutine_handle handle; 24 | }; 25 | 26 | template 27 | class task_executor_return { 28 | public: 29 | class promise_type final : public base_promise_t { 30 | public: 31 | task_executor_return get_return_object() noexcept { 32 | return task_executor_return( 33 | std::coroutine_handle::from_promise(*this)); 34 | } 35 | std::suspend_never initial_suspend() const noexcept { return {}; } 36 | std::suspend_always final_suspend() const noexcept { return {}; } 37 | }; 38 | 39 | std::coroutine_handle handle; 40 | }; 41 | 42 | } // namespace kuro::detail 43 | -------------------------------------------------------------------------------- /exec/unique_coroutine_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace kuro::detail { 7 | 8 | template 9 | class unique_coroutine_handle { 10 | public: 11 | unique_coroutine_handle() noexcept = default; 12 | unique_coroutine_handle(std::coroutine_handle h) 13 | : m_ptr(h.address()) {} 14 | bool done() const { return handle().done(); } 15 | Promise& promise() const { return handle().promise(); } 16 | void resume() const { handle().resume(); } 17 | operator std::coroutine_handle() const { return handle(); } 18 | 19 | private: 20 | std::coroutine_handle handle() const noexcept { 21 | return std::coroutine_handle::from_address(m_ptr.get()); 22 | } 23 | 24 | struct destroyer { 25 | constexpr destroyer() noexcept = default; 26 | void operator()(void* ptr) { 27 | std::coroutine_handle::from_address(ptr).destroy(); 28 | } 29 | }; 30 | 31 | std::unique_ptr m_ptr; 32 | }; 33 | 34 | } // namespace kuro::detail 35 | -------------------------------------------------------------------------------- /exec/unique_fd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace kuro::detail { 7 | 8 | inline void check_error(int ret_val) { 9 | if (ret_val == -1) { 10 | throw std::system_error(errno, std::system_category()); 11 | } 12 | } 13 | 14 | class unique_fd { 15 | public: 16 | unique_fd() : m_fd(-1) {} 17 | explicit unique_fd(int fd) : m_fd(fd) {} 18 | unique_fd(const unique_fd&) = delete; 19 | unique_fd& operator=(const unique_fd&) = delete; 20 | unique_fd(unique_fd&& other) { 21 | m_fd = other.m_fd; 22 | other.m_fd = -1; 23 | } 24 | unique_fd& operator=(unique_fd&& other) { 25 | if (m_fd != other.m_fd) { 26 | if (m_fd != -1) { 27 | detail::check_error(close(m_fd)); 28 | } 29 | m_fd = other.m_fd; 30 | other.m_fd = -1; 31 | } 32 | return *this; 33 | } 34 | ~unique_fd() { reset(); } 35 | 36 | explicit operator bool() const noexcept { return m_fd != -1; } 37 | unique_fd& operator=(int fd) { 38 | m_fd = fd; 39 | return *this; 40 | } 41 | int get() const noexcept { return m_fd; } 42 | void reset() { 43 | if (m_fd != -1) { 44 | detail::check_error(close(m_fd)); 45 | } 46 | } 47 | 48 | private: 49 | int m_fd; 50 | }; 51 | 52 | } // namespace kuro::detail 53 | -------------------------------------------------------------------------------- /exec/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace kuro { 8 | 9 | class void_t {}; 10 | 11 | namespace detail { 12 | 13 | template 14 | concept member_co_awaitable = requires(T a) { 15 | a.operator co_await(); 16 | }; 17 | 18 | template 19 | concept non_member_co_awaitable = requires(T a) { 20 | operator co_await(a); 21 | }; 22 | 23 | template 24 | concept generated_co_awaitable = 25 | member_co_awaitable || non_member_co_awaitable; 26 | 27 | template 28 | decltype(auto) get_base_awaitable(T& awaitable) { 29 | if constexpr (member_co_awaitable) { 30 | return awaitable.operator co_await(); 31 | } else if constexpr (non_member_co_awaitable) { 32 | return operator co_await(awaitable); 33 | } else { 34 | return operator co_await(awaitable); 35 | } 36 | } 37 | 38 | template 39 | struct get_base_awaitable_impl {}; 40 | 41 | template 42 | struct get_base_awaitable_impl { 43 | using Type = decltype(std::declval().operator co_await()); 44 | }; 45 | 46 | template 47 | struct get_base_awaitable_impl { 48 | using Type = decltype(operator co_await(std::declval())); 49 | }; 50 | 51 | template 52 | using get_base_awaitable_t = get_base_awaitable_impl::Type; 53 | 54 | template 55 | auto base_awaitable(T awaitable) { 56 | if constexpr (member_co_awaitable) { 57 | return awaitable.operator co_await(); 58 | } else if constexpr (non_member_co_awaitable) { 59 | return operator co_await(awaitable); 60 | } else { 61 | return awaitable; 62 | } 63 | } 64 | 65 | template 66 | using base_awaitable_t = 67 | decltype(base_awaitable(std::declval>())); 68 | 69 | template 70 | class awaitable_container { 71 | public: 72 | awaitable_container(T aw) : m_original(std::forward(aw)) {} 73 | auto& get() { return m_original; } 74 | 75 | private: 76 | T m_original; 77 | }; 78 | 79 | template 80 | class awaitable_container { 81 | public: 82 | awaitable_container(T aw) 83 | : m_original(std::forward(aw)), 84 | m_base(get_base_awaitable(m_original)) {} 85 | auto& get() { return m_base; } 86 | 87 | private: 88 | T m_original; 89 | get_base_awaitable_t m_base; 90 | }; 91 | 92 | template 93 | using awaited_t = decltype(std::declval>().await_resume()); 94 | 95 | template 96 | using non_void_awaited_t = 97 | std::conditional_t>, void_t, awaited_t>; 98 | 99 | template 100 | concept await_suspend_returnable = 101 | std::same_as || std::same_as || 102 | std::same_as>; 103 | 104 | } // namespace detail 105 | 106 | template 107 | concept awaitable = requires(detail::base_awaitable_t a) { 108 | { a.await_ready() } 109 | ->std::same_as; 110 | { a.await_suspend(std::coroutine_handle<>{}) } 111 | ->detail::await_suspend_returnable<>; 112 | {a.await_resume()}; 113 | }; 114 | 115 | } // namespace kuro -------------------------------------------------------------------------------- /fib.zig: -------------------------------------------------------------------------------- 1 | // Small event loop by kprotty 2 | // https://gist.github.com/kprotty/e4409f83f3e6ac87ae1c60f7aaaae95e 3 | 4 | const std = @import("std"); 5 | const allocator = std.heap.page_allocator; 6 | 7 | pub fn main() !void { 8 | const n = 10; 9 | const ret = try (try Task.run(fib, .{n})); 10 | std.debug.warn("fib({}) = {}\n", .{ n, ret }); 11 | } 12 | 13 | fn fib(n: usize) std.mem.Allocator.Error!usize { 14 | Task.yield(); 15 | 16 | if (n <= 1) { 17 | return n; 18 | } 19 | 20 | const l = try allocator.create(@Frame(fib)); 21 | defer allocator.destroy(l); 22 | const r = try allocator.create(@Frame(fib)); 23 | defer allocator.destroy(r); 24 | 25 | l.* = async fib(n - 1); 26 | r.* = async fib(n - 2); 27 | 28 | const lv = await l; 29 | const rv = await r; 30 | 31 | return (try lv) + (try rv); 32 | } 33 | 34 | pub const Task = struct { 35 | next: ?*Task = undefined, 36 | frame: anyframe, 37 | 38 | fn ReturnTypeOf(comptime func: anytype) type { 39 | return @typeInfo(@TypeOf(func)).Fn.return_type orelse unreachable; 40 | } 41 | 42 | pub fn run(comptime asyncFn: anytype, args: anytype) !ReturnTypeOf(asyncFn) { 43 | const Wrapper = struct { 44 | fn entry(fn_args: anytype, task: *Task, res: *?ReturnTypeOf(asyncFn)) void { 45 | Task.yield(); 46 | const result = @call(.{}, asyncFn, fn_args); 47 | suspend res.* = result; 48 | } 49 | }; 50 | 51 | var task: Task = undefined; 52 | var result: ?ReturnTypeOf(asyncFn) = null; 53 | var frame = async Wrapper.entry(args, &task, &result); 54 | 55 | while (Task.find()) |runnable_task| { 56 | resume runnable_task.frame; 57 | } 58 | 59 | return result orelse error.DeadLocked; 60 | } 61 | 62 | var head: ?*Task = null; 63 | var tail: ?*Task = null; 64 | 65 | pub fn yield() void { 66 | var task = Task{ .frame = @frame() }; 67 | suspend task.schedule(); 68 | } 69 | 70 | pub fn schedule(self: *Task) void { 71 | self.next = null; 72 | if (tail) |t| 73 | t.next = self; 74 | if (head == null) 75 | head = self; 76 | tail = self; 77 | } 78 | 79 | fn find() ?*Task { 80 | const task = head orelse return null; 81 | head = task.next; 82 | if (head == null) 83 | tail = null; 84 | return task; 85 | } 86 | }; 87 | --------------------------------------------------------------------------------