diff --git a/Cargo.lock b/Cargo.lock index a6b7f398..83100a5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,19 @@ dependencies = [ "libc", ] +[[package]] +name = "async-compat" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-task" version = "4.7.1" @@ -61,6 +74,12 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -149,6 +168,38 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "camino" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "981a6f317983eec002839b90fae7411a85621410ae591a9cab2ecf5cb5744873" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cc" version = "1.2.37" @@ -174,6 +225,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -198,6 +255,16 @@ dependencies = [ "libloading", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -222,6 +289,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -266,6 +348,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -280,13 +368,21 @@ dependencies = [ name = "examples" version = "0.0.0" dependencies = [ + "async-compat", "aws-sign-v4", + "cargo_metadata", "chrono", + "futures", + "futures-util", "http", + "http-body-util", + "hyper", + "hyper-util", "idna_adapter", "libc", "nginx-sys", "ngx", + "reqwest", "tokio", ] @@ -339,6 +435,95 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -356,8 +541,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -372,6 +573,31 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hex" version = "0.4.3" @@ -389,12 +615,107 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -450,6 +771,16 @@ dependencies = [ "unicode-joining-type", ] +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "io-uring" version = "0.7.10" @@ -461,6 +792,22 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itertools" version = "0.13.0" @@ -535,6 +882,12 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.5" @@ -563,7 +916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -596,6 +949,7 @@ version = "0.5.0" dependencies = [ "allocator-api2", "async-task", + "crossbeam-channel", "lock_api", "nginx-sys", "pin-project-lite", @@ -681,6 +1035,21 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -700,6 +1069,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.40" @@ -709,6 +1133,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -747,6 +1206,44 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + [[package]] name = "ring" version = "0.17.14" @@ -755,7 +1252,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -816,6 +1313,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] @@ -836,12 +1334,28 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde" version = "1.0.224" @@ -872,6 +1386,31 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -988,6 +1527,36 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" version = "0.4.44" @@ -1011,6 +1580,26 @@ dependencies = [ "windows-sys 0.61.0", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -1057,6 +1646,99 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.18.0" @@ -1156,12 +1838,39 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.101" @@ -1189,6 +1898,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.101" @@ -1221,6 +1943,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "1.0.2" @@ -1239,8 +1981,8 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.2.0", - "windows-result", - "windows-strings", + "windows-result 0.4.0", + "windows-strings 0.5.0", ] [[package]] @@ -1277,6 +2019,26 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.0" @@ -1286,6 +2048,15 @@ dependencies = [ "windows-link 0.2.0", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.5.0" @@ -1460,6 +2231,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "xattr" version = "1.5.1" @@ -1470,6 +2247,26 @@ dependencies = [ "rustix", ] +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index c8ab9ee2..f90a965b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,14 +42,17 @@ async-task = { version = "4.7.1", optional = true } lock_api = "0.4.13" nginx-sys = { path = "nginx-sys", version = "0.5.0"} pin-project-lite = { version = "0.2.16", optional = true } +crossbeam-channel = {version = "0.5", optional = true} [features] default = ["std"] # Enables a minimal async runtime built on top of the NGINX event loop. async = [ "alloc", + "std", "dep:async-task", "dep:pin-project-lite", + "dep:crossbeam-channel", ] # Provides APIs that require allocations via the `alloc` crate. alloc = ["allocator-api2/alloc"] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9006aba7..f8a30398 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -23,7 +23,19 @@ http = "1.1.0" # use unicode-rs idna backend for lower MSRV and faster builds idna_adapter = "=1.1.0" libc = "0.2.140" +# async tokio = { version = "1.33.0", features = ["full"] } +hyper = { version = "1", features = ["full"] } +http-body-util = "0.1" +hyper-util = { version = "0.1", features = ["full"] } +cargo_metadata = "0.23" +futures = "0.3.31" +futures-util = "0.3.31" +async-compat = "0.2" +reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", ] } + +[build-dependencies] +cargo_metadata = "0.23" [[example]] name = "curl" @@ -50,6 +62,7 @@ crate-type = ["cdylib"] name = "async" path = "async.rs" crate-type = ["cdylib"] +required-features = ["async"] [[example]] name = "shared_dict" @@ -65,3 +78,4 @@ default = ["export-modules", "ngx/vendored"] # See https://github.com/rust-lang/rust/issues/20267 export-modules = [] linux = [] +async = ["ngx/async"] diff --git a/examples/async.conf b/examples/async.conf index d96876e0..7d90d6d6 100644 --- a/examples/async.conf +++ b/examples/async.conf @@ -12,6 +12,7 @@ http { listen *:8000; server_name localhost; location / { + resolver 1.1.1.1; root html; index index.html index.htm; async on; diff --git a/examples/async.rs b/examples/async.rs index 47f5de8e..74643477 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -1,20 +1,34 @@ +use async_compat::Compat; +use futures::future::{self}; +use futures_util::FutureExt; +use http_body_util::Empty; +use hyper::body::Bytes; +use hyper_util::rt::TokioIo; +use nginx_sys::{ngx_http_core_loc_conf_t, NGX_LOG_ERR}; +use ngx::async_::resolver::Resolver; +use ngx::async_::{spawn, Task}; +use std::cell::RefCell; use std::ffi::{c_char, c_void}; -use std::ptr::{addr_of, addr_of_mut}; -use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; -use std::sync::{Arc, OnceLock}; +use std::future::Future; +use std::pin::Pin; +use std::ptr::{addr_of, addr_of_mut, NonNull}; +use std::sync::atomic::{AtomicPtr, Ordering}; +use std::task::Poll; use std::time::Instant; +use tokio::net::TcpStream; -use ngx::core; +use ngx::core::{self, Pool, Status}; use ngx::ffi::{ - ngx_array_push, ngx_command_t, ngx_conf_t, ngx_connection_t, ngx_event_t, ngx_http_handler_pt, + ngx_array_push, ngx_command_t, ngx_conf_t, ngx_connection_t, ngx_http_handler_pt, ngx_http_module_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_int_t, ngx_module_t, - ngx_post_event, ngx_posted_events, ngx_posted_next_events, ngx_str_t, ngx_uint_t, - NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_MODULE, NGX_LOG_EMERG, + ngx_post_event, ngx_posted_events, ngx_str_t, ngx_uint_t, NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, + NGX_HTTP_LOC_CONF_OFFSET, NGX_HTTP_MODULE, NGX_LOG_EMERG, }; -use ngx::http::{self, HttpModule, MergeConfigError}; +use ngx::http::{self, HTTPStatus, HttpModule, MergeConfigError, Request}; use ngx::http::{HttpModuleLocationConf, HttpModuleMainConf, NgxHttpCoreModule}; -use ngx::{http_request_handler, ngx_conf_log_error, ngx_log_debug_http, ngx_string}; -use tokio::runtime::Runtime; +use ngx::{ + http_request_handler, ngx_conf_log_error, ngx_log_debug_http, ngx_log_error, ngx_string, +}; struct Module; @@ -96,49 +110,150 @@ impl http::Merge for ModuleConfig { } } -unsafe extern "C" fn check_async_work_done(event: *mut ngx_event_t) { - let ctx = ngx::ngx_container_of!(event, RequestCTX, event); - let c: *mut ngx_connection_t = (*event).data.cast(); - - if (*ctx).done.load(Ordering::Relaxed) { - // Triggering async_access_handler again - ngx_post_event((*c).write, addr_of_mut!(ngx_posted_events)); - } else { - // this doesn't have have good performance but works as a simple thread-safe example and - // doesn't causes segfault. The best method that provides both thread-safety and - // performance requires an nginx patch. - ngx_post_event(event, addr_of_mut!(ngx_posted_next_events)); +fn yield_now() -> impl Future { + let mut yielded = false; + future::poll_fn(move |cx| { + if std::mem::replace(&mut yielded, true) { + Poll::Ready(()) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + }) +} + +async fn waste_yield() -> (String, String) { + let start = Instant::now(); + + for _ in 0..1000 { + yield_now().await; } + ( + "X-Waste-Yield-Time".to_string(), + start.elapsed().as_millis().to_string(), + ) } -struct RequestCTX { - done: Arc, - event: ngx_event_t, - task: Option>, +async fn waste_sleep() -> (String, String) { + let start = Instant::now(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + ( + "X-Waste-Sleep-Time".to_string(), + start.elapsed().as_millis().to_string(), + ) } -impl Default for RequestCTX { - fn default() -> Self { - Self { - done: AtomicBool::new(false).into(), - event: unsafe { std::mem::zeroed() }, - task: Default::default(), - } - } +async fn waste_ngx_sleep() -> (String, String) { + let start = Instant::now(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + ( + "X-Waste-Ngx-Sleep-Time".to_string(), + start.elapsed().as_millis().to_string(), + ) } -impl Drop for RequestCTX { - fn drop(&mut self) { - if let Some(handle) = self.task.take() { - handle.abort(); - } +async fn resolve_something( + clcf: &ngx_http_core_loc_conf_t, + pool: &mut Pool, + name: &str, +) -> (String, String) { + let start = Instant::now(); + let resolver = Resolver::from_resolver(NonNull::new(clcf.resolver).expect("resolver"), 30000); + + let _resolution = resolver + .resolve_name(unsafe { &ngx_str_t::from_str(pool.as_mut(), name) }, pool) + .await + .expect("resolution"); + + ( + format!("X-Resolve-Time"), + start.elapsed().as_millis().to_string(), + ) +} + +async fn reqwest_something() -> (String, String) { + let start = Instant::now(); + let _ = reqwest::get("https://example.com") + .await + .expect("response") + .text() + .await + .expect("body"); + ( + "X-Reqwest-Time".to_string(), + start.elapsed().as_millis().to_string(), + ) +} + +async fn hyper_something() -> (String, String) { + let start = Instant::now(); + // see https://hyper.rs/guides/1/client/basic/ + let url = "http://httpbin.org/ip".parse::().expect("uri"); + let host = url.host().expect("uri has no host"); + let port = url.port_u16().unwrap_or(80); + + let address = format!("{}:{}", host, port); - if self.event.posted() != 0 { - unsafe { ngx::ffi::ngx_delete_posted_event(&mut self.event) }; + let stream = TcpStream::connect(address).await.expect("connect"); + + let io = TokioIo::new(stream); + + // Create the Hyper client + let (mut sender, conn) = hyper::client::conn::http1::handshake(io) + .await + .expect("handshake"); + // Spawn a task to poll the connection, driving the HTTP state + let http_task = spawn(async move { + if let Err(err) = conn.await { + println!("Connection failed: {:?}", err); } + }); + let authority = url.authority().unwrap().clone(); + let req = hyper::Request::builder() + .uri(url) + .header(hyper::header::HOST, authority.as_str()) + .body(Empty::::new()) + .expect("body"); + let _ = sender.send_request(req).await.expect("response"); + + http_task.cancel().await; + + ( + "X-Hyper-Time".to_string(), + start.elapsed().as_millis().to_string(), + ) +} + +async fn async_access(request: &mut Request) -> Status { + let start = Instant::now(); + let clcf = NgxHttpCoreModule::location_conf(request).expect("http core loc conf"); + let mut pool = request.pool(); + + // some examples for io and timers + let futs: Vec>>> = vec![ + // ngx resolver + Box::pin(resolve_something(clcf, &mut pool, "example.com")), + // tokio sleep + Box::pin(waste_sleep()), + // ngx sleep + Box::pin(waste_ngx_sleep()), + // yield_now + Box::pin(waste_yield()), + // reqwest + Box::pin(reqwest_something()), + // hyper + Box::pin(hyper_something()), + ]; + for (header, value) in futures::future::join_all(futs).await { + request.add_header_out(&header, &value); } + request.add_header_out("X-Async-Time", &start.elapsed().as_millis().to_string()); + Status::NGX_OK } +#[derive(Default)] +struct RequestCTX(RefCell>>); + http_request_handler!(async_access_handler, |request: &mut http::Request| { let co = Module::location_conf(request).expect("module config is none"); @@ -148,52 +263,43 @@ http_request_handler!(async_access_handler, |request: &mut http::Request| { return core::Status::NGX_DECLINED; } - if let Some(ctx) = + // Check if we were called *again* + if let Some(RequestCTX(task)) = unsafe { request.get_module_ctx::(&*addr_of!(ngx_http_async_module)) } { - if !ctx.done.load(Ordering::Relaxed) { - return core::Status::NGX_AGAIN; + let task = task.take().expect("Task"); + // task should be finished when re-entering the handler + if !task.is_finished() { + ngx_log_error!(NGX_LOG_ERR, request.log(), "Task not finished"); + return HTTPStatus::INTERNAL_SERVER_ERROR.into(); } - - return core::Status::NGX_OK; + return task.now_or_never().expect("Task result"); } - let ctx = request.pool().allocate(RequestCTX::default()); - if ctx.is_null() { - return core::Status::NGX_ERROR; - } - request.set_module_ctx(ctx.cast(), unsafe { &*addr_of!(ngx_http_async_module) }); - - let ctx = unsafe { &mut *ctx }; - ctx.event.handler = Some(check_async_work_done); - ctx.event.data = request.connection().cast(); - ctx.event.log = unsafe { (*request.connection()).log }; - unsafe { ngx_post_event(&mut ctx.event, addr_of_mut!(ngx_posted_next_events)) }; - // Request is no longer needed and can be converted to something movable to the async block let req = AtomicPtr::new(request.into()); - let done_flag = ctx.done.clone(); - let rt = ngx_http_async_runtime(); - ctx.task = Some(rt.spawn(async move { - let start = Instant::now(); - tokio::time::sleep(std::time::Duration::from_secs(2)).await; + // Compat to provide a tokio runtime (without using the tokio scheduler) + let task = spawn(Compat::new(async move { let req = unsafe { http::Request::from_ngx_http_request(req.load(Ordering::Relaxed)) }; - // not really thread safe, we should apply all these operation in nginx thread - // but this is just an example. proper way would be storing these headers in the request ctx - // and apply them when we get back to the nginx thread. - req.add_header_out( - "X-Async-Time", - start.elapsed().as_millis().to_string().as_str(), - ); - - done_flag.store(true, Ordering::Release); - // there is a small issue here. If traffic is low we may get stuck behind a 300ms timer - // in the nginx event loop. To workaround it we can notify the event loop using - // pthread_kill( nginx_thread, SIGIO ) to wake up the event loop. (or patch nginx - // and use the same trick as the thread pool) + let result = async_access(req).await; + + let c: *mut ngx_connection_t = req.connection().cast(); + // trigger „write” event so nginx calls our handler again to finalize the request + unsafe { ngx_post_event((*c).write, addr_of_mut!(ngx_posted_events)) }; + + result })); + let ctx = request + .pool() + .allocate(RequestCTX(RefCell::new(Some(task)))); + + if ctx.is_null() { + return Status::NGX_ERROR; + } + request.set_module_ctx(ctx.cast(), unsafe { &*addr_of!(ngx_http_async_module) }); + core::Status::NGX_AGAIN }); @@ -225,19 +331,3 @@ extern "C" fn ngx_http_async_commands_set_enable( ngx::core::NGX_CONF_OK } - -fn ngx_http_async_runtime() -> &'static Runtime { - // Should not be called from the master process - assert_ne!( - unsafe { ngx::ffi::ngx_process }, - ngx::ffi::NGX_PROCESS_MASTER as _ - ); - - static RUNTIME: OnceLock = OnceLock::new(); - RUNTIME.get_or_init(|| { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .expect("tokio runtime init") - }) -} diff --git a/src/async_/spawn.rs b/src/async_/spawn.rs index dbab031f..d2d649ff 100644 --- a/src/async_/spawn.rs +++ b/src/async_/spawn.rs @@ -1,134 +1,93 @@ -use alloc::collections::vec_deque::VecDeque; -use core::cell::UnsafeCell; +extern crate std; + +use core::ptr; +use core::sync::atomic::{AtomicPtr, Ordering}; +use std::sync::OnceLock; + use core::future::Future; -use core::mem; -use core::ptr::{self, NonNull}; pub use async_task::Task; use async_task::{Runnable, ScheduleInfo, WithInfo}; -use nginx_sys::{ - ngx_del_timer, ngx_delete_posted_event, ngx_event_t, ngx_post_event, ngx_posted_next_events, -}; +use crossbeam_channel::{unbounded, Receiver, Sender}; +use nginx_sys::{ngx_event_actions, ngx_event_t, ngx_thread_tid, ngx_tid_t}; use crate::log::ngx_cycle_log; -use crate::{ngx_container_of, ngx_log_debug}; +use crate::ngx_log_debug; -static SCHEDULER: Scheduler = Scheduler::new(); +static MAIN_TID: AtomicPtr = AtomicPtr::new(ptr::null_mut()); -struct Scheduler(UnsafeCell); - -// SAFETY: Scheduler must only be used from the main thread of a worker process. -unsafe impl Send for Scheduler {} -unsafe impl Sync for Scheduler {} +#[inline] +fn on_event_thread() -> bool { + let main_tid = MAIN_TID.load(Ordering::Relaxed); + !main_tid.is_null() && unsafe { *main_tid == ngx_thread_tid() } +} -impl Scheduler { - const fn new() -> Self { - Self(SchedulerInner::new()) +extern "C" fn notify_handler(_ev: *mut ngx_event_t) { + let mut tid = unsafe { ngx_thread_tid() }; + + // initialize MAIN_TID on first execution + let _ = MAIN_TID.compare_exchange( + ptr::null_mut(), + &mut tid, + Ordering::Relaxed, + Ordering::Relaxed, + ); + + let scheduler = scheduler(); + let mut cnt = 0; + while let Ok(r) = scheduler.rx.try_recv() { + r.run(); + cnt += 1; } + ngx_log_debug!( + ngx_cycle_log().as_ptr(), + "async: notify_handler processed {cnt} items" + ); +} - pub fn schedule(&self, runnable: Runnable) { - // SAFETY: the cell is not empty, and we have exclusive access due to being a - // single-threaded application. - let inner = unsafe { &mut *UnsafeCell::raw_get(&self.0) }; - inner.send(runnable) +fn notify() { + ngx_log_debug!(ngx_cycle_log().as_ptr(), "async: ngx_notify"); + unsafe { + ngx_event_actions.notify.expect("ngx_notify")(Some(notify_handler)); } } -#[repr(C)] -struct SchedulerInner { - _ident: [usize; 4], // `ngx_event_ident` compatibility - event: ngx_event_t, - queue: VecDeque, +struct Scheduler { + rx: Receiver, + tx: Sender, } -impl SchedulerInner { - const fn new() -> UnsafeCell { - let mut event: ngx_event_t = unsafe { mem::zeroed() }; - event.handler = Some(Self::scheduler_event_handler); - - UnsafeCell::new(Self { - _ident: [ - 0, 0, 0, 0x4153594e, // ASYN - ], - event, - queue: VecDeque::new(), - }) - } - - pub fn send(&mut self, runnable: Runnable) { - // Cached `ngx_cycle.log` can be invalidated when reloading configuration in a single - // process mode. Update `log` every time to avoid using stale log pointer. - self.event.log = ngx_cycle_log().as_ptr(); - - // While this event is not used as a timer at the moment, we still want to ensure that it is - // compatible with `ngx_event_ident`. - if self.event.data.is_null() { - self.event.data = ptr::from_mut(self).cast(); - } - - // FIXME: VecDeque::push could panic on an allocation failure, switch to a datastructure - // which will not and propagate the failure. - self.queue.push_back(runnable); - unsafe { ngx_post_event(&mut self.event, ptr::addr_of_mut!(ngx_posted_next_events)) } +impl Scheduler { + fn new() -> Self { + let (tx, rx) = unbounded(); + Scheduler { tx, rx } } - /// This event handler is called by ngx_event_process_posted at the end of - /// ngx_process_events_and_timers. - extern "C" fn scheduler_event_handler(ev: *mut ngx_event_t) { - let mut runnables = { - // SAFETY: - // This handler always receives a non-null pointer to an event embedded into a - // UnsafeCell instance. We modify the contents of the `UnsafeCell`, - // but we ensured that: - // - we access the cell correctly, as documented in - // https://doc.rust-lang.org/stable/std/cell/struct.UnsafeCell.html#memory-layout - // - the access is unique due to being single-threaded - // - the reference is dropped before we start processing queued runnables. - let cell: NonNull> = - unsafe { ngx_container_of!(NonNull::new_unchecked(ev), Self, event).cast() }; - let this = unsafe { &mut *UnsafeCell::raw_get(cell.as_ptr()) }; - - ngx_log_debug!( - this.event.log, - "async: processing {} deferred wakeups", - this.queue.len() - ); - - // Move runnables to a new queue to avoid borrowing from the SchedulerInner and limit - // processing to already queued wakeups. This ensures that we correctly handle tasks - // that keep scheduling themselves (e.g. using yield_now() in a loop). - // We can't use drain() as it borrows from self and breaks aliasing rules. - mem::take(&mut this.queue) - }; - - for runnable in runnables.drain(..) { + fn schedule(&self, runnable: Runnable, info: ScheduleInfo) { + // If we are on the event loop thread it's safe to simply run the Runnable, otherwise we + // enqueue the Runnable and call notify to move it and interrupt epoll + // + // If woken_while_running, it indicates that a task has yielded itself to the Scheduler. + // Force round-trip via queue and notify to limit reentrancy. + // + if on_event_thread() && !info.woken_while_running { runnable.run(); + } else { + self.tx.send(runnable).expect("send"); + notify(); } } } -impl Drop for SchedulerInner { - fn drop(&mut self) { - if self.event.posted() != 0 { - unsafe { ngx_delete_posted_event(&mut self.event) }; - } +static SCHEDULER: OnceLock = OnceLock::new(); - if self.event.timer_set() != 0 { - unsafe { ngx_del_timer(&mut self.event) }; - } - } +fn scheduler() -> &'static Scheduler { + SCHEDULER.get_or_init(Scheduler::new) } fn schedule(runnable: Runnable, info: ScheduleInfo) { - if info.woken_while_running { - SCHEDULER.schedule(runnable); - ngx_log_debug!( - ngx_cycle_log().as_ptr(), - "async: task scheduled while running" - ); - } else { - runnable.run(); - } + let scheduler = scheduler(); + scheduler.schedule(runnable, info); } /// Creates a new task running on the NGINX event loop. @@ -138,10 +97,7 @@ where T: 'static, { ngx_log_debug!(ngx_cycle_log().as_ptr(), "async: spawning new task"); - let scheduler = WithInfo(schedule); - // Safety: single threaded embedding takes care of send/sync requirements for future and - // scheduler. Future and scheduler are both 'static. - let (runnable, task) = unsafe { async_task::spawn_unchecked(future, scheduler) }; + let (runnable, task) = unsafe { async_task::spawn_unchecked(future, WithInfo(schedule)) }; runnable.schedule(); task }