diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..bff29e6 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] diff --git a/Cargo.lock b/Cargo.lock index 4a10265..5b18898 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.53" @@ -94,6 +103,38 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic_refcell" version = "0.1.8" @@ -132,6 +173,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bit-set" version = "0.5.2" @@ -201,6 +248,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "cache-padded" version = "1.2.0" @@ -288,7 +341,7 @@ version = "3.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85" dependencies = [ - "heck", + "heck 0.4.0", "proc-macro-error", "proc-macro2", "quote", @@ -362,6 +415,43 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "console-api" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc347c19eb5b940f396ac155822caee6662f850d97306890ac3773ed76c90c5a" +dependencies = [ + "prost", + "prost-types", + "tonic", + "tonic-build", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565a7dfea2d10dd0e5c57cc394d5d441b1910960d8c9211ed14135e0e6ec3a20" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures", + "hdrhistogram", + "humantime", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "copyless" version = "0.1.5" @@ -703,6 +793,15 @@ version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + [[package]] name = "fern" version = "0.6.0" @@ -719,6 +818,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +[[package]] +name = "fixedbitset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + [[package]] name = "fnv" version = "1.0.7" @@ -916,6 +1033,25 @@ dependencies = [ "bitflags", ] +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.7.1", + "tracing", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -925,6 +1061,28 @@ dependencies = [ "ahash", ] +[[package]] +name = "hdrhistogram" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31672b7011be2c4f7456c4ddbcb40e7e9a4a9fad8efe49a6ebaf5f307d0109c0" +dependencies = [ + "base64", + "byteorder", + "flate2", + "nom", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.0" @@ -946,6 +1104,82 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1000,6 +1234,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.1" @@ -1174,6 +1417,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "naga" version = "0.8.5" @@ -1509,7 +1758,7 @@ dependencies = [ "backtrace", "cfg-if 1.0.0", "libc", - "petgraph", + "petgraph 0.5.1", "redox_syscall", "smallvec", "thread-id", @@ -1528,10 +1777,40 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" dependencies = [ - "fixedbitset", + "fixedbitset 0.2.0", "indexmap", ] +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset 0.4.1", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.8" @@ -1623,6 +1902,59 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9145ac0af1d93c638c98c40cf7d25665f427b2a44ad0a99b1dccf3e2f25bb987" +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph 0.6.0", + "prost", + "prost-types", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +dependencies = [ + "bytes", + "prost", +] + [[package]] name = "quote" version = "1.0.15" @@ -1721,6 +2053,32 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "renderdoc-sys" version = "0.7.1" @@ -1794,6 +2152,24 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.5" @@ -1834,6 +2210,16 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -1861,6 +2247,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -1907,6 +2307,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + [[package]] name = "tiff" version = "0.6.1" @@ -1929,6 +2338,87 @@ dependencies = [ "winapi", ] +[[package]] +name = "tokio" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot 0.12.0", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "winapi", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.8" @@ -1938,12 +2428,154 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util 0.6.9", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util 0.7.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" +dependencies = [ + "lazy_static", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9df98b037d039d03400d9dd06b0f8ce05486b5f25e9a2d7d36196e142ebbc52" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + [[package]] name = "ttf-parser" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ccbe8381883510b6a2d8f1e32905bddd178c11caef8083086d0c0c9ab0ac281" +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + [[package]] name = "unicode-width" version = "0.1.9" @@ -1956,12 +2588,28 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -2382,6 +3030,7 @@ dependencies = [ "cgmath", "chrono", "clap", + "console-subscriber", "crossbeam", "egui", "egui_wgpu_backend", @@ -2399,6 +3048,7 @@ dependencies = [ "serde", "serde_json", "thiserror", + "tokio", "wgpu", "winit", "zsw-egui", @@ -2509,6 +3159,7 @@ dependencies = [ "futures", "log", "pollster", + "tokio", "winit", "zsw-egui", "zsw-img", diff --git a/zsw-renderer/Cargo.toml b/zsw-renderer/Cargo.toml index 1d43c5d..25bb52d 100644 --- a/zsw-renderer/Cargo.toml +++ b/zsw-renderer/Cargo.toml @@ -24,6 +24,7 @@ egui_wgpu_backend = "0.16.0" # Async futures = "0.3.21" pollster = "0.2.4" +tokio = "1.17.0" # Error handling anyhow = "1.0.52" diff --git a/zsw-renderer/src/lib.rs b/zsw-renderer/src/lib.rs index 9d6b5aa..b94b3c1 100644 --- a/zsw-renderer/src/lib.rs +++ b/zsw-renderer/src/lib.rs @@ -53,7 +53,7 @@ use { anyhow::Context, futures::lock::Mutex, - std::{mem, thread, time::Duration}, + std::{mem, time::Duration}, winit::window::Window, zsw_egui::Egui, zsw_img::ImageLoader, @@ -91,7 +91,7 @@ impl Renderer { &self, window: &Window, input: &Input, - wgpu: &'wgpu Wgpu<'window>, + wgpu: &'wgpu Wgpu, panels: &Panels, egui: &'egui Egui, image_loader: &ImageLoader, @@ -125,9 +125,8 @@ impl Renderer { }); // Then sleep until next frame - // TODO: Await while sleeping if let Some(duration) = sleep_duration.checked_sub(total_duration) { - thread::sleep(duration); + tokio::time::sleep(duration).await; } } } @@ -143,7 +142,7 @@ impl Renderer { /// # Blocking /// Locks [`zsw_panels::PanelsLock`] on `panels` async fn update<'window, 'panels>( - wgpu: &Wgpu<'window>, + wgpu: &Wgpu, panels: &'panels Panels, image_loader: &ImageLoader, ) -> Result<(), anyhow::Error> { @@ -166,7 +165,7 @@ impl Renderer { async fn render<'window, 'wgpu, 'egui, 'panels>( window: &Window, input: &Input, - wgpu: &'wgpu Wgpu<'window>, + wgpu: &'wgpu Wgpu, panels: &'panels Panels, egui: &'egui Egui, ) -> Result<(), anyhow::Error> { diff --git a/zsw-settings-window/src/lib.rs b/zsw-settings-window/src/lib.rs index aa58357..0a6a749 100644 --- a/zsw-settings-window/src/lib.rs +++ b/zsw-settings-window/src/lib.rs @@ -119,7 +119,7 @@ impl SettingsWindow { /// Blocks until [`Self::paint_jobs`] on `egui` is called. pub async fn run<'wgpu, 'egui, 'playlist, 'panels, 'profiles>( &self, - wgpu: &'wgpu Wgpu<'_>, + wgpu: &'wgpu Wgpu, egui: &'egui Egui, window: &Window, panels: &'panels Panels, diff --git a/zsw-util/src/fetch_update_lock.rs b/zsw-util/src/fetch_update_lock.rs index cea89e1..efa920c 100644 --- a/zsw-util/src/fetch_update_lock.rs +++ b/zsw-util/src/fetch_update_lock.rs @@ -7,7 +7,6 @@ use { Future, }, std::{ - collections::VecDeque, mem, ops::{Deref, DerefMut}, pin::Pin, @@ -24,8 +23,8 @@ struct Inner { /// If this value has been seen seen: bool, - /// All wakers - wakers: VecDeque, + /// Waker + waker: Option, } /// Fetch-update lock @@ -43,7 +42,7 @@ impl FetchUpdateLock { let inner = Inner { value, seen: false, - wakers: VecDeque::new(), + waker: None, }; Self { inner: Mutex::new(inner), @@ -63,7 +62,7 @@ impl FetchUpdateLock { // Set that the value was seen and wake up someone to update it inner.seen = true; - if let Some(waker) = inner.wakers.pop_front() { + if let Some(waker) = inner.waker.take() { waker.wake(); } @@ -94,7 +93,7 @@ impl FetchUpdateLock { // DEADLOCK: Caller ensures we can wait. // We guarantee we unlock while waiting CondVarFuture::new(move |waker| { - inner.wakers.push_back(waker.clone()); + inner.waker = Some(waker.clone()); mem::drop(inner); }) .await; diff --git a/zsw-util/src/future.rs b/zsw-util/src/future.rs deleted file mode 100644 index 75c7b80..0000000 --- a/zsw-util/src/future.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! Future helpers - -// Imports -use { - parking_lot::{Condvar, Mutex}, - std::{ - future::Future, - sync::{ - atomic::{self, AtomicBool}, - Arc, - }, - task, - }, -}; - - -/// Future runner -/// -/// Adapts a future to run on it's own thread, and be cancellable -/// when polling. -#[derive(Debug)] -pub struct FutureRunner { - /// Signal - signal: Arc, - - /// If we're running - running: AtomicBool, -} - -impl FutureRunner { - /// Creates a new future runner - #[must_use] - pub fn new() -> Self { - // Create the waker - Self { - signal: Arc::new(FutureSignal::new()), - running: AtomicBool::new(false), - } - } - - /// Executes the future - /// - /// # Panics - /// Panics if called more than once - #[allow(clippy::result_unit_err)] // TODO: Use custom enum to say if we were cancelled - pub fn run(&self, f: F) -> Result - where - F: Future, - { - // 'lock' the running bool - assert!( - !self.running.swap(true, atomic::Ordering::AcqRel), - "Cannot run a future runner more than once" - ); - - // Pin the future - futures::pin_mut!(f); - - // Create the waker - let waker = task::Waker::from(Arc::clone(&self.signal)); - let mut ctx = task::Context::from_waker(&waker); - - // Then poll it until we should exit - // Note: On the first loop, `wait` instantly returns for us to loop - while let FutureSignalStatus::Poll = self.signal.wait() { - match f.as_mut().poll(&mut ctx) { - task::Poll::Ready(output) => return Ok(output), - task::Poll::Pending => (), - } - } - - // Exit the signal if we're still waiting - self.signal.exit(); - - Err(()) - } - - /// Stops the future - pub fn stop(&self) { - self.signal.exit(); - } -} - -impl Default for FutureRunner { - fn default() -> Self { - Self::new() - } -} - -impl Drop for FutureRunner { - fn drop(&mut self) { - // Stop the future on-drop - self.stop(); - } -} - -/// Signal inner -#[derive(Debug)] -struct FutureSignalInner { - /// If we should exit - should_exit: bool, - - /// If the future should be polled - should_poll: bool, -} - -/// Status on signal waiting -enum FutureSignalStatus { - /// Should poll - Poll, - - /// Should exit - Exit, -} - -/// Waker signal for [`FuturesRunner`] -#[derive(Debug)] -struct FutureSignal { - /// Inner - inner: Mutex, - - /// Condvar for waiting - cond_var: Condvar, -} - -impl FutureSignal { - /// Creates a new signal - fn new() -> Self { - Self { - inner: Mutex::new(FutureSignalInner { - should_exit: false, - should_poll: true, - }), - cond_var: Condvar::new(), - } - } - - /// Waits until the future should be polled, or we should quit - fn wait(&self) -> FutureSignalStatus { - // Keep waiting until either `should_poll` or `should_exit` are true - // DEADLOCK: We'll be woken up in the waker eventually - let mut inner = self.inner.lock(); - loop { - match (inner.should_exit, inner.should_poll) { - // If we should exit, regardless if we should poll, return - // Note: Doesn't matter if we set `should_poll` to false here - (true, _) => break FutureSignalStatus::Exit, - - // Else if we should poll, set it to false and return - (_, true) => { - inner.should_poll = false; - break FutureSignalStatus::Poll; - }, - - // Else wait - _ => self.cond_var.wait(&mut inner), - } - } - } - - /// Sets to exit - pub fn exit(&self) { - // Lock, set `should_exit` to `true` and notify - // DEADLOCK: `Self::wait` only locks it temporarily without blocking - let mut inner = self.inner.lock(); - inner.should_exit = true; - let _ = self.cond_var.notify_one(); - } -} - -impl task::Wake for FutureSignal { - fn wake(self: std::sync::Arc) { - // Set that we should be polling - // DEADLOCK: `Self::wait` only locks it temporarily without blocking - let mut inner = self.inner.lock(); - inner.should_poll = true; - - // Then notify the waiter - let _ = self.cond_var.notify_one(); - } -} diff --git a/zsw-util/src/lib.rs b/zsw-util/src/lib.rs index f34bab5..3017e3b 100644 --- a/zsw-util/src/lib.rs +++ b/zsw-util/src/lib.rs @@ -61,7 +61,6 @@ // Modules mod display_wrapper; mod fetch_update_lock; -mod future; mod lock; mod rect; mod scan_dir; @@ -71,7 +70,6 @@ mod thread; pub use { display_wrapper::DisplayWrapper, fetch_update_lock::{FetchUpdateLock, FetchUpdateLockGuard}, - future::FutureRunner, lock::Lock, rect::Rect, scan_dir::dir_files_iter, diff --git a/zsw-wgpu/src/lib.rs b/zsw-wgpu/src/lib.rs index a90cfde..4520ce3 100644 --- a/zsw-wgpu/src/lib.rs +++ b/zsw-wgpu/src/lib.rs @@ -61,7 +61,7 @@ use { anyhow::Context, crossbeam::atomic::AtomicCell, futures::lock::{Mutex, MutexGuard}, - std::marker::PhantomData, + std::sync::Arc, wgpu::TextureFormat, winit::{dpi::PhysicalSize, window::Window}, }; @@ -92,7 +92,7 @@ pub struct Surface { // seems to not result in any panics, but it might be worth checking, especially if we // ever need to "restart" `wgpu` in any scenario without restarting the application. #[derive(Debug)] -pub struct Wgpu<'window> { +pub struct Wgpu { /// Device // TODO: There exists a `Device::poll` method, but I'm not sure if we should // have to call that? Seems to be used for async, but we don't use any @@ -130,27 +130,27 @@ pub struct Wgpu<'window> { // without showing the user at least 1 frame of the resized surface. queued_resize: AtomicCell>>, - /// Window lifetime - // Note: Our surface must outlive the window, so we make sure of it using the `'window` lifetime - window_phantom: PhantomData<&'window Window>, + /// Window + // Note: Our surface must outlive the window, so we make sure of it by arcing it + _window: Arc, /// Lock source lock_source: LockSource, } -impl<'window> Wgpu<'window> { +impl Wgpu { /// Creates the `wgpu` wrapper given the window to create it in. - pub async fn new(window: &'window Window) -> Result, anyhow::Error> { + pub async fn new(window: Arc) -> Result { // Create the surface and adapter - // SAFETY: Due to our lifetime, we ensure the window outlives us and thus the surface - let (surface, adapter) = unsafe { self::create_surface_and_adapter(window).await? }; + // SAFETY: Due to the window being arced, and we storing it, we ensure the window outlives us and thus the surface + let (surface, adapter) = unsafe { self::create_surface_and_adapter(&window).await? }; // Then create the device and it's queue let (device, queue) = self::create_device(&adapter).await?; // Configure the surface and get the preferred texture format and surface size let (surface_texture_format, surface_size) = - self::configure_window_surface(window, &surface, &adapter, &device)?; + self::configure_window_surface(&window, &surface, &adapter, &device)?; log::info!("Successfully initialized"); Ok(Self { @@ -162,7 +162,7 @@ impl<'window> Wgpu<'window> { queue, surface_texture_format, queued_resize: AtomicCell::new(None), - window_phantom: PhantomData, + _window: window, lock_source: LockSource, }) } diff --git a/zsw/Cargo.toml b/zsw/Cargo.toml index 67200f8..027a4e7 100644 --- a/zsw/Cargo.toml +++ b/zsw/Cargo.toml @@ -32,8 +32,10 @@ epi = "0.16.0" # Async async-channel = "1.6.1" +console-subscriber = {version = "0.1.3", optional = true} futures = "0.3.21" pollster = "0.2.4" +tokio = {version = "1.17.0", features = ["full"]} # Serialization serde = {version = "1.0.132", features = ["derive"]} @@ -73,3 +75,7 @@ native-dialog = "0.6.3" crossbeam = "0.8.1" parking_lot = {version = "0.12.0", features = ["deadlock_detection"]} rayon = "1.5.1" + +[features] + +tokio-console = ["console-subscriber", "tokio/tracing"] diff --git a/zsw/src/app.rs b/zsw/src/app.rs index 40b9215..009bd6b 100644 --- a/zsw/src/app.rs +++ b/zsw/src/app.rs @@ -13,8 +13,10 @@ use { crate::Args, anyhow::Context, cgmath::{Point2, Vector2}, + futures::future::OptionFuture, pollster::FutureExt, - std::{iter, num::NonZeroUsize, thread}, + std::{num::NonZeroUsize, sync::Arc, thread}, + tokio::task, winit::{ dpi::{PhysicalPosition, PhysicalSize}, event_loop::EventLoop, @@ -32,135 +34,145 @@ use { zsw_profiles::Profiles, zsw_renderer::Renderer, zsw_settings_window::SettingsWindow, - zsw_util::{FutureRunner, Rect}, + zsw_util::Rect, zsw_wgpu::Wgpu, }; /// Runs the application +// TODO: Not arc everything #[allow(clippy::too_many_lines)] // TODO: Refactor -pub fn run(args: &Args) -> Result<(), anyhow::Error> { +#[allow(clippy::future_not_send)] // We only want this to run in the main thread anyway, we spawn everything else +pub async fn run(args: Arc) -> Result<(), anyhow::Error> { // Build the window let (mut event_loop, window) = self::create_window()?; + let window = Arc::new(window); // Create the wgpu interface // TODO: Execute future inn background and continue initializing - let wgpu = Wgpu::new(&window).block_on().context("Unable to create renderer")?; + let wgpu = Wgpu::new(Arc::clone(&window)) + .await + .context("Unable to create renderer")?; + let wgpu = Arc::new(wgpu); // Create the playlist let playlist = Playlist::new(); + let playlist = Arc::new(playlist); // Create the image loader let image_loader = ImageLoader::new(); + let image_loader = Arc::new(image_loader); // Create the panels let panels = Panels::new(wgpu.device(), wgpu.surface_texture_format()).context("Unable to create panels")?; + let panels = Arc::new(panels); // Create egui let egui = Egui::new(&window, &wgpu).context("Unable to create egui state")?; + let egui = Arc::new(egui); // Create the profiles let profiles = Profiles::new().context("Unable to load profiles")?; + let profiles = Arc::new(profiles); // Create the event handler let mut event_handler = EventHandler::new(); // Create the renderer let renderer = Renderer::new(); + let renderer = Arc::new(renderer); // Create the settings window let settings_window = SettingsWindow::new(); + let settings_window = Arc::new(settings_window); // Create the input let input = Input::new(); + let input = Arc::new(input); - // All runners - // Note: They must exists outside of the thread scope because - // their `run` can last until the very end of the function - let profile_loader_runner = FutureRunner::new(); - let playlist_runner = FutureRunner::new(); - let image_loader_threads = thread::available_parallelism().map_or(1, NonZeroUsize::get); - let image_loader_runners = iter::repeat_with(FutureRunner::new) - .take(image_loader_threads) + // TODO: Bundle all of these onto a single struct to pass onto the runners, + // via some generic + + // Then add all futures + let profiles_loader_task: OptionFuture<_> = args + .profile + .clone() + .map({ + let profiles = Arc::clone(&profiles); + let playlist = Arc::clone(&playlist); + let panels = Arc::clone(&panels); + move |path| { + task::Builder::new() + .name("Profiles loader") + .spawn(async move { profiles.run_loader_applier(&path, &playlist, &panels).await }) + } + }) + .into(); + let playlist_task = task::Builder::new().name("Playlist runner").spawn({ + let playlist = Arc::clone(&playlist); + async move { playlist.run().await } + }); + let image_loader_tasks = thread::available_parallelism().map_or(1, NonZeroUsize::get); + let image_loader_tasks = (0..image_loader_tasks) + .map(|idx| { + let image_loader = Arc::clone(&image_loader); + let playlist = Arc::clone(&playlist); + task::Builder::new() + .name(&format!("Image loader #{idx}")) + .spawn(async move { image_loader.run(&playlist).await }) + }) .collect::>(); - let settings_window_runner = FutureRunner::new(); - let renderer_runner = FutureRunner::new(); - - // Start all threads and then wait in the main thread for events - // DEADLOCK: We ensure all threads lock each lock in the same order, - // and that we don't lock them. - thread::scope(|s| { - // Create the thread spawner - let mut thread_spawner = zsw_util::ThreadSpawner::new(s); - - // Spawn the profile loader if we have any - // DEADLOCK: See above - if let Some(path) = &args.profile { - thread_spawner.spawn("Profile loader", || { - // Note: We don't care whether we got cancelled or returned successfully - profile_loader_runner - .run(profiles.run_loader_applier(path, &playlist, &panels)) - .into_ok_or_err(); - })?; + let settings_window_task = task::Builder::new().name("Settings window runner").spawn({ + let settings_window = Arc::clone(&settings_window); + let wgpu = Arc::clone(&wgpu); + let egui = Arc::clone(&egui); + let window = Arc::clone(&window); + let profiles = Arc::clone(&profiles); + let playlist = Arc::clone(&playlist); + let panels = Arc::clone(&panels); + let renderer = Arc::clone(&renderer); + async move { + settings_window + .run(&wgpu, &egui, &window, &panels, &playlist, &profiles, &renderer) + .await; } - - // Spawn the playlist thread - // DEADLOCK: See above - thread_spawner.spawn("Playlist", || { - playlist_runner.run(playlist.run()).into_err(); - })?; - - // Spawn all image loaders - // DEADLOCK: See above - for (thread_idx, runner) in image_loader_runners.iter().enumerate() { - thread_spawner.spawn(format!("Image Loader${thread_idx}"), || { - runner.run(image_loader.run(&playlist)).into_err(); - })?; + }); + let renderer_task = task::Builder::new().name("Renderer runner").spawn({ + let wgpu = Arc::clone(&wgpu); + let egui = Arc::clone(&egui); + let window = Arc::clone(&window); + let panels = Arc::clone(&panels); + let renderer = Arc::clone(&renderer); + let input = Arc::clone(&input); + async move { + renderer + .run(&window, &input, &wgpu, &panels, &egui, &image_loader) + .await; } + }); - // Spawn the settings window thread - // DEADLOCK: See above - thread_spawner.spawn("Settings window", || { - settings_window_runner - .run(settings_window.run(&wgpu, &egui, &window, &panels, &playlist, &profiles, &renderer)) - .into_err(); - })?; + // Run the event loop until exit + event_loop.run_return(|event, _, control_flow| { + event_handler + .handle_event(&wgpu, &egui, &settings_window, &input, event, control_flow) + .block_on(); + }); - // Spawn the renderer thread - // DEADLOCK: See above - thread_spawner.spawn("Renderer", || { - renderer_runner - .run(renderer.run(&window, &input, &wgpu, &panels, &egui, &image_loader)) - .into_err(); - })?; + // Then join all tasks + let _ = profiles_loader_task + .await + .transpose() + .context("Unable to await for profiles loader runner")?; + playlist_task.await.context("Unable to await for playlist runner")?; + for task in image_loader_tasks { + task.await.context("Unable to wait for image loader runner")?; + } + settings_window_task + .await + .context("Unable to await for settings window runner")?; + renderer_task.await.context("Unable to await for renderer runner")?; - // Run event loop in this thread until we quit - // DEADLOCK: `run_return` exits once the user requests it. - // See above - // Note: Doesn't make sense to use a runner here, since nothing will call `stop`. - event_loop.run_return(|event, _, control_flow| { - event_handler - .handle_event(&wgpu, &egui, &settings_window, &input, event, control_flow) - .block_on(); - }); - - // Note: In release builds, once we get here, we can just exit, - // no need to make the user wait for shutdown code. - #[cfg(not(debug_assertions))] - std::process::exit(0); - - // Stop all runners at the end - // Note: Order doesn't matter, as they don't block - playlist_runner.stop(); - image_loader_runners.iter().for_each(FutureRunner::stop); - settings_window_runner.stop(); - renderer_runner.stop(); - - // Then join all threads - thread_spawner.join_all().context("Unable to join all threads")?; - - Ok(()) - }) + Ok(()) } /// Creates the window, as well as the associated event loop diff --git a/zsw/src/app/event_handler.rs b/zsw/src/app/event_handler.rs index 76f00cb..37c7dc2 100644 --- a/zsw/src/app/event_handler.rs +++ b/zsw/src/app/event_handler.rs @@ -28,7 +28,7 @@ impl EventHandler { // TODO: Inverse dependencies of `settings_window` and `panels` and let them depend on us pub async fn handle_event<'window, 'egui>( &mut self, - wgpu: &Wgpu<'_>, + wgpu: &Wgpu, egui: &'egui Egui, settings_window: &SettingsWindow, input: &Input, diff --git a/zsw/src/main.rs b/zsw/src/main.rs index 17f4498..1a91abd 100644 --- a/zsw/src/main.rs +++ b/zsw/src/main.rs @@ -76,7 +76,18 @@ mod logger; pub use self::args::Args; // Imports -use {anyhow::Context, clap::StructOpt}; +use { + anyhow::Context, + clap::StructOpt, + std::{ + num::NonZeroUsize, + sync::{ + atomic::{self, AtomicUsize}, + Arc, + }, + thread, + }, +}; fn main() -> Result<(), anyhow::Error> { // Initialize logger @@ -85,9 +96,9 @@ fn main() -> Result<(), anyhow::Error> { Err(err) => eprintln!("Unable to initialize logger: {err:?}"), } - // Initialize the deadlock detection - #[cfg(debug_assertions)] - self::deadlock_init(); + // Initialize the tokio console subscriber if given the feature + #[cfg(feature = "tokio-console")] + console_subscriber::init(); // Customize the rayon pool thread // Note: This is used indirectly in `image` by `jpeg-decoder` @@ -98,7 +109,7 @@ fn main() -> Result<(), anyhow::Error> { // Get arguments let args = match Args::try_parse() { - Ok(args) => args, + Ok(args) => Arc::new(args), Err(err) => { log::warn!("Unable to retrieve arguments: {err:?}"); err.exit(); @@ -106,10 +117,23 @@ fn main() -> Result<(), anyhow::Error> { }; log::debug!("Arguments: {args:#?}"); + // Create the runtime and enter it + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2 * thread::available_parallelism().map_or(1, NonZeroUsize::get)) // TODO: Adjust? + .enable_time() + .thread_name_fn(|| { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + let id = NEXT_ID.fetch_add(1, atomic::Ordering::AcqRel); + format!("tokio-runtime-{}", id) + }) + .build() + .context("Unable to create runtime")?; + let _runtime_enter = runtime.enter(); + // Run the app and restart if we get an error (up to 5 errors) let mut errors = 0; while errors < 5 { - match app::run(&args) { + match runtime.block_on(app::run(Arc::clone(&args))) { Ok(()) => { log::info!("Application finished"); break; @@ -124,34 +148,3 @@ fn main() -> Result<(), anyhow::Error> { Ok(()) } - -/// Initializes deadlock detection -#[cfg(debug_assertions)] -fn deadlock_init() { - // Create a background thread which checks for deadlocks every 10s - #[allow(clippy::let_underscore_drop)] // We want to detach the thread - let _ = std::thread::Builder::new() - .name("Deadlock detection".to_owned()) - .spawn(move || loop { - // Sleep so we aren't continuously checking - std::thread::sleep(std::time::Duration::from_secs(10)); - - // Then check if we have any and continue if we don't - log::debug!("Checking for deadlocks"); - let deadlocks = parking_lot::deadlock::check_deadlock(); - if deadlocks.is_empty() { - log::debug!("Found no deadlocks"); - continue; - } - - // If we do, log them - log::warn!("Detected {} deadlocks", deadlocks.len()); - for (idx, threads) in deadlocks.iter().enumerate() { - log::warn!("Deadlock #{idx}"); - for thread in threads { - log::warn!("\tThread Id {:#?}", thread.thread_id()); - log::warn!("\tBacktrace: {:#?}", thread.backtrace()); - } - } - }); -}