1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-25 14:05:41 -05:00

Port from mio to polling

This patch replaces the mio crate with the polling. Now that
smol-rs/polling#96 has been merged, we should be at full feature parity
with mio v0.6 now.

Fixes #7104.
Fixes #6486.
This commit is contained in:
John Nunley 2023-10-07 12:56:11 -07:00 committed by GitHub
parent ace987f343
commit c2f8abecfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 711 additions and 586 deletions

349
Cargo.lock generated
View file

@ -14,7 +14,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"getrandom", "getrandom",
"once_cell", "once_cell",
"version_check", "version_check",
@ -53,7 +53,7 @@ dependencies = [
"notify", "notify",
"objc", "objc",
"once_cell", "once_cell",
"parking_lot 0.12.1", "parking_lot",
"png", "png",
"serde", "serde",
"serde_json", "serde_json",
@ -100,18 +100,16 @@ dependencies = [
"home", "home",
"libc", "libc",
"log", "log",
"mio 0.6.23", "miow",
"mio-anonymous-pipes",
"mio-extras",
"miow 0.3.7",
"nix 0.26.2", "nix 0.26.2",
"parking_lot 0.12.1", "parking_lot",
"piper",
"polling",
"regex-automata", "regex-automata",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"signal-hook", "signal-hook",
"signal-hook-mio",
"toml 0.7.4", "toml 0.7.4",
"unicode-width", "unicode-width",
"vte", "vte",
@ -336,12 +334,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -421,7 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342"
dependencies = [ dependencies = [
"lazy-bytes-cast", "lazy-bytes-cast",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -480,6 +472,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "concurrent-queue"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "copypasta" name = "copypasta"
version = "0.8.2" version = "0.8.2"
@ -553,7 +554,7 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
] ]
[[package]] [[package]]
@ -562,7 +563,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
] ]
@ -572,7 +573,7 @@ version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
] ]
[[package]] [[package]]
@ -595,7 +596,7 @@ dependencies = [
"once_cell", "once_cell",
"pkg-config", "pkg-config",
"servo-fontconfig", "servo-fontconfig",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -659,7 +660,7 @@ dependencies = [
"libc", "libc",
"serde", "serde",
"serde_derive", "serde_derive",
"winapi 0.3.9", "winapi",
"wio", "wio",
] ]
@ -707,6 +708,12 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]] [[package]]
name = "fdeflate" name = "fdeflate"
version = "0.3.0" version = "0.3.0"
@ -722,7 +729,7 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"redox_syscall 0.2.16", "redox_syscall 0.2.16",
"windows-sys 0.48.0", "windows-sys 0.48.0",
@ -818,20 +825,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "fuchsia-zircon" name = "futures-io"
version = "0.3.3" version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
dependencies = [
"bitflags 1.3.2",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]] [[package]]
name = "gethostname" name = "gethostname"
@ -840,7 +837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
dependencies = [ dependencies = [
"libc", "libc",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -850,7 +847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177"
dependencies = [ dependencies = [
"libc", "libc",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -859,7 +856,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"wasi", "wasi",
] ]
@ -995,15 +992,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if 1.0.0",
]
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.11" version = "1.0.11"
@ -1015,15 +1003,6 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.7" version = "0.4.7"
@ -1049,7 +1028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [ dependencies = [
"cesu8", "cesu8",
"cfg-if 1.0.0", "cfg-if",
"combine", "combine",
"jni-sys", "jni-sys",
"log", "log",
@ -1082,16 +1061,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]] [[package]]
name = "khronos_api" name = "khronos_api"
version = "3.1.0" version = "3.1.0"
@ -1130,12 +1099,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.147"
@ -1148,8 +1111,8 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -1247,25 +1210,6 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "mio"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
dependencies = [
"cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow 0.2.2",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.8" version = "0.8.8"
@ -1278,61 +1222,13 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "mio-anonymous-pipes"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bc513025fe5005a3aa561b50fdb2cda5a150b84800ae02acd8aa9ed62ca1a6b"
dependencies = [
"mio 0.6.23",
"miow 0.3.7",
"parking_lot 0.11.2",
"spsc-buffer",
"winapi 0.3.9",
]
[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
"mio 0.6.23",
"slab",
]
[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
"iovec",
"libc",
"mio 0.6.23",
]
[[package]]
name = "miow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]] [[package]]
name = "miow" name = "miow"
version = "0.3.7" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -1365,17 +1261,6 @@ dependencies = [
"jni-sys", "jni-sys",
] ]
[[package]]
name = "net2"
version = "0.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
dependencies = [
"cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.24.3" version = "0.24.3"
@ -1383,7 +1268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"memoffset 0.6.5", "memoffset 0.6.5",
] ]
@ -1396,7 +1281,7 @@ checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"memoffset 0.6.5", "memoffset 0.6.5",
] ]
@ -1408,7 +1293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"memoffset 0.7.1", "memoffset 0.7.1",
"static_assertions", "static_assertions",
@ -1437,7 +1322,7 @@ dependencies = [
"inotify", "inotify",
"kqueue", "kqueue",
"libc", "libc",
"mio 0.8.8", "mio",
"walkdir", "walkdir",
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
@ -1561,17 +1446,6 @@ dependencies = [
"redox_syscall 0.3.5", "redox_syscall 0.3.5",
] ]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.1" version = "0.12.1"
@ -1579,21 +1453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core 0.9.8", "parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi 0.3.9",
] ]
[[package]] [[package]]
@ -1602,7 +1462,7 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"libc", "libc",
"redox_syscall 0.3.5", "redox_syscall 0.3.5",
"smallvec", "smallvec",
@ -1615,6 +1475,23 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "piper"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
dependencies = [
"atomic-waker",
"fastrand",
"futures-io",
]
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.27" version = "0.3.27"
@ -1634,6 +1511,20 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "polling"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62a79e457c9898100b4298d57d69ec53d06f9a6ed352431ce5f377e082d2e846"
dependencies = [
"cfg-if",
"concurrent-queue",
"pin-project-lite",
"rustix 0.38.8",
"tracing",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "1.3.1" version = "1.3.1"
@ -1748,9 +1639,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.4" version = "0.38.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
dependencies = [ dependencies = [
"bitflags 2.3.3", "bitflags 2.3.3",
"errno", "errno",
@ -1887,18 +1778,6 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
] ]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio 0.6.23",
"mio-uds",
"signal-hook",
]
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.1" version = "1.4.1"
@ -1914,15 +1793,6 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
[[package]]
name = "slab"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.6" version = "1.0.6"
@ -1997,12 +1867,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "spsc-buffer"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b"
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"
@ -2061,7 +1925,7 @@ dependencies = [
"arrayref", "arrayref",
"arrayvec", "arrayvec",
"bytemuck", "bytemuck",
"cfg-if 1.0.0", "cfg-if",
"log", "log",
"tiny-skia-path", "tiny-skia-path",
] ]
@ -2120,6 +1984,23 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.9" version = "1.0.9"
@ -2222,7 +2103,7 @@ version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]
@ -2247,7 +2128,7 @@ version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if",
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@ -2463,12 +2344,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -2479,12 +2354,6 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu",
] ]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -2497,7 +2366,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -2506,7 +2375,7 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -2676,7 +2545,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"raw-window-handle", "raw-window-handle",
"redox_syscall 0.3.5", "redox_syscall 0.3.5",
"rustix 0.38.4", "rustix 0.38.8",
"sctk-adwaita", "sctk-adwaita",
"serde", "serde",
"smithay-client-toolkit 0.17.0", "smithay-client-toolkit 0.17.0",
@ -2710,7 +2579,7 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@ -2719,17 +2588,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
] ]
[[package]] [[package]]
@ -2760,7 +2619,7 @@ checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507"
dependencies = [ dependencies = [
"gethostname 0.2.3", "gethostname 0.2.3",
"nix 0.24.3", "nix 0.24.3",
"winapi 0.3.9", "winapi",
"winapi-wsapoll", "winapi-wsapoll",
"x11rb-protocol 0.10.0", "x11rb-protocol 0.10.0",
] ]
@ -2777,7 +2636,7 @@ dependencies = [
"libloading", "libloading",
"nix 0.26.2", "nix 0.26.2",
"once_cell", "once_cell",
"winapi 0.3.9", "winapi",
"winapi-wsapoll", "winapi-wsapoll",
"x11rb-protocol 0.12.0", "x11rb-protocol 0.12.0",
] ]

View file

@ -569,6 +569,6 @@ impl WindowContext {
impl Drop for WindowContext { impl Drop for WindowContext {
fn drop(&mut self) { fn drop(&mut self) {
// Shutdown the terminal's PTY. // Shutdown the terminal's PTY.
let _ = self.notifier.0.send(Msg::Shutdown); self.notifier.0.send(Msg::Shutdown);
} }
} }

View file

@ -23,9 +23,8 @@ bitflags = { version = "2.2.1", features = ["serde"] }
home = "0.5.5" home = "0.5.5"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
mio = "0.6.20"
mio-extras = "2"
parking_lot = "0.12.0" parking_lot = "0.12.0"
polling = "3.0.0"
regex-automata = "0.3.6" regex-automata = "0.3.6"
serde = { version = "1", features = ["derive", "rc"] } serde = { version = "1", features = ["derive", "rc"] }
serde_yaml = "0.8" serde_yaml = "0.8"
@ -36,12 +35,11 @@ vte = { version = "0.12.0", default-features = false, features = ["ansi", "serde
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
nix = { version = "0.26.2", default-features = false, features = ["term"] } nix = { version = "0.26.2", default-features = false, features = ["term"] }
signal-hook = "0.3.10" signal-hook = "0.3.10"
signal-hook-mio = { version = "0.2.1", features = ["support-v0_6"] }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
mio-anonymous-pipes = "0.2" piper = "0.2.1"
miow = "0.3" miow = "0.3.0"
windows-sys = { version = "0.48", features = [ windows-sys = { version = "0.48.0", features = [
"Win32_System_Console", "Win32_System_Console",
"Win32_Foundation", "Win32_Foundation",
"Win32_Security", "Win32_Security",

View file

@ -5,15 +5,14 @@ use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::io::{self, ErrorKind, Read, Write}; use std::io::{self, ErrorKind, Read, Write};
use std::marker::Send; use std::marker::Send;
use std::num::NonZeroUsize;
use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
use std::sync::Arc; use std::sync::Arc;
use std::thread::JoinHandle; use std::thread::JoinHandle;
use std::time::Instant; use std::time::Instant;
use log::error; use log::error;
#[cfg(not(windows))] use polling::{Event as PollingEvent, Events, PollMode};
use mio::unix::UnixReady;
use mio::{self, Events, PollOpt, Ready};
use mio_extras::channel::{self, Receiver, Sender};
use crate::event::{self, Event, EventListener, WindowSize}; use crate::event::{self, Event, EventListener, WindowSize};
use crate::sync::FairMutex; use crate::sync::FairMutex;
@ -21,7 +20,7 @@ use crate::term::Term;
use crate::{ansi, thread, tty}; use crate::{ansi, thread, tty};
/// Max bytes to read from the PTY before forced terminal synchronization. /// Max bytes to read from the PTY before forced terminal synchronization.
const READ_BUFFER_SIZE: usize = 0x10_0000; pub(crate) const READ_BUFFER_SIZE: usize = 0x10_0000;
/// Max bytes to read from the PTY while the terminal is locked. /// Max bytes to read from the PTY while the terminal is locked.
const MAX_LOCKED_READ: usize = u16::MAX as usize; const MAX_LOCKED_READ: usize = u16::MAX as usize;
@ -39,14 +38,14 @@ pub enum Msg {
Resize(WindowSize), Resize(WindowSize),
} }
/// The main event!.. loop. /// The main event loop.
/// ///
/// Handles all the PTY I/O and runs the PTY parser which updates terminal /// Handles all the PTY I/O and runs the PTY parser which updates terminal
/// state. /// state.
pub struct EventLoop<T: tty::EventedPty, U: EventListener> { pub struct EventLoop<T: tty::EventedPty, U: EventListener> {
poll: mio::Poll, poll: Arc<polling::Poller>,
pty: T, pty: T,
rx: Receiver<Msg>, rx: PeekableReceiver<Msg>,
tx: Sender<Msg>, tx: Sender<Msg>,
terminal: Arc<FairMutex<Term<U>>>, terminal: Arc<FairMutex<Term<U>>>,
event_proxy: U, event_proxy: U,
@ -54,97 +53,6 @@ pub struct EventLoop<T: tty::EventedPty, U: EventListener> {
ref_test: bool, ref_test: bool,
} }
/// Helper type which tracks how much of a buffer has been written.
struct Writing {
source: Cow<'static, [u8]>,
written: usize,
}
pub struct Notifier(pub Sender<Msg>);
impl event::Notify for Notifier {
fn notify<B>(&self, bytes: B)
where
B: Into<Cow<'static, [u8]>>,
{
let bytes = bytes.into();
// terminal hangs if we send 0 bytes through.
if bytes.len() == 0 {
return;
}
let _ = self.0.send(Msg::Input(bytes));
}
}
impl event::OnResize for Notifier {
fn on_resize(&mut self, window_size: WindowSize) {
let _ = self.0.send(Msg::Resize(window_size));
}
}
/// All of the mutable state needed to run the event loop.
///
/// Contains list of items to write, current write state, etc. Anything that
/// would otherwise be mutated on the `EventLoop` goes here.
#[derive(Default)]
pub struct State {
write_list: VecDeque<Cow<'static, [u8]>>,
writing: Option<Writing>,
parser: ansi::Processor,
}
impl State {
#[inline]
fn ensure_next(&mut self) {
if self.writing.is_none() {
self.goto_next();
}
}
#[inline]
fn goto_next(&mut self) {
self.writing = self.write_list.pop_front().map(Writing::new);
}
#[inline]
fn take_current(&mut self) -> Option<Writing> {
self.writing.take()
}
#[inline]
fn needs_write(&self) -> bool {
self.writing.is_some() || !self.write_list.is_empty()
}
#[inline]
fn set_current(&mut self, new: Option<Writing>) {
self.writing = new;
}
}
impl Writing {
#[inline]
fn new(c: Cow<'static, [u8]>) -> Writing {
Writing { source: c, written: 0 }
}
#[inline]
fn advance(&mut self, n: usize) {
self.written += n;
}
#[inline]
fn remaining_bytes(&self) -> &[u8] {
&self.source[self.written..]
}
#[inline]
fn finished(&self) -> bool {
self.written >= self.source.len()
}
}
impl<T, U> EventLoop<T, U> impl<T, U> EventLoop<T, U>
where where
T: tty::EventedPty + event::OnResize + Send + 'static, T: tty::EventedPty + event::OnResize + Send + 'static,
@ -158,12 +66,12 @@ where
hold: bool, hold: bool,
ref_test: bool, ref_test: bool,
) -> EventLoop<T, U> { ) -> EventLoop<T, U> {
let (tx, rx) = channel::channel(); let (tx, rx) = mpsc::channel();
EventLoop { EventLoop {
poll: mio::Poll::new().expect("create mio Poll"), poll: polling::Poller::new().expect("create Poll").into(),
pty, pty,
tx, tx,
rx, rx: PeekableReceiver::new(rx),
terminal, terminal,
event_proxy, event_proxy,
hold, hold,
@ -171,15 +79,15 @@ where
} }
} }
pub fn channel(&self) -> Sender<Msg> { pub fn channel(&self) -> EventLoopSender {
self.tx.clone() EventLoopSender { sender: self.tx.clone(), poller: self.poll.clone() }
} }
/// Drain the channel. /// Drain the channel.
/// ///
/// Returns `false` when a shutdown message was received. /// Returns `false` when a shutdown message was received.
fn drain_recv_channel(&mut self, state: &mut State) -> bool { fn drain_recv_channel(&mut self, state: &mut State) -> bool {
while let Ok(msg) = self.rx.try_recv() { while let Some(msg) = self.rx.recv() {
match msg { match msg {
Msg::Input(input) => state.write_list.push_back(input), Msg::Input(input) => state.write_list.push_back(input),
Msg::Resize(window_size) => self.pty.on_resize(window_size), Msg::Resize(window_size) => self.pty.on_resize(window_size),
@ -190,20 +98,6 @@ where
true true
} }
/// Returns a `bool` indicating whether or not the event loop should continue running.
#[inline]
fn channel_event(&mut self, token: mio::Token, state: &mut State) -> bool {
if !self.drain_recv_channel(state) {
return false;
}
self.poll
.reregister(&self.rx, token, Ready::readable(), PollOpt::edge() | PollOpt::oneshot())
.unwrap();
true
}
#[inline] #[inline]
fn pty_read<X>( fn pty_read<X>(
&mut self, &mut self,
@ -313,17 +207,15 @@ where
let mut state = State::default(); let mut state = State::default();
let mut buf = [0u8; READ_BUFFER_SIZE]; let mut buf = [0u8; READ_BUFFER_SIZE];
let mut tokens = (0..).map(Into::into); let poll_opts = PollMode::Level;
let mut interest = PollingEvent::readable(0);
let poll_opts = PollOpt::edge() | PollOpt::oneshot();
let channel_token = tokens.next().unwrap();
self.poll.register(&self.rx, channel_token, Ready::readable(), poll_opts).unwrap();
// Register TTY through EventedRW interface. // Register TTY through EventedRW interface.
self.pty.register(&self.poll, &mut tokens, Ready::readable(), poll_opts).unwrap(); unsafe {
self.pty.register(&self.poll, interest, poll_opts).unwrap();
}
let mut events = Events::with_capacity(1024); let mut events = Events::with_capacity(NonZeroUsize::new(1024).unwrap());
let mut pipe = if self.ref_test { let mut pipe = if self.ref_test {
Some(File::create("./alacritty.recording").expect("create alacritty recording")) Some(File::create("./alacritty.recording").expect("create alacritty recording"))
@ -337,7 +229,8 @@ where
let timeout = let timeout =
handler.sync_timeout().map(|st| st.saturating_duration_since(Instant::now())); handler.sync_timeout().map(|st| st.saturating_duration_since(Instant::now()));
if let Err(err) = self.poll.poll(&mut events, timeout) { events.clear();
if let Err(err) = self.poll.wait(&mut events, timeout) {
match err.kind() { match err.kind() {
ErrorKind::Interrupted => continue, ErrorKind::Interrupted => continue,
_ => panic!("EventLoop polling error: {err:?}"), _ => panic!("EventLoop polling error: {err:?}"),
@ -345,21 +238,20 @@ where
} }
// Handle synchronized update timeout. // Handle synchronized update timeout.
if events.is_empty() { if events.is_empty() && self.rx.peek().is_none() {
state.parser.stop_sync(&mut *self.terminal.lock()); state.parser.stop_sync(&mut *self.terminal.lock());
self.event_proxy.send_event(Event::Wakeup); self.event_proxy.send_event(Event::Wakeup);
continue; continue;
} }
for event in events.iter() { // Handle channel events, if there are any.
match event.token() { if !self.drain_recv_channel(&mut state) {
token if token == channel_token => { break;
if !self.channel_event(channel_token, &mut state) { }
break 'event_loop;
}
},
token if token == self.pty.child_event_token() => { for event in events.iter() {
match event.key {
tty::PTY_CHILD_EVENT_TOKEN => {
if let Some(tty::ChildEvent::Exited) = self.pty.next_child_event() { if let Some(tty::ChildEvent::Exited) = self.pty.next_child_event() {
if self.hold { if self.hold {
// With hold enabled, make sure the PTY is drained. // With hold enabled, make sure the PTY is drained.
@ -374,17 +266,13 @@ where
} }
}, },
token tty::PTY_READ_WRITE_TOKEN => {
if token == self.pty.read_token() if event.is_interrupt() {
|| token == self.pty.write_token() =>
{
#[cfg(unix)]
if UnixReady::from(event.readiness()).is_hup() {
// Don't try to do I/O on a dead PTY. // Don't try to do I/O on a dead PTY.
continue; continue;
} }
if event.readiness().is_readable() { if event.readable {
if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut())
{ {
// On Linux, a `read` on the master side of a PTY can fail // On Linux, a `read` on the master side of a PTY can fail
@ -402,7 +290,7 @@ where
} }
} }
if event.readiness().is_writable() { if event.writable {
if let Err(err) = self.pty_write(&mut state) { if let Err(err) = self.pty_write(&mut state) {
error!("Error writing to PTY in event loop: {}", err); error!("Error writing to PTY in event loop: {}", err);
break 'event_loop; break 'event_loop;
@ -414,19 +302,152 @@ where
} }
// Register write interest if necessary. // Register write interest if necessary.
let mut interest = Ready::readable(); let needs_write = state.needs_write();
if state.needs_write() { if needs_write != interest.writable {
interest.insert(Ready::writable()); interest.writable = needs_write;
// Re-register with new interest.
self.pty.reregister(&self.poll, interest, poll_opts).unwrap();
} }
// Reregister with new interest.
self.pty.reregister(&self.poll, interest, poll_opts).unwrap();
} }
// The evented instances are not dropped here so deregister them explicitly. // The evented instances are not dropped here so deregister them explicitly.
let _ = self.poll.deregister(&self.rx);
let _ = self.pty.deregister(&self.poll); let _ = self.pty.deregister(&self.poll);
(self, state) (self, state)
}) })
} }
} }
/// Helper type which tracks how much of a buffer has been written.
struct Writing {
source: Cow<'static, [u8]>,
written: usize,
}
pub struct Notifier(pub EventLoopSender);
impl event::Notify for Notifier {
fn notify<B>(&self, bytes: B)
where
B: Into<Cow<'static, [u8]>>,
{
let bytes = bytes.into();
// Terminal hangs if we send 0 bytes through.
if bytes.len() == 0 {
return;
}
self.0.send(Msg::Input(bytes));
}
}
impl event::OnResize for Notifier {
fn on_resize(&mut self, window_size: WindowSize) {
self.0.send(Msg::Resize(window_size));
}
}
pub struct EventLoopSender {
sender: Sender<Msg>,
poller: Arc<polling::Poller>,
}
impl EventLoopSender {
pub fn send(&self, msg: Msg) {
let _ = self.sender.send(msg);
let _ = self.poller.notify();
}
}
/// All of the mutable state needed to run the event loop.
///
/// Contains list of items to write, current write state, etc. Anything that
/// would otherwise be mutated on the `EventLoop` goes here.
#[derive(Default)]
pub struct State {
write_list: VecDeque<Cow<'static, [u8]>>,
writing: Option<Writing>,
parser: ansi::Processor,
}
impl State {
#[inline]
fn ensure_next(&mut self) {
if self.writing.is_none() {
self.goto_next();
}
}
#[inline]
fn goto_next(&mut self) {
self.writing = self.write_list.pop_front().map(Writing::new);
}
#[inline]
fn take_current(&mut self) -> Option<Writing> {
self.writing.take()
}
#[inline]
fn needs_write(&self) -> bool {
self.writing.is_some() || !self.write_list.is_empty()
}
#[inline]
fn set_current(&mut self, new: Option<Writing>) {
self.writing = new;
}
}
impl Writing {
#[inline]
fn new(c: Cow<'static, [u8]>) -> Writing {
Writing { source: c, written: 0 }
}
#[inline]
fn advance(&mut self, n: usize) {
self.written += n;
}
#[inline]
fn remaining_bytes(&self) -> &[u8] {
&self.source[self.written..]
}
#[inline]
fn finished(&self) -> bool {
self.written >= self.source.len()
}
}
struct PeekableReceiver<T> {
rx: Receiver<T>,
peeked: Option<T>,
}
impl<T> PeekableReceiver<T> {
fn new(rx: Receiver<T>) -> Self {
Self { rx, peeked: None }
}
fn peek(&mut self) -> Option<&T> {
if self.peeked.is_none() {
self.peeked = self.rx.try_recv().ok();
}
self.peeked.as_ref()
}
fn recv(&mut self) -> Option<T> {
if self.peeked.is_some() {
self.peeked.take()
} else {
match self.rx.try_recv() {
Err(TryRecvError::Disconnected) => panic!("event loop channel closed"),
res => res.ok(),
}
}
}
}

View file

@ -1,10 +1,13 @@
//! TTY related functionality. //! TTY related functionality.
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use std::{env, io}; use std::{env, io};
use crate::config::Config; use crate::config::Config;
use polling::{Event, PollMode, Poller};
#[cfg(not(windows))] #[cfg(not(windows))]
mod unix; mod unix;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -22,20 +25,15 @@ pub trait EventedReadWrite {
type Reader: io::Read; type Reader: io::Read;
type Writer: io::Write; type Writer: io::Write;
fn register( /// # Safety
&mut self, ///
_: &mio::Poll, /// The underlying sources must outlive their registration in the `Poller`.
_: &mut dyn Iterator<Item = mio::Token>, unsafe fn register(&mut self, _: &Arc<Poller>, _: Event, _: PollMode) -> io::Result<()>;
_: mio::Ready, fn reregister(&mut self, _: &Arc<Poller>, _: Event, _: PollMode) -> io::Result<()>;
_: mio::PollOpt, fn deregister(&mut self, _: &Arc<Poller>) -> io::Result<()>;
) -> io::Result<()>;
fn reregister(&mut self, _: &mio::Poll, _: mio::Ready, _: mio::PollOpt) -> io::Result<()>;
fn deregister(&mut self, _: &mio::Poll) -> io::Result<()>;
fn reader(&mut self) -> &mut Self::Reader; fn reader(&mut self) -> &mut Self::Reader;
fn read_token(&self) -> mio::Token;
fn writer(&mut self) -> &mut Self::Writer; fn writer(&mut self) -> &mut Self::Writer;
fn write_token(&self) -> mio::Token;
} }
/// Events concerning TTY child processes. /// Events concerning TTY child processes.
@ -51,8 +49,6 @@ pub enum ChildEvent {
/// notified if the PTY child process does something we care about (other than writing to the TTY). /// notified if the PTY child process does something we care about (other than writing to the TTY).
/// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`). /// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`).
pub trait EventedPty: EventedReadWrite { pub trait EventedPty: EventedReadWrite {
fn child_event_token(&self) -> mio::Token;
/// Tries to retrieve an event. /// Tries to retrieve an event.
/// ///
/// Returns `Some(event)` on success, or `None` if there are no events to retrieve. /// Returns `Some(event)` on success, or `None` if there are no events to retrieve.

View file

@ -2,26 +2,34 @@
use std::ffi::CStr; use std::ffi::CStr;
use std::fs::File; use std::fs::File;
use std::io::{Error, ErrorKind, Result}; use std::io::{Error, ErrorKind, Read, Result};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::net::UnixStream;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use std::sync::Arc;
use std::{env, ptr}; use std::{env, ptr};
use libc::{self, c_int, winsize, TIOCSCTTY}; use libc::{self, c_int, winsize, TIOCSCTTY};
use log::error; use log::error;
use mio::unix::EventedFd;
use nix::pty::openpty; use nix::pty::openpty;
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
use nix::sys::termios::{self, InputFlags, SetArg}; use nix::sys::termios::{self, InputFlags, SetArg};
use polling::{Event, PollMode, Poller};
use signal_hook::consts as sigconsts; use signal_hook::consts as sigconsts;
use signal_hook_mio::v0_6::Signals; use signal_hook::low_level::pipe as signal_pipe;
use crate::config::PtyConfig; use crate::config::PtyConfig;
use crate::event::{OnResize, WindowSize}; use crate::event::{OnResize, WindowSize};
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite}; use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
// Interest in PTY read/writes.
pub(crate) const PTY_READ_WRITE_TOKEN: usize = 0;
// Interest in new child events.
pub(crate) const PTY_CHILD_EVENT_TOKEN: usize = 1;
macro_rules! die { macro_rules! die {
($($arg:tt)*) => {{ ($($arg:tt)*) => {{
error!($($arg)*); error!($($arg)*);
@ -103,9 +111,7 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Result<Passwd<'_>> {
pub struct Pty { pub struct Pty {
child: Child, child: Child,
file: File, file: File,
token: mio::Token, signals: UnixStream,
signals: Signals,
signals_token: mio::Token,
} }
impl Pty { impl Pty {
@ -254,7 +260,14 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: u64) -> Resul
} }
// Prepare signal handling before spawning child. // Prepare signal handling before spawning child.
let signals = Signals::new([sigconsts::SIGCHLD]).expect("error preparing signal handling"); let signals = {
let (sender, recv) = UnixStream::pair()?;
// Register the recv end of the pipe for SIGCHLD.
signal_pipe::register(sigconsts::SIGCHLD, sender)?;
recv.set_nonblocking(true)?;
recv
};
match builder.spawn() { match builder.spawn() {
Ok(child) => { Ok(child) => {
@ -264,13 +277,7 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: u64) -> Resul
set_nonblocking(master); set_nonblocking(master);
} }
let mut pty = Pty { let mut pty = Pty { child, file: unsafe { File::from_raw_fd(master) }, signals };
child,
file: unsafe { File::from_raw_fd(master) },
token: mio::Token::from(0),
signals,
signals_token: mio::Token::from(0),
};
pty.on_resize(window_size); pty.on_resize(window_size);
Ok(pty) Ok(pty)
}, },
@ -300,46 +307,47 @@ impl EventedReadWrite for Pty {
type Writer = File; type Writer = File;
#[inline] #[inline]
fn register( unsafe fn register(
&mut self, &mut self,
poll: &mio::Poll, poll: &Arc<Poller>,
token: &mut dyn Iterator<Item = mio::Token>, mut interest: Event,
interest: mio::Ready, poll_opts: PollMode,
poll_opts: mio::PollOpt,
) -> Result<()> { ) -> Result<()> {
self.token = token.next().unwrap(); interest.key = PTY_READ_WRITE_TOKEN;
poll.register(&EventedFd(&self.file.as_raw_fd()), self.token, interest, poll_opts)?; unsafe {
poll.add_with_mode(&self.file, interest, poll_opts)?;
}
self.signals_token = token.next().unwrap(); unsafe {
poll.register( poll.add_with_mode(
&self.signals, &self.signals,
self.signals_token, Event::readable(PTY_CHILD_EVENT_TOKEN),
mio::Ready::readable(), PollMode::Level,
mio::PollOpt::level(), )
) }
} }
#[inline] #[inline]
fn reregister( fn reregister(
&mut self, &mut self,
poll: &mio::Poll, poll: &Arc<Poller>,
interest: mio::Ready, mut interest: Event,
poll_opts: mio::PollOpt, poll_opts: PollMode,
) -> Result<()> { ) -> Result<()> {
poll.reregister(&EventedFd(&self.file.as_raw_fd()), self.token, interest, poll_opts)?; interest.key = PTY_READ_WRITE_TOKEN;
poll.modify_with_mode(&self.file, interest, poll_opts)?;
poll.reregister( poll.modify_with_mode(
&self.signals, &self.signals,
self.signals_token, Event::readable(PTY_CHILD_EVENT_TOKEN),
mio::Ready::readable(), PollMode::Level,
mio::PollOpt::level(),
) )
} }
#[inline] #[inline]
fn deregister(&mut self, poll: &mio::Poll) -> Result<()> { fn deregister(&mut self, poll: &Arc<Poller>) -> Result<()> {
poll.deregister(&EventedFd(&self.file.as_raw_fd()))?; poll.delete(&self.file)?;
poll.deregister(&self.signals) poll.delete(&self.signals)
} }
#[inline] #[inline]
@ -347,44 +355,33 @@ impl EventedReadWrite for Pty {
&mut self.file &mut self.file
} }
#[inline]
fn read_token(&self) -> mio::Token {
self.token
}
#[inline] #[inline]
fn writer(&mut self) -> &mut File { fn writer(&mut self) -> &mut File {
&mut self.file &mut self.file
} }
#[inline]
fn write_token(&self) -> mio::Token {
self.token
}
} }
impl EventedPty for Pty { impl EventedPty for Pty {
#[inline] #[inline]
fn next_child_event(&mut self) -> Option<ChildEvent> { fn next_child_event(&mut self) -> Option<ChildEvent> {
self.signals.pending().next().and_then(|signal| { // See if there has been a SIGCHLD.
if signal != sigconsts::SIGCHLD { let mut buf = [0u8; 1];
return None; if let Err(err) = self.signals.read(&mut buf) {
if err.kind() != ErrorKind::WouldBlock {
error!("Error reading from signal pipe: {}", err);
} }
return None;
}
match self.child.try_wait() { // Match on the child process.
Err(e) => { match self.child.try_wait() {
error!("Error checking child process termination: {}", e); Err(err) => {
None error!("Error checking child process termination: {}", err);
}, None
Ok(None) => None, },
Ok(_) => Some(ChildEvent::Exited), Ok(None) => None,
} Ok(_) => Some(ChildEvent::Exited),
}) }
}
#[inline]
fn child_event_token(&self) -> mio::Token {
self.signals_token
} }
} }

View file

@ -0,0 +1,276 @@
//! Code for running a reader/writer on another thread while driving it through `polling`.
use std::io::prelude::*;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Wake, Waker};
use std::{io, thread};
use piper::{pipe, Reader, Writer};
use polling::os::iocp::{CompletionPacket, PollerIocpExt};
use polling::{Event, PollMode, Poller};
use crate::thread::spawn_named;
struct Registration {
interest: Mutex<Option<Interest>>,
end: PipeEnd,
}
#[derive(Copy, Clone)]
enum PipeEnd {
Reader,
Writer,
}
struct Interest {
/// The event to send about completion.
event: Event,
/// The poller to send the event to.
poller: Arc<Poller>,
/// The mode that we are in.
mode: PollMode,
}
/// Poll a reader in another thread.
pub struct UnblockedReader<R> {
/// The event to send about completion.
interest: Arc<Registration>,
/// The pipe that we are reading from.
pipe: Reader,
/// Is this the first time registering?
first_register: bool,
/// We logically own the reader, but we don't actually use it.
_reader: PhantomData<R>,
}
impl<R: Read + Send + 'static> UnblockedReader<R> {
/// Spawn a new unblocked reader.
pub fn new(mut source: R, pipe_capacity: usize) -> Self {
// Create a new pipe.
let (reader, mut writer) = pipe(pipe_capacity);
let interest = Arc::new(Registration {
interest: Mutex::<Option<Interest>>::new(None),
end: PipeEnd::Reader,
});
// Spawn the reader thread.
spawn_named("alacritty-tty-reader-thread", move || {
let waker = Waker::from(Arc::new(ThreadWaker(thread::current())));
let mut context = Context::from_waker(&waker);
loop {
// Read from the reader into the pipe.
match writer.poll_fill(&mut context, &mut source) {
Poll::Ready(Ok(0)) => {
// Either the pipe is closed or the reader is at its EOF.
// In any case, we are done.
return;
},
Poll::Ready(Ok(_)) => {
// Keep reading.
continue;
},
Poll::Ready(Err(e)) if e.kind() == io::ErrorKind::Interrupted => {
// We were interrupted; continue.
continue;
},
Poll::Ready(Err(e)) => {
log::error!("error writing to pipe: {}", e);
return;
},
Poll::Pending => {
// We are now waiting on the other end to advance. Park the
// thread until they do.
thread::park();
},
}
}
});
Self { interest, pipe: reader, first_register: true, _reader: PhantomData }
}
/// Register interest in the reader.
pub fn register(&mut self, poller: &Arc<Poller>, event: Event, mode: PollMode) {
let mut interest = self.interest.interest.lock().unwrap();
*interest = Some(Interest { event, poller: poller.clone(), mode });
// Send the event to start off with if we have any data.
if (!self.pipe.is_empty() && event.readable) || self.first_register {
self.first_register = false;
poller.post(CompletionPacket::new(event)).ok();
}
}
/// Deregister interest in the reader.
pub fn deregister(&self) {
let mut interest = self.interest.interest.lock().unwrap();
*interest = None;
}
/// Try to read from the reader.
pub fn try_read(&mut self, buf: &mut [u8]) -> usize {
let waker = Waker::from(self.interest.clone());
match self.pipe.poll_drain_bytes(&mut Context::from_waker(&waker), buf) {
Poll::Pending => 0,
Poll::Ready(n) => n,
}
}
}
impl<R: Read + Send + 'static> Read for UnblockedReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok(self.try_read(buf))
}
}
/// Poll a writer in another thread.
pub struct UnblockedWriter<W> {
/// The interest to send about completion.
interest: Arc<Registration>,
/// The pipe that we are writing to.
pipe: Writer,
/// We logically own the writer, but we don't actually use it.
_reader: PhantomData<W>,
}
impl<W: Write + Send + 'static> UnblockedWriter<W> {
/// Spawn a new unblocked writer.
pub fn new(mut sink: W, pipe_capacity: usize) -> Self {
// Create a new pipe.
let (mut reader, writer) = pipe(pipe_capacity);
let interest = Arc::new(Registration {
interest: Mutex::<Option<Interest>>::new(None),
end: PipeEnd::Writer,
});
// Spawn the writer thread.
spawn_named("alacritty-tty-writer-thread", move || {
let waker = Waker::from(Arc::new(ThreadWaker(thread::current())));
let mut context = Context::from_waker(&waker);
loop {
// Write from the pipe into the writer.
match reader.poll_drain(&mut context, &mut sink) {
Poll::Ready(Ok(0)) => {
// Either the pipe is closed or the writer is full.
// In any case, we are done.
return;
},
Poll::Ready(Ok(_)) => {
// Keep writing.
continue;
},
Poll::Ready(Err(e)) if e.kind() == io::ErrorKind::Interrupted => {
// We were interrupted; continue.
continue;
},
Poll::Ready(Err(e)) => {
log::error!("error writing to pipe: {}", e);
return;
},
Poll::Pending => {
// We are now waiting on the other end to advance. Park the
// thread until they do.
thread::park();
},
}
}
});
Self { interest, pipe: writer, _reader: PhantomData }
}
/// Register interest in the writer.
pub fn register(&self, poller: &Arc<Poller>, event: Event, mode: PollMode) {
let mut interest = self.interest.interest.lock().unwrap();
*interest = Some(Interest { event, poller: poller.clone(), mode });
// Send the event to start off with if we have room for data.
if !self.pipe.is_full() && event.writable {
poller.post(CompletionPacket::new(event)).ok();
}
}
/// Deregister interest in the writer.
pub fn deregister(&self) {
let mut interest = self.interest.interest.lock().unwrap();
*interest = None;
}
/// Try to write to the writer.
pub fn try_write(&mut self, buf: &[u8]) -> usize {
let waker = Waker::from(self.interest.clone());
match self.pipe.poll_fill_bytes(&mut Context::from_waker(&waker), buf) {
Poll::Pending => 0,
Poll::Ready(n) => n,
}
}
}
impl<W: Write + Send + 'static> Write for UnblockedWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(self.try_write(buf))
}
fn flush(&mut self) -> io::Result<()> {
// Nothing to flush.
Ok(())
}
}
struct ThreadWaker(thread::Thread);
impl Wake for ThreadWaker {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.unpark();
}
}
impl Wake for Registration {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
let mut interest_lock = self.interest.lock().unwrap();
if let Some(interest) = interest_lock.as_ref() {
// Send the event to the poller.
let send_event = match self.end {
PipeEnd::Reader => interest.event.readable,
PipeEnd::Writer => interest.event.writable,
};
if send_event {
interest.poller.post(CompletionPacket::new(interest.event)).ok();
// Clear the event if we're in oneshot mode.
if matches!(interest.mode, PollMode::Oneshot | PollMode::EdgeOneshot) {
*interest_lock = None;
}
}
}
}
}

View file

@ -1,8 +1,10 @@
use std::ffi::c_void; use std::ffi::c_void;
use std::io::Error; use std::io::Error;
use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::atomic::{AtomicPtr, Ordering};
use std::sync::{mpsc, Arc, Mutex};
use mio_extras::channel::{channel, Receiver, Sender}; use polling::os::iocp::{CompletionPacket, PollerIocpExt};
use polling::{Event, Poller};
use windows_sys::Win32::Foundation::{BOOLEAN, HANDLE}; use windows_sys::Win32::Foundation::{BOOLEAN, HANDLE};
use windows_sys::Win32::System::Threading::{ use windows_sys::Win32::System::Threading::{
@ -12,27 +14,43 @@ use windows_sys::Win32::System::Threading::{
use crate::tty::ChildEvent; use crate::tty::ChildEvent;
struct Interest {
poller: Arc<Poller>,
event: Event,
}
struct ChildExitSender {
sender: mpsc::Sender<ChildEvent>,
interest: Arc<Mutex<Option<Interest>>>,
}
/// WinAPI callback to run when child process exits. /// WinAPI callback to run when child process exits.
extern "system" fn child_exit_callback(ctx: *mut c_void, timed_out: BOOLEAN) { extern "system" fn child_exit_callback(ctx: *mut c_void, timed_out: BOOLEAN) {
if timed_out != 0 { if timed_out != 0 {
return; return;
} }
let event_tx: Box<_> = unsafe { Box::from_raw(ctx as *mut Sender<ChildEvent>) }; let event_tx: Box<_> = unsafe { Box::from_raw(ctx as *mut ChildExitSender) };
let _ = event_tx.send(ChildEvent::Exited); let _ = event_tx.sender.send(ChildEvent::Exited);
let interest = event_tx.interest.lock().unwrap();
if let Some(interest) = interest.as_ref() {
interest.poller.post(CompletionPacket::new(interest.event)).ok();
}
} }
pub struct ChildExitWatcher { pub struct ChildExitWatcher {
wait_handle: AtomicPtr<c_void>, wait_handle: AtomicPtr<c_void>,
event_rx: Receiver<ChildEvent>, event_rx: mpsc::Receiver<ChildEvent>,
interest: Arc<Mutex<Option<Interest>>>,
} }
impl ChildExitWatcher { impl ChildExitWatcher {
pub fn new(child_handle: HANDLE) -> Result<ChildExitWatcher, Error> { pub fn new(child_handle: HANDLE) -> Result<ChildExitWatcher, Error> {
let (event_tx, event_rx) = channel::<ChildEvent>(); let (event_tx, event_rx) = mpsc::channel();
let mut wait_handle: HANDLE = 0; let mut wait_handle: HANDLE = 0;
let sender_ref = Box::new(event_tx); let interest = Arc::new(Mutex::new(None));
let sender_ref = Box::new(ChildExitSender { sender: event_tx, interest: interest.clone() });
let success = unsafe { let success = unsafe {
RegisterWaitForSingleObject( RegisterWaitForSingleObject(
@ -51,13 +69,22 @@ impl ChildExitWatcher {
Ok(ChildExitWatcher { Ok(ChildExitWatcher {
wait_handle: AtomicPtr::from(wait_handle as *mut c_void), wait_handle: AtomicPtr::from(wait_handle as *mut c_void),
event_rx, event_rx,
interest,
}) })
} }
} }
pub fn event_rx(&self) -> &Receiver<ChildEvent> { pub fn event_rx(&self) -> &mpsc::Receiver<ChildEvent> {
&self.event_rx &self.event_rx
} }
pub fn register(&self, poller: &Arc<Poller>, event: Event) {
*self.interest.lock().unwrap() = Some(Interest { poller: poller.clone(), event });
}
pub fn deregister(&self) {
*self.interest.lock().unwrap() = None;
}
} }
impl Drop for ChildExitWatcher { impl Drop for ChildExitWatcher {
@ -72,36 +99,28 @@ impl Drop for ChildExitWatcher {
mod tests { mod tests {
use std::os::windows::io::AsRawHandle; use std::os::windows::io::AsRawHandle;
use std::process::Command; use std::process::Command;
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use mio::{Events, Poll, PollOpt, Ready, Token}; use super::super::PTY_CHILD_EVENT_TOKEN;
use super::*; use super::*;
#[test] #[test]
pub fn event_is_emitted_when_child_exits() { pub fn event_is_emitted_when_child_exits() {
const WAIT_TIMEOUT: Duration = Duration::from_millis(200); const WAIT_TIMEOUT: Duration = Duration::from_millis(200);
let poller = Arc::new(Poller::new().unwrap());
let mut child = Command::new("cmd.exe").spawn().unwrap(); let mut child = Command::new("cmd.exe").spawn().unwrap();
let child_exit_watcher = ChildExitWatcher::new(child.as_raw_handle() as HANDLE).unwrap(); let child_exit_watcher = ChildExitWatcher::new(child.as_raw_handle() as HANDLE).unwrap();
child_exit_watcher.register(&poller, Event::readable(PTY_CHILD_EVENT_TOKEN));
let mut events = Events::with_capacity(1);
let poll = Poll::new().unwrap();
let child_events_token = Token::from(0usize);
poll.register(
child_exit_watcher.event_rx(),
child_events_token,
Ready::readable(),
PollOpt::oneshot(),
)
.unwrap();
child.kill().unwrap(); child.kill().unwrap();
// Poll for the event or fail with timeout if nothing has been sent. // Poll for the event or fail with timeout if nothing has been sent.
poll.poll(&mut events, Some(WAIT_TIMEOUT)).unwrap(); let mut events = polling::Events::new();
assert_eq!(events.iter().next().unwrap().token(), child_events_token); poller.wait(&mut events, Some(WAIT_TIMEOUT)).unwrap();
assert_eq!(events.iter().next().unwrap().key, PTY_CHILD_EVENT_TOKEN);
// Verify that at least one `ChildEvent::Exited` was received. // Verify that at least one `ChildEvent::Exited` was received.
assert_eq!(child_exit_watcher.event_rx().try_recv(), Ok(ChildEvent::Exited)); assert_eq!(child_exit_watcher.event_rx().try_recv(), Ok(ChildEvent::Exited));
} }

View file

@ -3,8 +3,6 @@ use std::io::Error;
use std::os::windows::io::IntoRawHandle; use std::os::windows::io::IntoRawHandle;
use std::{mem, ptr}; use std::{mem, ptr};
use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite};
use windows_sys::core::{HRESULT, PWSTR}; use windows_sys::core::{HRESULT, PWSTR};
use windows_sys::Win32::Foundation::{HANDLE, S_OK}; use windows_sys::Win32::Foundation::{HANDLE, S_OK};
use windows_sys::Win32::System::Console::{ use windows_sys::Win32::System::Console::{
@ -21,9 +19,12 @@ use windows_sys::Win32::System::Threading::{
use crate::config::PtyConfig; use crate::config::PtyConfig;
use crate::event::{OnResize, WindowSize}; use crate::event::{OnResize, WindowSize};
use crate::tty::windows::blocking::{UnblockedReader, UnblockedWriter};
use crate::tty::windows::child::ChildExitWatcher; use crate::tty::windows::child::ChildExitWatcher;
use crate::tty::windows::{cmdline, win32_string, Pty}; use crate::tty::windows::{cmdline, win32_string, Pty};
const PIPE_CAPACITY: usize = crate::event_loop::READ_BUFFER_SIZE;
/// Load the pseudoconsole API from conpty.dll if possible, otherwise use the /// Load the pseudoconsole API from conpty.dll if possible, otherwise use the
/// standard Windows API. /// standard Windows API.
/// ///
@ -220,8 +221,8 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> {
} }
} }
let conin = EventedAnonWrite::new(conin); let conin = UnblockedWriter::new(conin, PIPE_CAPACITY);
let conout = EventedAnonRead::new(conout); let conout = UnblockedReader::new(conout, PIPE_CAPACITY);
let child_watcher = ChildExitWatcher::new(proc_info.hProcess).unwrap(); let child_watcher = ChildExitWatcher::new(proc_info.hProcess).unwrap();
let conpty = Conpty { handle: pty_handle as HPCON, api }; let conpty = Conpty { handle: pty_handle as HPCON, api };

View file

@ -3,17 +3,27 @@ use std::io::{self, Error, ErrorKind, Result};
use std::iter::once; use std::iter::once;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
use std::sync::mpsc::TryRecvError; use std::sync::mpsc::TryRecvError;
use std::sync::Arc;
use crate::config::{Program, PtyConfig}; use crate::config::{Program, PtyConfig};
use crate::event::{OnResize, WindowSize}; use crate::event::{OnResize, WindowSize};
use crate::tty::windows::child::ChildExitWatcher; use crate::tty::windows::child::ChildExitWatcher;
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite}; use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
mod blocking;
mod child; mod child;
mod conpty; mod conpty;
use blocking::{UnblockedReader, UnblockedWriter};
use conpty::Conpty as Backend; use conpty::Conpty as Backend;
use mio_anonymous_pipes::{EventedAnonRead as ReadPipe, EventedAnonWrite as WritePipe}; use miow::pipe::{AnonRead, AnonWrite};
use polling::{Event, Poller};
pub const PTY_CHILD_EVENT_TOKEN: usize = 1;
pub const PTY_READ_WRITE_TOKEN: usize = 2;
type ReadPipe = UnblockedReader<AnonRead>;
type WritePipe = UnblockedWriter<AnonWrite>;
pub struct Pty { pub struct Pty {
// XXX: Backend is required to be the first field, to ensure correct drop order. Dropping // XXX: Backend is required to be the first field, to ensure correct drop order. Dropping
@ -21,9 +31,6 @@ pub struct Pty {
backend: Backend, backend: Backend,
conout: ReadPipe, conout: ReadPipe,
conin: WritePipe, conin: WritePipe,
read_token: mio::Token,
write_token: mio::Token,
child_event_token: mio::Token,
child_watcher: ChildExitWatcher, child_watcher: ChildExitWatcher,
} }
@ -39,51 +46,29 @@ impl Pty {
conin: impl Into<WritePipe>, conin: impl Into<WritePipe>,
child_watcher: ChildExitWatcher, child_watcher: ChildExitWatcher,
) -> Self { ) -> Self {
Self { Self { backend: backend.into(), conout: conout.into(), conin: conin.into(), child_watcher }
backend: backend.into(),
conout: conout.into(),
conin: conin.into(),
read_token: 0.into(),
write_token: 0.into(),
child_event_token: 0.into(),
child_watcher,
}
} }
} }
fn with_key(mut event: Event, key: usize) -> Event {
event.key = key;
event
}
impl EventedReadWrite for Pty { impl EventedReadWrite for Pty {
type Reader = ReadPipe; type Reader = ReadPipe;
type Writer = WritePipe; type Writer = WritePipe;
#[inline] #[inline]
fn register( unsafe fn register(
&mut self, &mut self,
poll: &mio::Poll, poll: &Arc<Poller>,
token: &mut dyn Iterator<Item = mio::Token>, interest: polling::Event,
interest: mio::Ready, poll_opts: polling::PollMode,
poll_opts: mio::PollOpt,
) -> io::Result<()> { ) -> io::Result<()> {
self.read_token = token.next().unwrap(); self.conin.register(poll, with_key(interest, PTY_READ_WRITE_TOKEN), poll_opts);
self.write_token = token.next().unwrap(); self.conout.register(poll, with_key(interest, PTY_READ_WRITE_TOKEN), poll_opts);
self.child_watcher.register(poll, with_key(interest, PTY_CHILD_EVENT_TOKEN));
if interest.is_readable() {
poll.register(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?
} else {
poll.register(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?
}
if interest.is_writable() {
poll.register(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?
} else {
poll.register(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?
}
self.child_event_token = token.next().unwrap();
poll.register(
self.child_watcher.event_rx(),
self.child_event_token,
mio::Ready::readable(),
poll_opts,
)?;
Ok(()) Ok(())
} }
@ -91,36 +76,23 @@ impl EventedReadWrite for Pty {
#[inline] #[inline]
fn reregister( fn reregister(
&mut self, &mut self,
poll: &mio::Poll, poll: &Arc<Poller>,
interest: mio::Ready, interest: polling::Event,
poll_opts: mio::PollOpt, poll_opts: polling::PollMode,
) -> io::Result<()> { ) -> io::Result<()> {
if interest.is_readable() { self.conin.register(poll, with_key(interest, PTY_READ_WRITE_TOKEN), poll_opts);
poll.reregister(&self.conout, self.read_token, mio::Ready::readable(), poll_opts)?; self.conout.register(poll, with_key(interest, PTY_READ_WRITE_TOKEN), poll_opts);
} else { self.child_watcher.register(poll, with_key(interest, PTY_CHILD_EVENT_TOKEN));
poll.reregister(&self.conout, self.read_token, mio::Ready::empty(), poll_opts)?;
}
if interest.is_writable() {
poll.reregister(&self.conin, self.write_token, mio::Ready::writable(), poll_opts)?;
} else {
poll.reregister(&self.conin, self.write_token, mio::Ready::empty(), poll_opts)?;
}
poll.reregister(
self.child_watcher.event_rx(),
self.child_event_token,
mio::Ready::readable(),
poll_opts,
)?;
Ok(()) Ok(())
} }
#[inline] #[inline]
fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> { fn deregister(&mut self, _poll: &Arc<Poller>) -> io::Result<()> {
poll.deregister(&self.conout)?; self.conin.deregister();
poll.deregister(&self.conin)?; self.conout.deregister();
poll.deregister(self.child_watcher.event_rx())?; self.child_watcher.deregister();
Ok(()) Ok(())
} }
@ -129,27 +101,13 @@ impl EventedReadWrite for Pty {
&mut self.conout &mut self.conout
} }
#[inline]
fn read_token(&self) -> mio::Token {
self.read_token
}
#[inline] #[inline]
fn writer(&mut self) -> &mut Self::Writer { fn writer(&mut self) -> &mut Self::Writer {
&mut self.conin &mut self.conin
} }
#[inline]
fn write_token(&self) -> mio::Token {
self.write_token
}
} }
impl EventedPty for Pty { impl EventedPty for Pty {
fn child_event_token(&self) -> mio::Token {
self.child_event_token
}
fn next_child_event(&mut self) -> Option<ChildEvent> { fn next_child_event(&mut self) -> Option<ChildEvent> {
match self.child_watcher.event_rx().try_recv() { match self.child_watcher.event_rx().try_recv() {
Ok(ev) => Some(ev), Ok(ev) => Some(ev),