Bug 1910513 - Update tokio to 1.39.2. r=nika,supply-chain-reviewers

This also update mio to 1.0.1 and tokio-macros to 2.4.0

Differential Revision: https://phabricator.services.mozilla.com/D218157
This commit is contained in:
Mike Hommey 2024-07-31 02:53:18 +00:00
parent 130bcf8632
commit 7c72645ded
441 changed files with 24354 additions and 8632 deletions

View File

@ -30,11 +30,6 @@ git = "https://github.com/gfx-rs/wgpu"
rev = "aeb2067e8120c1ff480625c00b9571db8d01d5a4"
replace-with = "vendored-sources"
[source."git+https://github.com/glandium/mio?rev=9a2ef335c366044ffe73b1c4acabe50a1daefe05"]
git = "https://github.com/glandium/mio"
rev = "9a2ef335c366044ffe73b1c4acabe50a1daefe05"
replace-with = "vendored-sources"
[source."git+https://github.com/hsivonen/any_all_workaround?rev=7fb1b7034c9f172aade21ee1c8554e8d8a48af80"]
git = "https://github.com/hsivonen/any_all_workaround"
rev = "7fb1b7034c9f172aade21ee1c8554e8d8a48af80"

64
Cargo.lock generated
View File

@ -263,7 +263,7 @@ dependencies = [
"libc",
"log",
"mach",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -285,13 +285,13 @@ dependencies = [
"libc",
"log",
"memmap2",
"mio 1.999.999",
"mio",
"scopeguard",
"serde",
"serde_bytes",
"serde_derive",
"slab",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -1048,7 +1048,7 @@ dependencies = [
"unic-langid",
"uuid",
"warp",
"windows-sys 0.52.0",
"windows-sys",
"yaml-rust",
"zip",
]
@ -1702,7 +1702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -3761,20 +3761,15 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.8"
source = "git+https://github.com/glandium/mio?rev=9a2ef335c366044ffe73b1c4acabe50a1daefe05#9a2ef335c366044ffe73b1c4acabe50a1daefe05"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [
"hermit-abi",
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]]
name = "mio"
version = "1.999.999"
dependencies = [
"mio 0.8.8",
"windows-sys",
]
[[package]]
@ -3893,7 +3888,7 @@ dependencies = [
"lmdb-rkv-sys",
"log",
"memchr",
"mio 0.8.8",
"mio",
"nom",
"num-integer",
"num-traits",
@ -3926,7 +3921,7 @@ dependencies = [
"url",
"uuid",
"winapi",
"windows-sys 0.52.0",
"windows-sys",
"xml-rs",
"yoke",
"zerofrom",
@ -3986,7 +3981,7 @@ dependencies = [
"serde",
"serde_json",
"uuid",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -4689,7 +4684,7 @@ dependencies = [
"mozilla-central-workspace-hack",
"scroll",
"thiserror",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -4814,7 +4809,7 @@ dependencies = [
"libc",
"once_cell",
"socket2 0.5.7",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -5146,7 +5141,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -5480,7 +5475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -5801,7 +5796,7 @@ dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -5947,27 +5942,25 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.29.1"
version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"libc",
"mio 0.8.8",
"num_cpus",
"mio",
"pin-project-lite",
"socket2 0.4.999",
"socket2 0.5.7",
"tokio-macros",
"windows-sys 0.48.999",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
@ -6924,13 +6917,6 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.48.999"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.52.0"

View File

@ -111,9 +111,6 @@ mozilla-central-workspace-hack = { path = "build/workspace-hack" }
# crate, which we don't really need as we require MSVC anyways.
windows-targets = { path = "build/rust/windows-targets" }
# Patch windows-sys from 0.48.0 to 0.52.0
windows-sys = { path = "build/rust/windows-sys" }
# Patch windows to use a non-vendored local copy of the crate.
windows = { path = "build/rust/windows" }
@ -222,11 +219,6 @@ tabs = { git = "https://github.com/mozilla/application-services", rev = "8fd08c6
viaduct = { git = "https://github.com/mozilla/application-services", rev = "8fd08c6f2f8acd38579bd3142fecda9272957b72" }
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "8fd08c6f2f8acd38579bd3142fecda9272957b72" }
# Patch mio 0.8.8 to use windows-sys 0.52 (backport https://github.com/tokio-rs/mio/commit/eea9e3e0c469480e5c59c01e6c3c7e5fd88f0848)
mio_0_8 = { package = "mio", git = "https://github.com/glandium/mio", rev = "9a2ef335c366044ffe73b1c4acabe50a1daefe05" }
# Downgrade mio 1.0 to 0.8 until tokio is upgraded to use mio 1.0.
mio = { path = "build/rust/mio" }
# Patch `gpu-descriptor` 0.3.0 to remove unnecessary `allocator-api2` dep.:
# Still waiting for the now-merged <https://github.com/zakarumych/gpu-descriptor/pull/40> to be released.
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "7b71a4e47c81903ad75e2c53deb5ab1310f6ff4d" }

View File

@ -1,19 +0,0 @@
[package]
name = "mio"
version = "1.999.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies.mio]
default-features = false
version = "0.8"
[features]
default = ["mio/default"]
log = ["mio/log"]
net = ["mio/net"]
os-ext = ["mio/os-ext"]
os-poll = ["mio/os-poll"]

View File

@ -1,5 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub use mio::*;

View File

@ -1,232 +0,0 @@
[package]
name = "windows-sys"
version = "0.48.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies.windows-sys]
version = "0.52"
default-features = false
[features]
Wdk = ["windows-sys/Wdk"]
Wdk_System = ["windows-sys/Wdk_System"]
Wdk_System_OfflineRegistry = ["windows-sys/Wdk_System_OfflineRegistry"]
Win32 = ["windows-sys/Win32"]
Win32_Data = ["windows-sys/Win32_Data"]
Win32_Data_HtmlHelp = ["windows-sys/Win32_Data_HtmlHelp"]
Win32_Data_RightsManagement = ["windows-sys/Win32_Data_RightsManagement"]
Win32_Devices = ["windows-sys/Win32_Devices"]
Win32_Devices_AllJoyn = ["windows-sys/Win32_Devices_AllJoyn"]
Win32_Devices_BiometricFramework = ["windows-sys/Win32_Devices_BiometricFramework"]
Win32_Devices_Bluetooth = ["windows-sys/Win32_Devices_Bluetooth"]
Win32_Devices_Communication = ["windows-sys/Win32_Devices_Communication"]
Win32_Devices_DeviceAndDriverInstallation = ["windows-sys/Win32_Devices_DeviceAndDriverInstallation"]
Win32_Devices_DeviceQuery = ["windows-sys/Win32_Devices_DeviceQuery"]
Win32_Devices_Display = ["windows-sys/Win32_Devices_Display"]
Win32_Devices_Enumeration = ["windows-sys/Win32_Devices_Enumeration"]
Win32_Devices_Enumeration_Pnp = ["windows-sys/Win32_Devices_Enumeration_Pnp"]
Win32_Devices_Fax = ["windows-sys/Win32_Devices_Fax"]
Win32_Devices_HumanInterfaceDevice = ["windows-sys/Win32_Devices_HumanInterfaceDevice"]
Win32_Devices_PortableDevices = ["windows-sys/Win32_Devices_PortableDevices"]
Win32_Devices_Properties = ["windows-sys/Win32_Devices_Properties"]
Win32_Devices_Pwm = ["windows-sys/Win32_Devices_Pwm"]
Win32_Devices_Sensors = ["windows-sys/Win32_Devices_Sensors"]
Win32_Devices_SerialCommunication = ["windows-sys/Win32_Devices_SerialCommunication"]
Win32_Devices_Tapi = ["windows-sys/Win32_Devices_Tapi"]
Win32_Devices_Usb = ["windows-sys/Win32_Devices_Usb"]
Win32_Devices_WebServicesOnDevices = ["windows-sys/Win32_Devices_WebServicesOnDevices"]
Win32_Foundation = ["windows-sys/Win32_Foundation"]
Win32_Gaming = ["windows-sys/Win32_Gaming"]
Win32_Globalization = ["windows-sys/Win32_Globalization"]
Win32_Graphics = ["windows-sys/Win32_Graphics"]
Win32_Graphics_Dwm = ["windows-sys/Win32_Graphics_Dwm"]
Win32_Graphics_Gdi = ["windows-sys/Win32_Graphics_Gdi"]
Win32_Graphics_Hlsl = ["windows-sys/Win32_Graphics_Hlsl"]
Win32_Graphics_OpenGL = ["windows-sys/Win32_Graphics_OpenGL"]
Win32_Graphics_Printing = ["windows-sys/Win32_Graphics_Printing"]
Win32_Graphics_Printing_PrintTicket = ["windows-sys/Win32_Graphics_Printing_PrintTicket"]
Win32_Management = ["windows-sys/Win32_Management"]
Win32_Management_MobileDeviceManagementRegistration = ["windows-sys/Win32_Management_MobileDeviceManagementRegistration"]
Win32_Media = ["windows-sys/Win32_Media"]
Win32_Media_Audio = ["windows-sys/Win32_Media_Audio"]
Win32_Media_DxMediaObjects = ["windows-sys/Win32_Media_DxMediaObjects"]
Win32_Media_KernelStreaming = ["windows-sys/Win32_Media_KernelStreaming"]
Win32_Media_Multimedia = ["windows-sys/Win32_Media_Multimedia"]
Win32_Media_Streaming = ["windows-sys/Win32_Media_Streaming"]
Win32_Media_WindowsMediaFormat = ["windows-sys/Win32_Media_WindowsMediaFormat"]
Win32_NetworkManagement = ["windows-sys/Win32_NetworkManagement"]
Win32_NetworkManagement_Dhcp = ["windows-sys/Win32_NetworkManagement_Dhcp"]
Win32_NetworkManagement_Dns = ["windows-sys/Win32_NetworkManagement_Dns"]
Win32_NetworkManagement_InternetConnectionWizard = ["windows-sys/Win32_NetworkManagement_InternetConnectionWizard"]
Win32_NetworkManagement_IpHelper = ["windows-sys/Win32_NetworkManagement_IpHelper"]
Win32_NetworkManagement_Multicast = ["windows-sys/Win32_NetworkManagement_Multicast"]
Win32_NetworkManagement_Ndis = ["windows-sys/Win32_NetworkManagement_Ndis"]
Win32_NetworkManagement_NetBios = ["windows-sys/Win32_NetworkManagement_NetBios"]
Win32_NetworkManagement_NetManagement = ["windows-sys/Win32_NetworkManagement_NetManagement"]
Win32_NetworkManagement_NetShell = ["windows-sys/Win32_NetworkManagement_NetShell"]
Win32_NetworkManagement_NetworkDiagnosticsFramework = ["windows-sys/Win32_NetworkManagement_NetworkDiagnosticsFramework"]
Win32_NetworkManagement_P2P = ["windows-sys/Win32_NetworkManagement_P2P"]
Win32_NetworkManagement_QoS = ["windows-sys/Win32_NetworkManagement_QoS"]
Win32_NetworkManagement_Rras = ["windows-sys/Win32_NetworkManagement_Rras"]
Win32_NetworkManagement_Snmp = ["windows-sys/Win32_NetworkManagement_Snmp"]
Win32_NetworkManagement_WNet = ["windows-sys/Win32_NetworkManagement_WNet"]
Win32_NetworkManagement_WebDav = ["windows-sys/Win32_NetworkManagement_WebDav"]
Win32_NetworkManagement_WiFi = ["windows-sys/Win32_NetworkManagement_WiFi"]
Win32_NetworkManagement_WindowsConnectionManager = ["windows-sys/Win32_NetworkManagement_WindowsConnectionManager"]
Win32_NetworkManagement_WindowsFilteringPlatform = ["windows-sys/Win32_NetworkManagement_WindowsFilteringPlatform"]
Win32_NetworkManagement_WindowsFirewall = ["windows-sys/Win32_NetworkManagement_WindowsFirewall"]
Win32_NetworkManagement_WindowsNetworkVirtualization = ["windows-sys/Win32_NetworkManagement_WindowsNetworkVirtualization"]
Win32_Networking = ["windows-sys/Win32_Networking"]
Win32_Networking_ActiveDirectory = ["windows-sys/Win32_Networking_ActiveDirectory"]
Win32_Networking_Clustering = ["windows-sys/Win32_Networking_Clustering"]
Win32_Networking_HttpServer = ["windows-sys/Win32_Networking_HttpServer"]
Win32_Networking_Ldap = ["windows-sys/Win32_Networking_Ldap"]
Win32_Networking_WebSocket = ["windows-sys/Win32_Networking_WebSocket"]
Win32_Networking_WinHttp = ["windows-sys/Win32_Networking_WinHttp"]
Win32_Networking_WinInet = ["windows-sys/Win32_Networking_WinInet"]
Win32_Networking_WinSock = ["windows-sys/Win32_Networking_WinSock"]
Win32_Networking_WindowsWebServices = ["windows-sys/Win32_Networking_WindowsWebServices"]
Win32_Security = ["windows-sys/Win32_Security"]
Win32_Security_AppLocker = ["windows-sys/Win32_Security_AppLocker"]
Win32_Security_Authentication = ["windows-sys/Win32_Security_Authentication"]
Win32_Security_Authentication_Identity = ["windows-sys/Win32_Security_Authentication_Identity"]
Win32_Security_Authorization = ["windows-sys/Win32_Security_Authorization"]
Win32_Security_Credentials = ["windows-sys/Win32_Security_Credentials"]
Win32_Security_Cryptography = ["windows-sys/Win32_Security_Cryptography"]
Win32_Security_Cryptography_Catalog = ["windows-sys/Win32_Security_Cryptography_Catalog"]
Win32_Security_Cryptography_Certificates = ["windows-sys/Win32_Security_Cryptography_Certificates"]
Win32_Security_Cryptography_Sip = ["windows-sys/Win32_Security_Cryptography_Sip"]
Win32_Security_Cryptography_UI = ["windows-sys/Win32_Security_Cryptography_UI"]
Win32_Security_DiagnosticDataQuery = ["windows-sys/Win32_Security_DiagnosticDataQuery"]
Win32_Security_DirectoryServices = ["windows-sys/Win32_Security_DirectoryServices"]
Win32_Security_EnterpriseData = ["windows-sys/Win32_Security_EnterpriseData"]
Win32_Security_ExtensibleAuthenticationProtocol = ["windows-sys/Win32_Security_ExtensibleAuthenticationProtocol"]
Win32_Security_Isolation = ["windows-sys/Win32_Security_Isolation"]
Win32_Security_LicenseProtection = ["windows-sys/Win32_Security_LicenseProtection"]
Win32_Security_NetworkAccessProtection = ["windows-sys/Win32_Security_NetworkAccessProtection"]
Win32_Security_WinTrust = ["windows-sys/Win32_Security_WinTrust"]
Win32_Security_WinWlx = ["windows-sys/Win32_Security_WinWlx"]
Win32_Storage = ["windows-sys/Win32_Storage"]
Win32_Storage_Cabinets = ["windows-sys/Win32_Storage_Cabinets"]
Win32_Storage_CloudFilters = ["windows-sys/Win32_Storage_CloudFilters"]
Win32_Storage_Compression = ["windows-sys/Win32_Storage_Compression"]
Win32_Storage_DistributedFileSystem = ["windows-sys/Win32_Storage_DistributedFileSystem"]
Win32_Storage_FileHistory = ["windows-sys/Win32_Storage_FileHistory"]
Win32_Storage_FileSystem = ["windows-sys/Win32_Storage_FileSystem"]
Win32_Storage_Imapi = ["windows-sys/Win32_Storage_Imapi"]
Win32_Storage_IndexServer = ["windows-sys/Win32_Storage_IndexServer"]
Win32_Storage_InstallableFileSystems = ["windows-sys/Win32_Storage_InstallableFileSystems"]
Win32_Storage_IscsiDisc = ["windows-sys/Win32_Storage_IscsiDisc"]
Win32_Storage_Jet = ["windows-sys/Win32_Storage_Jet"]
Win32_Storage_OfflineFiles = ["windows-sys/Win32_Storage_OfflineFiles"]
Win32_Storage_OperationRecorder = ["windows-sys/Win32_Storage_OperationRecorder"]
Win32_Storage_Packaging = ["windows-sys/Win32_Storage_Packaging"]
Win32_Storage_Packaging_Appx = ["windows-sys/Win32_Storage_Packaging_Appx"]
Win32_Storage_ProjectedFileSystem = ["windows-sys/Win32_Storage_ProjectedFileSystem"]
Win32_Storage_StructuredStorage = ["windows-sys/Win32_Storage_StructuredStorage"]
Win32_Storage_Vhd = ["windows-sys/Win32_Storage_Vhd"]
Win32_Storage_Xps = ["windows-sys/Win32_Storage_Xps"]
Win32_System = ["windows-sys/Win32_System"]
Win32_System_AddressBook = ["windows-sys/Win32_System_AddressBook"]
Win32_System_Antimalware = ["windows-sys/Win32_System_Antimalware"]
Win32_System_ApplicationInstallationAndServicing = ["windows-sys/Win32_System_ApplicationInstallationAndServicing"]
Win32_System_ApplicationVerifier = ["windows-sys/Win32_System_ApplicationVerifier"]
Win32_System_ClrHosting = ["windows-sys/Win32_System_ClrHosting"]
Win32_System_Com = ["windows-sys/Win32_System_Com"]
Win32_System_Com_Marshal = ["windows-sys/Win32_System_Com_Marshal"]
Win32_System_Com_StructuredStorage = ["windows-sys/Win32_System_Com_StructuredStorage"]
Win32_System_Com_Urlmon = ["windows-sys/Win32_System_Com_Urlmon"]
Win32_System_ComponentServices = ["windows-sys/Win32_System_ComponentServices"]
Win32_System_Console = ["windows-sys/Win32_System_Console"]
Win32_System_CorrelationVector = ["windows-sys/Win32_System_CorrelationVector"]
Win32_System_DataExchange = ["windows-sys/Win32_System_DataExchange"]
Win32_System_DeploymentServices = ["windows-sys/Win32_System_DeploymentServices"]
Win32_System_DeveloperLicensing = ["windows-sys/Win32_System_DeveloperLicensing"]
Win32_System_Diagnostics = ["windows-sys/Win32_System_Diagnostics"]
Win32_System_Diagnostics_Ceip = ["windows-sys/Win32_System_Diagnostics_Ceip"]
Win32_System_Diagnostics_Debug = ["windows-sys/Win32_System_Diagnostics_Debug"]
Win32_System_Diagnostics_Debug_Extensions = ["windows-sys/Win32_System_Diagnostics_Debug_Extensions"]
Win32_System_Diagnostics_Etw = ["windows-sys/Win32_System_Diagnostics_Etw"]
Win32_System_Diagnostics_ProcessSnapshotting = ["windows-sys/Win32_System_Diagnostics_ProcessSnapshotting"]
Win32_System_Diagnostics_ToolHelp = ["windows-sys/Win32_System_Diagnostics_ToolHelp"]
Win32_System_DistributedTransactionCoordinator = ["windows-sys/Win32_System_DistributedTransactionCoordinator"]
Win32_System_Environment = ["windows-sys/Win32_System_Environment"]
Win32_System_ErrorReporting = ["windows-sys/Win32_System_ErrorReporting"]
Win32_System_EventCollector = ["windows-sys/Win32_System_EventCollector"]
Win32_System_EventLog = ["windows-sys/Win32_System_EventLog"]
Win32_System_EventNotificationService = ["windows-sys/Win32_System_EventNotificationService"]
Win32_System_GroupPolicy = ["windows-sys/Win32_System_GroupPolicy"]
Win32_System_HostCompute = ["windows-sys/Win32_System_HostCompute"]
Win32_System_HostComputeNetwork = ["windows-sys/Win32_System_HostComputeNetwork"]
Win32_System_HostComputeSystem = ["windows-sys/Win32_System_HostComputeSystem"]
Win32_System_Hypervisor = ["windows-sys/Win32_System_Hypervisor"]
Win32_System_IO = ["windows-sys/Win32_System_IO"]
Win32_System_Iis = ["windows-sys/Win32_System_Iis"]
Win32_System_Ioctl = ["windows-sys/Win32_System_Ioctl"]
Win32_System_JobObjects = ["windows-sys/Win32_System_JobObjects"]
Win32_System_Js = ["windows-sys/Win32_System_Js"]
Win32_System_Kernel = ["windows-sys/Win32_System_Kernel"]
Win32_System_LibraryLoader = ["windows-sys/Win32_System_LibraryLoader"]
Win32_System_Mailslots = ["windows-sys/Win32_System_Mailslots"]
Win32_System_Mapi = ["windows-sys/Win32_System_Mapi"]
Win32_System_Memory = ["windows-sys/Win32_System_Memory"]
Win32_System_Memory_NonVolatile = ["windows-sys/Win32_System_Memory_NonVolatile"]
Win32_System_MessageQueuing = ["windows-sys/Win32_System_MessageQueuing"]
Win32_System_MixedReality = ["windows-sys/Win32_System_MixedReality"]
Win32_System_Ole = ["windows-sys/Win32_System_Ole"]
Win32_System_PasswordManagement = ["windows-sys/Win32_System_PasswordManagement"]
Win32_System_Performance = ["windows-sys/Win32_System_Performance"]
Win32_System_Performance_HardwareCounterProfiling = ["windows-sys/Win32_System_Performance_HardwareCounterProfiling"]
Win32_System_Pipes = ["windows-sys/Win32_System_Pipes"]
Win32_System_Power = ["windows-sys/Win32_System_Power"]
Win32_System_ProcessStatus = ["windows-sys/Win32_System_ProcessStatus"]
Win32_System_Recovery = ["windows-sys/Win32_System_Recovery"]
Win32_System_Registry = ["windows-sys/Win32_System_Registry"]
Win32_System_RemoteDesktop = ["windows-sys/Win32_System_RemoteDesktop"]
Win32_System_RemoteManagement = ["windows-sys/Win32_System_RemoteManagement"]
Win32_System_RestartManager = ["windows-sys/Win32_System_RestartManager"]
Win32_System_Restore = ["windows-sys/Win32_System_Restore"]
Win32_System_Rpc = ["windows-sys/Win32_System_Rpc"]
Win32_System_Search = ["windows-sys/Win32_System_Search"]
Win32_System_Search_Common = ["windows-sys/Win32_System_Search_Common"]
Win32_System_SecurityCenter = ["windows-sys/Win32_System_SecurityCenter"]
Win32_System_Services = ["windows-sys/Win32_System_Services"]
Win32_System_SetupAndMigration = ["windows-sys/Win32_System_SetupAndMigration"]
Win32_System_Shutdown = ["windows-sys/Win32_System_Shutdown"]
Win32_System_StationsAndDesktops = ["windows-sys/Win32_System_StationsAndDesktops"]
Win32_System_SubsystemForLinux = ["windows-sys/Win32_System_SubsystemForLinux"]
Win32_System_SystemInformation = ["windows-sys/Win32_System_SystemInformation"]
Win32_System_SystemServices = ["windows-sys/Win32_System_SystemServices"]
Win32_System_Threading = ["windows-sys/Win32_System_Threading"]
Win32_System_Time = ["windows-sys/Win32_System_Time"]
Win32_System_TpmBaseServices = ["windows-sys/Win32_System_TpmBaseServices"]
Win32_System_UserAccessLogging = ["windows-sys/Win32_System_UserAccessLogging"]
Win32_System_VirtualDosMachines = ["windows-sys/Win32_System_VirtualDosMachines"]
Win32_System_WindowsProgramming = ["windows-sys/Win32_System_WindowsProgramming"]
Win32_System_Wmi = ["windows-sys/Win32_System_Wmi"]
Win32_UI = ["windows-sys/Win32_UI"]
Win32_UI_Accessibility = ["windows-sys/Win32_UI_Accessibility"]
Win32_UI_ColorSystem = ["windows-sys/Win32_UI_ColorSystem"]
Win32_UI_Controls = ["windows-sys/Win32_UI_Controls"]
Win32_UI_Controls_Dialogs = ["windows-sys/Win32_UI_Controls_Dialogs"]
Win32_UI_HiDpi = ["windows-sys/Win32_UI_HiDpi"]
Win32_UI_Input = ["windows-sys/Win32_UI_Input"]
Win32_UI_Input_Ime = ["windows-sys/Win32_UI_Input_Ime"]
Win32_UI_Input_KeyboardAndMouse = ["windows-sys/Win32_UI_Input_KeyboardAndMouse"]
Win32_UI_Input_Pointer = ["windows-sys/Win32_UI_Input_Pointer"]
Win32_UI_Input_Touch = ["windows-sys/Win32_UI_Input_Touch"]
Win32_UI_Input_XboxController = ["windows-sys/Win32_UI_Input_XboxController"]
Win32_UI_InteractionContext = ["windows-sys/Win32_UI_InteractionContext"]
Win32_UI_Magnification = ["windows-sys/Win32_UI_Magnification"]
Win32_UI_Shell = ["windows-sys/Win32_UI_Shell"]
Win32_UI_Shell_PropertiesSystem = ["windows-sys/Win32_UI_Shell_PropertiesSystem"]
Win32_UI_TabletPC = ["windows-sys/Win32_UI_TabletPC"]
Win32_UI_TextServices = ["windows-sys/Win32_UI_TextServices"]
Win32_UI_WindowsAndMessaging = ["windows-sys/Win32_UI_WindowsAndMessaging"]
Win32_Web = ["windows-sys/Win32_Web"]
Win32_Web_InternetExplorer = ["windows-sys/Win32_Web_InternetExplorer"]
default = ["windows-sys/default"]

View File

@ -1,5 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub use windows_sys::*;

View File

@ -43,7 +43,7 @@ indexmap = { version = "2", default-features = false, features = ["serde", "std"
libc = { version = "0.2", features = ["extra_traits"] }
lmdb-rkv-sys = { version = "0.11", default-features = false, features = ["mdb_idl_logn_9"], optional = true }
log = { version = "0.4", features = ["release_max_level_info", "std"], optional = true }
mio = { version = "0.8", features = ["log"], optional = true }
mio = { version = "1", features = ["log"], optional = true }
num-integer = { version = "0.1", optional = true }
num-traits = { version = "0.2", features = ["i128", "libm"], optional = true }
once_cell = { version = "1", optional = true }
@ -59,7 +59,7 @@ stable_deref_trait = { version = "1", features = ["std"], optional = true }
strsim = { version = "0.10", optional = true }
time = { version = "0.3", features = ["macros", "parsing", "serde"], optional = true }
tinystr = { version = "0.7", features = ["zerovec"], optional = true }
tokio = { version = "1", features = ["fs", "macros", "num_cpus", "rt-multi-thread"], optional = true }
tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread"], optional = true }
tokio-util = { version = "0.7", features = ["io"], optional = true }
tracing = { version = "0.1", features = ["log"], optional = true }
unic-langid = { version = "0.9", features = ["likelysubtags"], optional = true }

View File

@ -3018,8 +3018,7 @@ delta = "0.8.0 -> 0.8.6"
[[audits.mio]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.8.8 -> 0.8.8@git:9a2ef335c366044ffe73b1c4acabe50a1daefe05"
importable = false
delta = "0.8.8 -> 1.0.1"
[[audits.moz_cbor]]
who = "Bobby Holley <bobbyholley@gmail.com>"
@ -5739,11 +5738,17 @@ user-id = 3618 # David Tolnay (dtolnay)
start = "2020-06-16"
end = "2024-04-25"
[[trusted.tokio]]
criteria = "safe-to-run"
user-id = 6741 # Alice Ryhl (Darksonn)
start = "2020-12-25"
end = "2025-07-30"
[[trusted.tokio-macros]]
criteria = "safe-to-deploy"
user-id = 6741 # Alice Ryhl (Darksonn)
start = "2020-10-26"
end = "2024-05-05"
end = "2025-07-30"
[[trusted.tokio-util]]
criteria = "safe-to-deploy"

View File

@ -110,10 +110,6 @@ notes = "This is a first-party crate which is also published to crates.io. We ce
audit-as-crates-io = true
notes = "This is a pinned version of the upstream code, presumably to get a fix that hadn't been released yet. We should consider switching to the latest official release."
[policy."mio:0.8.8@git:9a2ef335c366044ffe73b1c4acabe50a1daefe05"]
audit-as-crates-io = true
notes = "This is 0.8.8 + https://github.com/tokio-rs/mio/commit/eea9e3e0c469480e5c59c01e6c3c7e5fd88f0848."
[policy.mozbuild]
audit-as-crates-io = false
notes = "The crates.io version of this is just a placeholder to allow public crates to depend on mozbuild."

View File

@ -594,6 +594,20 @@ user-id = 3618
user-login = "dtolnay"
user-name = "David Tolnay"
[[publisher.tokio]]
version = "1.39.2"
when = "2024-07-27"
user-id = 6741
user-login = "Darksonn"
user-name = "Alice Ryhl"
[[publisher.tokio-macros]]
version = "2.4.0"
when = "2024-07-23"
user-id = 6741
user-login = "Darksonn"
user-name = "Alice Ryhl"
[[publisher.tokio-util]]
version = "0.7.2"
when = "2022-05-15"

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,103 @@
# 1.0.1
* Added Fuchsia support
(https://github.com/tokio-rs/mio/pull/1811).
* Added GNU/Hurd support
(https://github.com/tokio-rs/mio/pull/1816).
* Fixed an issue where accepting on a UDS socket without sometime pass an address
with a NULL byte to SocketAddr::from_pathname
(https://github.com/tokio-rs/mio/pull/1817).
* Internal cleanups that should make the `cfg` sitations easier to follow
(https://github.com/tokio-rs/mio/pull/1812,
https://github.com/tokio-rs/mio/pull/1813).
# 1.0
With v1 Mio is able to bump its MSRV to 1.70, allowing us to implement I/O
safety traits (https://github.com/rust-lang/rust/issues/87074) and replace
`SocketAddr` with the version found in the standard library.
## Added
* Implement `AsFd` for`TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`,
`UnixListener`, `UnixStream`, `pipe::Receiver` and `pipe::Sender`
(https://github.com/tokio-rs/mio/pull/1749, https://github.com/tokio-rs/mio/pull/1797).
* Implement `From` for `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`,
`UnixListener`, and `UnixStream` for their standard library counterpart
(https://github.com/tokio-rs/mio/pull/1767).
* Add support for abstract namespaces on Android
(https://github.com/tokio-rs/mio/pull/1749).
* Add support for QNX OS
(https://github.com/tokio-rs/mio/pull/1766,
https://github.com/tokio-rs/mio/pull/1800).
* Add support for Apple visionOS
(https://github.com/tokio-rs/mio/pull/1795).
* Support for Haiku
(https://github.com/tokio-rs/mio/pull/1807).
## Removed
* The `SocketAddr` type is removed in favour of `std::os::unix::net::SocketAddr`
(https://github.com/tokio-rs/mio/pull/1760). All methods on Mio's version
should exist on the version in the standard library.
## Changes
* MSRV was updated to 1.74, updating to Rust edition edition
(https://github.com/tokio-rs/mio/pull/1733).
* `UnixDatagram::{local_addr,peer_addr,bind_addr,recv_from}`,
`UnixListener::{local_addr,bind_addr,accept}` and
`UnixStream::{local_addr,peer_addr,connect_addr}` return and/or use
`std::os::unix::net::SocketAddr` instead of Mio's own `SocketAddr` type
(https://github.com/tokio-rs/mio/pull/1760).
* Use `OwnedFd` internally for `Poll` where possible
(https://github.com/tokio-rs/mio/pull/1749).
* Support ESP-IDF and Hermit without cfg flags
(https://github.com/tokio-rs/mio/pull/1789,
https://github.com/tokio-rs/mio/pull/1802,
https://github.com/tokio-rs/mio/pull/1802).
* Updated windows-sys to v0.52
(https://github.com/tokio-rs/mio/pull/1668).
# 0.8.11
* Fix receiving IOCP events after deregistering a Windows named pipe
(https://github.com/tokio-rs/mio/pull/1760, backport pr:
https://github.com/tokio-rs/mio/pull/1761).
# 0.8.10
## Added
* Solaris support
(https://github.com/tokio-rs/mio/pull/1724).
# 0.8.9
## Added
* ESP-IDF framework support
(https://github.com/tokio-rs/mio/pull/1692).
* AIX operating system support
(https://github.com/tokio-rs/mio/pull/1704).
* Vita support
(https://github.com/tokio-rs/mio/pull/1721).
* `{UnixListener,UnixStream}:bind_addr`
(https://github.com/tokio-rs/mio/pull/1630).
* `mio_unsupported_force_poll_poll` and `mio_unsupported_force_waker_pipe`
**unsupported** configuration flags to force a specific poll or waker
implementation
(https://github.com/tokio-rs/mio/pull/1684,
https://github.com/tokio-rs/mio/pull/1685,
https://github.com/tokio-rs/mio/pull/1692).
## Fixed
* The `pipe(2)` based waker (swapped file descriptors)
(https://github.com/tokio-rs/mio/pull/1722).
* The duplicate waker check to work correctly with cloned `Registry`s.
(https://github.com/tokio-rs/mio/pull/1706).
# 0.8.8
## Fixed
@ -400,7 +500,7 @@ information.
at least `event::Source` and `Poll`.
* Mio now uses Rust 2018 and rustfmt for all code.
* `Event` was changed to be a wrapper around the OS event. This means it can be
significantly larger on some OSes.
significantly larger on some OSs.
* `Ready` was removed and replaced with various `is_*` methods on `Event`. For
example instead checking for readable readiness using
`Event::ready().is_readable()`, you would call `Event::is_readable()`.

175
third_party/rust/mio/Cargo.lock generated vendored Normal file
View File

@ -0,0 +1,175 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"log",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "mio"
version = "1.0.1"
dependencies = [
"env_logger",
"hermit-abi",
"libc",
"log",
"rand",
"wasi",
"windows-sys",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

View File

@ -10,14 +10,16 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
edition = "2021"
rust-version = "1.70"
name = "mio"
version = "0.8.8"
version = "1.0.1"
authors = [
"Carl Lerche <me@carllerche.com>",
"Thomas de Zeeuw <thomasdezeeuw@gmail.com>",
"Tokio Contributors <team@tokio.rs>",
]
build = false
include = [
"Cargo.toml",
"LICENSE",
@ -26,6 +28,10 @@ include = [
"src/**/*.rs",
"examples/**/*.rs",
]
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Lightweight non-blocking I/O."
homepage = "https://github.com/tokio-rs/mio"
readme = "README.md"
@ -43,12 +49,14 @@ all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
"--generate-link-to-definition",
]
targets = [
"aarch64-apple-ios",
"aarch64-linux-android",
"wasm32-wasi",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnu",
"x86_64-pc-windows-msvc",
"x86_64-unknown-dragonfly",
"x86_64-unknown-freebsd",
@ -56,6 +64,7 @@ targets = [
"x86_64-unknown-linux-gnu",
"x86_64-unknown-netbsd",
"x86_64-unknown-openbsd",
"x86_64-unknown-hermit",
]
[package.metadata.playground]
@ -65,15 +74,21 @@ features = [
"net",
]
[lib]
name = "mio"
path = "src/lib.rs"
[[example]]
name = "tcp_server"
name = "tcp_listenfd_server"
path = "examples/tcp_listenfd_server.rs"
required-features = [
"os-poll",
"net",
]
[[example]]
name = "tcp_listenfd_server"
name = "tcp_server"
path = "examples/tcp_server.rs"
required-features = [
"os-poll",
"net",
@ -81,6 +96,7 @@ required-features = [
[[example]]
name = "udp_server"
path = "examples/udp_server.rs"
required-features = [
"os-poll",
"net",
@ -90,13 +106,13 @@ required-features = [
version = "0.4.8"
optional = true
[dev-dependencies]
rand = "0.8"
[dev-dependencies.env_logger]
version = "0.9.3"
default-features = false
[dev-dependencies.rand]
version = "0.8"
[features]
default = ["log"]
net = []
@ -107,12 +123,18 @@ os-ext = [
]
os-poll = []
[target."cfg(target_os = \"wasi\")".dependencies]
libc = "0.2.121"
wasi = "0.11.0"
[target.'cfg(target_os = "hermit")'.dependencies.libc]
version = "0.3.9"
package = "hermit-abi"
[target."cfg(unix)".dependencies]
libc = "0.2.121"
[target.'cfg(target_os = "wasi")'.dependencies.libc]
version = "0.2.149"
[target.'cfg(target_os = "wasi")'.dependencies.wasi]
version = "0.11.0"
[target."cfg(unix)".dependencies.libc]
version = "0.2.149"
[target."cfg(windows)".dependencies.windows-sys]
version = "0.52"
@ -126,3 +148,11 @@ features = [
"Win32_System_IO",
"Win32_System_WindowsProgramming",
]
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = [
"cfg(mio_unsupported_force_poll_poll)",
"cfg(mio_unsupported_force_waker_pipe)",
]

View File

@ -20,8 +20,8 @@ overhead as possible over the OS abstractions.
**API documentation**
* [v1](https://docs.rs/mio/^1)
* [v0.8](https://docs.rs/mio/^0.8)
* [v0.7](https://docs.rs/mio/^0.7)
This is a low level library, if you are looking for something easier to get
started with, see [Tokio](https://tokio.rs).
@ -32,7 +32,7 @@ To use `mio`, first add this to your `Cargo.toml`:
```toml
[dependencies]
mio = "0.8"
mio = "1"
```
Next we can start using Mio. The following is quick introduction using
@ -110,7 +110,7 @@ fn main() -> Result<(), Box<dyn Error>> {
## Features
* Non-blocking TCP, UDP
* Non-blocking TCP, UDP, UDS
* I/O event queue backed by epoll, kqueue, and IOCP
* Zero allocations at runtime
* Platform specific extensions
@ -138,13 +138,13 @@ Currently supported platforms:
* iOS
* macOS
There are potentially others. If you find that Mio works on another
platform, submit a PR to update the list!
Mio can handle interfacing with each of the event systems of the aforementioned
platforms. The details of their implementation are further discussed in the
`Poll` type of the API documentation (see above).
Mio generally supports the same versions of the above mentioned platforms as
Rust the language (rustc) does, unless otherwise noted.
The Windows implementation for polling sockets is using the [wepoll] strategy.
This uses the Windows AFD system to access socket readiness events.
@ -152,20 +152,54 @@ This uses the Windows AFD system to access socket readiness events.
### Unsupported
* Haiku, see [issue #1472]
* Solaris, see [issue #1152]
* Wine, see [issue #1444]
[issue #1472]: https://github.com/tokio-rs/mio/issues/1472
[issue #1152]: https://github.com/tokio-rs/mio/issues/1152
[issue #1444]: https://github.com/tokio-rs/mio/issues/1444
## MSRV Policy
The MSRV (Minimum Supported Rust Version) is fixed for a given minor (1.x)
version. However it can be increased when bumping minor versions, i.e. going
from 1.0 to 1.1 allows us to increase the MSRV. Users unable to increase their
Rust version can use an older minor version instead. Below is a list of Mio versions
and their MSRV:
* v0.8: Rust 1.46.
* v1.0: Rust 1.70.
Note however that Mio also has dependencies, which might have different MSRV
policies. We try to stick to the above policy when updating dependencies, but
this is not always possible.
## Unsupported flags
Mio uses different implementations to support the same functionality depending
on the platform. Mio generally uses the "best" implementation possible, where
"best" usually means most efficient for Mio's use case. However this means that
the implementation is often specific to a limited number of platforms, meaning
we often have multiple implementations for the same functionality. In some cases
it might be required to not use the "best" implementation, but another
implementation Mio supports (on other platforms). **Mio does not officially
support secondary implementations on platforms**, however we do have various cfg
flags to force another implementation for these situations.
Current flags:
* `mio_unsupported_force_poll_poll`, uses an implementation based on `poll(2)`
for `mio::Poll`.
* `mio_unsupported_force_waker_pipe`, uses an implementation based on `pipe(2)`
for `mio::Waker`.
**Again, Mio does not officially supports this**. Furthermore these flags may
disappear in the future.
## Community
A group of Mio users hang out on [Discord], this can be a good place to go for
questions.
questions. It's also possible to open a [new issue on GitHub] to ask questions,
report bugs or suggest new features.
[Discord]: https://discord.gg/tokio
[new issue on GitHub]: https://github.com/tokio-rs/mio/issues/new
## Contributing

View File

@ -19,10 +19,8 @@ const DATA: &[u8] = b"Hello world!\n";
#[cfg(not(windows))]
fn get_first_listen_fd_listener() -> Option<std::net::TcpListener> {
#[cfg(unix)]
use std::os::unix::io::FromRawFd;
#[cfg(target_os = "wasi")]
use std::os::wasi::io::FromRawFd;
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
use std::os::fd::FromRawFd;
let stdlistener = unsafe { std::net::TcpListener::from_raw_fd(3) };
stdlistener.set_nonblocking(true).unwrap();

View File

@ -40,7 +40,12 @@ fn main() -> io::Result<()> {
println!("You'll see our welcome message and anything you type will be printed here.");
loop {
poll.poll(&mut events, None)?;
if let Err(err) = poll.poll(&mut events, None) {
if interrupted(&err) {
continue;
}
return Err(err);
}
for event in events.iter() {
match event.token() {

View File

@ -40,7 +40,12 @@ fn main() -> io::Result<()> {
// Our event loop.
loop {
// Poll to check if we have events waiting for us.
poll.poll(&mut events, None)?;
if let Err(err) = poll.poll(&mut events, None) {
if err.kind() == io::ErrorKind::Interrupted {
continue;
}
return Err(err);
}
// Process each event.
for event in events.iter() {

View File

@ -28,8 +28,8 @@ impl Event {
///
/// # Notes
///
/// Out-of-band (OOB) data also triggers readable events. But must
/// application don't actually read OOB data, this could leave an
/// Out-of-band (OOB) data also triggers readable events. But most
/// applications don't actually read OOB data, this could leave an
/// application open to a Denial-of-Service (Dos) attack, see
/// <https://github.com/sandstorm-io/sandstorm-website/blob/58f93346028c0576e8147627667328eaaf4be9fa/_posts/2015-04-08-osx-security-bug.md>.
/// However because Mio uses edge-triggers it will not result in an infinite

View File

@ -38,6 +38,7 @@ impl Interest {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
pub const AIO: Interest = Interest(unsafe { NonZeroU8::new_unchecked(AIO) });
@ -63,6 +64,7 @@ impl Interest {
/// # silent_dead_code_warning(INTERESTS)
/// ```
#[allow(clippy::should_implement_trait)]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn add(self, other: Interest) -> Interest {
Interest(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
}
@ -87,31 +89,37 @@ impl Interest {
/// // Its also possible to remove multiple interests at once.
/// assert_eq!(RW_INTERESTS.remove(RW_INTERESTS), None);
/// ```
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn remove(self, other: Interest) -> Option<Interest> {
NonZeroU8::new(self.0.get() & !other.0.get()).map(Interest)
}
/// Returns true if the value includes readable readiness.
#[must_use]
pub const fn is_readable(self) -> bool {
(self.0.get() & READABLE) != 0
}
/// Returns true if the value includes writable readiness.
#[must_use]
pub const fn is_writable(self) -> bool {
(self.0.get() & WRITABLE) != 0
}
/// Returns true if `Interest` contains AIO readiness.
#[must_use]
pub const fn is_aio(self) -> bool {
(self.0.get() & AIO) != 0
}
/// Returns true if `Interest` contains LIO readiness.
#[must_use]
pub const fn is_lio(self) -> bool {
(self.0.get() & LIO) != 0
}
/// Returns true if `Interest` contains priority readiness.
#[must_use]
pub const fn is_priority(self) -> bool {
(self.0.get() & PRIORITY) != 0
}
@ -156,6 +164,7 @@ impl fmt::Debug for Interest {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
{
@ -167,7 +176,7 @@ impl fmt::Debug for Interest {
one = true
}
}
#[cfg(any(target_os = "freebsd"))]
#[cfg(target_os = "freebsd")]
{
if self.is_lio() {
if one {

View File

@ -1,8 +1,10 @@
use std::ops::{Deref, DerefMut};
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(target_os = "wasi")]
use std::os::wasi::io::AsRawFd;
#[cfg(any(unix, target_os = "wasi"))]
use std::os::fd::AsRawFd;
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::AsRawFd;
#[cfg(windows)]
use std::os::windows::io::AsRawSocket;
#[cfg(debug_assertions)]
@ -21,7 +23,7 @@ use crate::{event, Interest, Registry, Token};
/// Mio supports registering any FD or socket that can be registered with the
/// underlying OS selector. `IoSource` provides the necessary bridge.
///
/// [`RawFd`]: std::os::unix::io::RawFd
/// [`RawFd`]: std::os::fd::RawFd
/// [`RawSocket`]: std::os::windows::io::RawSocket
///
/// # Notes
@ -32,33 +34,6 @@ use crate::{event, Interest, Registry, Token};
///
/// [`Poll`]: crate::Poll
/// [`do_io`]: IoSource::do_io
/*
///
/// # Examples
///
/// Basic usage.
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Interest, Poll, Token};
/// use mio::IoSource;
///
/// use std::net;
///
/// let poll = Poll::new()?;
///
/// // Bind a std TCP listener.
/// let listener = net::TcpListener::bind("127.0.0.1:0")?;
/// // Wrap it in the `IoSource` type.
/// let mut listener = IoSource::new(listener);
///
/// // Register the listener.
/// poll.registry().register(&mut listener, Token(0), Interest::READABLE)?;
/// # Ok(())
/// # }
/// ```
*/
pub struct IoSource<T> {
state: IoSourceState,
inner: T,
@ -129,7 +104,7 @@ impl<T> DerefMut for IoSource<T> {
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit"))]
impl<T> event::Source for IoSource<T>
where
T: AsRawFd,
@ -142,9 +117,8 @@ where
) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.associate(registry)?;
registry
.selector()
.register(self.inner.as_raw_fd(), token, interests)
self.state
.register(registry, token, interests, self.inner.as_raw_fd())
}
fn reregister(
@ -155,15 +129,14 @@ where
) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.check_association(registry)?;
registry
.selector()
.reregister(self.inner.as_raw_fd(), token, interests)
self.state
.reregister(registry, token, interests, self.inner.as_raw_fd())
}
fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.remove_association(registry)?;
registry.selector().deregister(self.inner.as_raw_fd())
self.state.deregister(registry, self.inner.as_raw_fd())
}
}

View File

@ -85,6 +85,14 @@ pub mod unix {
pub use crate::sys::SourceFd;
}
#[cfg(all(target_os = "hermit", feature = "os-ext"))]
#[cfg_attr(docsrs, doc(cfg(all(target_os = "hermit", feature = "os-ext"))))]
pub mod hermit {
//! Hermit only extensions.
pub use crate::sys::SourceFd;
}
#[cfg(all(windows, feature = "os-ext"))]
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "os-ext"))))]
pub mod windows {

View File

@ -51,8 +51,8 @@ macro_rules! cfg_net {
macro_rules! cfg_io_source {
($($item:item)*) => {
$(
#[cfg(any(feature = "net", all(unix, feature = "os-ext")))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "net", all(unix, feature = "os-ext")))))]
#[cfg(any(feature = "net", all(any(unix, target_os = "hermit"), feature = "os-ext")))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "net", all(any(unix, target_os = "hermit"), feature = "os-ext")))))]
$item
)*
}

View File

@ -36,4 +36,4 @@ pub use self::udp::UdpSocket;
#[cfg(unix)]
mod uds;
#[cfg(unix)]
pub use self::uds::{SocketAddr, UnixDatagram, UnixListener, UnixStream};
pub use self::uds::{UnixDatagram, UnixListener, UnixStream};

View File

@ -1,15 +1,17 @@
use std::net::{self, SocketAddr};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(any(unix, target_os = "wasi"))]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::{fmt, io};
use crate::io_source::IoSource;
use crate::net::TcpStream;
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit"))]
use crate::sys::tcp::set_reuseaddr;
#[cfg(not(target_os = "wasi"))]
use crate::sys::tcp::{bind, listen, new_for_addr};
@ -58,7 +60,7 @@ impl TcpListener {
#[cfg(not(target_os = "wasi"))]
pub fn bind(addr: SocketAddr) -> io::Result<TcpListener> {
let socket = new_for_addr(addr)?;
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit"))]
let listener = unsafe { TcpListener::from_raw_fd(socket) };
#[cfg(windows)]
let listener = unsafe { TcpListener::from_raw_socket(socket as _) };
@ -166,21 +168,21 @@ impl fmt::Debug for TcpListener {
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl FromRawFd for TcpListener {
/// Converts a `RawFd` to a `TcpListener`.
///
@ -193,6 +195,13 @@ impl FromRawFd for TcpListener {
}
}
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl AsFd for TcpListener {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
#[cfg(windows)]
impl IntoRawSocket for TcpListener {
fn into_raw_socket(self) -> RawSocket {
@ -220,29 +229,20 @@ impl FromRawSocket for TcpListener {
}
}
#[cfg(target_os = "wasi")]
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[cfg(target_os = "wasi")]
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(target_os = "wasi")]
impl FromRawFd for TcpListener {
/// Converts a `RawFd` to a `TcpListener`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
TcpListener::from_std(FromRawFd::from_raw_fd(fd))
impl From<TcpListener> for net::TcpListener {
fn from(listener: TcpListener) -> Self {
// Safety: This is safe since we are extracting the raw fd from a well-constructed
// mio::net::TcpListener which ensures that we actually pass in a valid file
// descriptor/socket
unsafe {
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
{
net::TcpListener::from_raw_fd(listener.into_raw_fd())
}
#[cfg(windows)]
{
net::TcpListener::from_raw_socket(listener.into_raw_socket())
}
}
}
}

View File

@ -1,10 +1,12 @@
use std::fmt;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::net::{self, Shutdown, SocketAddr};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(any(unix, target_os = "wasi"))]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
@ -68,12 +70,15 @@ impl TcpStream {
/// 1. Call `TcpStream::connect`
/// 2. Register the returned stream with at least [write interest].
/// 3. Wait for a (writable) event.
/// 4. Check `TcpStream::peer_addr`. If it returns `libc::EINPROGRESS` or
/// 4. Check `TcpStream::take_error`. If it returns an error, then
/// something went wrong. If it returns `Ok(None)`, then proceed to
/// step 5.
/// 5. Check `TcpStream::peer_addr`. If it returns `libc::EINPROGRESS` or
/// `ErrorKind::NotConnected` it means the stream is not yet connected,
/// go back to step 3. If it returns an address it means the stream is
/// connected, go to step 5. If another error is returned something
/// connected, go to step 6. If another error is returned something
/// went wrong.
/// 5. Now the stream can be used.
/// 6. Now the stream can be used.
///
/// This may return a `WouldBlock` in which case the socket connection
/// cannot be completed immediately, it usually means there are insufficient
@ -83,7 +88,7 @@ impl TcpStream {
#[cfg(not(target_os = "wasi"))]
pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
let socket = new_for_addr(addr)?;
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit"))]
let stream = unsafe { TcpStream::from_raw_fd(socket) };
#[cfg(windows)]
let stream = unsafe { TcpStream::from_raw_socket(socket as _) };
@ -227,8 +232,8 @@ impl TcpStream {
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// #[cfg(unix)]
/// use std::os::unix::io::AsRawFd;
/// #[cfg(any(unix, target_os = "wasi"))]
/// use std::os::fd::AsRawFd;
/// #[cfg(windows)]
/// use std::os::windows::io::AsRawSocket;
/// use mio::net::TcpStream;
@ -345,21 +350,21 @@ impl fmt::Debug for TcpStream {
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl FromRawFd for TcpStream {
/// Converts a `RawFd` to a `TcpStream`.
///
@ -372,6 +377,13 @@ impl FromRawFd for TcpStream {
}
}
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl AsFd for TcpStream {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
#[cfg(windows)]
impl IntoRawSocket for TcpStream {
fn into_raw_socket(self) -> RawSocket {
@ -399,29 +411,20 @@ impl FromRawSocket for TcpStream {
}
}
#[cfg(target_os = "wasi")]
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[cfg(target_os = "wasi")]
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(target_os = "wasi")]
impl FromRawFd for TcpStream {
/// Converts a `RawFd` to a `TcpStream`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
TcpStream::from_std(FromRawFd::from_raw_fd(fd))
impl From<TcpStream> for net::TcpStream {
fn from(stream: TcpStream) -> Self {
// Safety: This is safe since we are extracting the raw fd from a well-constructed
// mio::net::TcpStream which ensures that we actually pass in a valid file
// descriptor/socket
unsafe {
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
{
net::TcpStream::from_raw_fd(stream.into_raw_fd())
}
#[cfg(windows)]
{
net::TcpStream::from_raw_socket(stream.into_raw_socket())
}
}
}
}

View File

@ -7,17 +7,19 @@
//!
//! [portability guidelines]: ../struct.Poll.html#portability
use crate::io_source::IoSource;
use crate::{event, sys, Interest, Registry, Token};
use std::fmt;
use std::io;
use std::net;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(any(unix, target_os = "wasi"))]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::{fmt, io, net};
use crate::io_source::IoSource;
use crate::{event, sys, Interest, Registry, Token};
/// A User Datagram Protocol socket.
///
@ -572,8 +574,8 @@ impl UdpSocket {
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// #[cfg(unix)]
/// use std::os::unix::io::AsRawFd;
/// #[cfg(any(unix, target_os = "wasi"))]
/// use std::os::fd::AsRawFd;
/// #[cfg(windows)]
/// use std::os::windows::io::AsRawSocket;
/// use mio::net::UdpSocket;
@ -642,21 +644,21 @@ impl fmt::Debug for UdpSocket {
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl IntoRawFd for UdpSocket {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl AsRawFd for UdpSocket {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl FromRawFd for UdpSocket {
/// Converts a `RawFd` to a `UdpSocket`.
///
@ -669,6 +671,13 @@ impl FromRawFd for UdpSocket {
}
}
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
impl AsFd for UdpSocket {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
#[cfg(windows)]
impl IntoRawSocket for UdpSocket {
fn into_raw_socket(self) -> RawSocket {
@ -695,3 +704,21 @@ impl FromRawSocket for UdpSocket {
UdpSocket::from_std(FromRawSocket::from_raw_socket(socket))
}
}
impl From<UdpSocket> for net::UdpSocket {
fn from(socket: UdpSocket) -> Self {
// Safety: This is safe since we are extracting the raw fd from a well-constructed
// mio::net::UdpSocket which ensures that we actually pass in a valid file
// descriptor/socket
unsafe {
#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
{
net::UdpSocket::from_raw_fd(socket.into_raw_fd())
}
#[cfg(windows)]
{
net::UdpSocket::from_raw_socket(socket.into_raw_socket())
}
}
}
}

View File

@ -1,12 +1,12 @@
use crate::io_source::IoSource;
use crate::{event, sys, Interest, Registry, Token};
use std::net::Shutdown;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;
use std::{fmt, io};
use crate::io_source::IoSource;
use crate::{event, sys, Interest, Registry, Token};
/// A Unix datagram socket.
pub struct UnixDatagram {
inner: IoSource<net::UnixDatagram>,
@ -15,7 +15,13 @@ pub struct UnixDatagram {
impl UnixDatagram {
/// Creates a Unix datagram socket bound to the given path.
pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
sys::uds::datagram::bind(path.as_ref()).map(UnixDatagram::from_std)
let addr = SocketAddr::from_pathname(path)?;
UnixDatagram::bind_addr(&addr)
}
/// Creates a new `UnixDatagram` bound to the specified socket `address`.
pub fn bind_addr(address: &SocketAddr) -> io::Result<UnixDatagram> {
sys::uds::datagram::bind_addr(address).map(UnixDatagram::from_std)
}
/// Creates a new `UnixDatagram` from a standard `net::UnixDatagram`.
@ -54,24 +60,23 @@ impl UnixDatagram {
}
/// Returns the address of this socket.
pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::datagram::local_addr(&self.inner)
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
/// Returns the address of this socket's peer.
///
/// The `connect` method will connect the socket to a peer.
pub fn peer_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::datagram::peer_addr(&self.inner)
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.inner.peer_addr()
}
/// Receives data from the socket.
///
/// On success, returns the number of bytes read and the address from
/// whence the data came.
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, sys::SocketAddr)> {
self.inner
.do_io(|inner| sys::uds::datagram::recv_from(inner, buf))
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.inner.do_io(|inner| inner.recv_from(buf))
}
/// Receives data from the socket.
@ -130,7 +135,7 @@ impl UnixDatagram {
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// use std::os::unix::io::AsRawFd;
/// use std::os::fd::AsRawFd;
/// use mio::net::UnixDatagram;
///
/// let (dgram1, dgram2) = UnixDatagram::pair()?;
@ -234,3 +239,18 @@ impl FromRawFd for UnixDatagram {
UnixDatagram::from_std(FromRawFd::from_raw_fd(fd))
}
}
impl From<UnixDatagram> for net::UnixDatagram {
fn from(datagram: UnixDatagram) -> Self {
// Safety: This is safe since we are extracting the raw fd from a well-constructed
// mio::net::uds::UnixListener which ensures that we actually pass in a valid file
// descriptor/socket
unsafe { net::UnixDatagram::from_raw_fd(datagram.into_raw_fd()) }
}
}
impl AsFd for UnixDatagram {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}

View File

@ -1,21 +1,27 @@
use crate::io_source::IoSource;
use crate::net::{SocketAddr, UnixStream};
use crate::{event, sys, Interest, Registry, Token};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;
use std::{fmt, io};
use crate::io_source::IoSource;
use crate::net::UnixStream;
use crate::{event, sys, Interest, Registry, Token};
/// A non-blocking Unix domain socket server.
pub struct UnixListener {
inner: IoSource<net::UnixListener>,
}
impl UnixListener {
/// Creates a new `UnixListener` bound to the specified socket.
/// Creates a new `UnixListener` bound to the specified socket `path`.
pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
sys::uds::listener::bind(path.as_ref()).map(UnixListener::from_std)
let addr = SocketAddr::from_pathname(path)?;
UnixListener::bind_addr(&addr)
}
/// Creates a new `UnixListener` bound to the specified socket `address`.
pub fn bind_addr(address: &SocketAddr) -> io::Result<UnixListener> {
sys::uds::listener::bind_addr(address).map(UnixListener::from_std)
}
/// Creates a new `UnixListener` from a standard `net::UnixListener`.
@ -39,8 +45,8 @@ impl UnixListener {
}
/// Returns the local socket address of this listener.
pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::listener::local_addr(&self.inner)
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
/// Returns the value of the `SO_ERROR` option.
@ -102,3 +108,18 @@ impl FromRawFd for UnixListener {
UnixListener::from_std(FromRawFd::from_raw_fd(fd))
}
}
impl From<UnixListener> for net::UnixListener {
fn from(listener: UnixListener) -> Self {
// Safety: This is safe since we are extracting the raw fd from a well-constructed
// mio::net::uds::UnixListener which ensures that we actually pass in a valid file
// descriptor/socket
unsafe { net::UnixListener::from_raw_fd(listener.into_raw_fd()) }
}
}
impl AsFd for UnixListener {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}

View File

@ -6,5 +6,3 @@ pub use self::listener::UnixListener;
mod stream;
pub use self::stream::UnixStream;
pub use crate::sys::SocketAddr;

View File

@ -1,13 +1,13 @@
use crate::io_source::IoSource;
use crate::{event, sys, Interest, Registry, Token};
use std::fmt;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::net::Shutdown;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;
use crate::io_source::IoSource;
use crate::{event, sys, Interest, Registry, Token};
/// A non-blocking Unix stream socket.
pub struct UnixStream {
inner: IoSource<net::UnixStream>,
@ -19,7 +19,16 @@ impl UnixStream {
/// This may return a `WouldBlock` in which case the socket connection
/// cannot be completed immediately. Usually it means the backlog is full.
pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
sys::uds::stream::connect(path.as_ref()).map(UnixStream::from_std)
let addr = SocketAddr::from_pathname(path)?;
UnixStream::connect_addr(&addr)
}
/// Connects to the socket named by `address`.
///
/// This may return a `WouldBlock` in which case the socket connection
/// cannot be completed immediately. Usually it means the backlog is full.
pub fn connect_addr(address: &SocketAddr) -> io::Result<UnixStream> {
sys::uds::stream::connect_addr(address).map(UnixStream::from_std)
}
/// Creates a new `UnixStream` from a standard `net::UnixStream`.
@ -50,13 +59,13 @@ impl UnixStream {
}
/// Returns the socket address of the local half of this connection.
pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::stream::local_addr(&self.inner)
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
/// Returns the socket address of the remote half of this connection.
pub fn peer_addr(&self) -> io::Result<sys::SocketAddr> {
sys::uds::stream::peer_addr(&self.inner)
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.inner.peer_addr()
}
/// Returns the value of the `SO_ERROR` option.
@ -91,7 +100,7 @@ impl UnixStream {
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// use std::os::unix::io::AsRawFd;
/// use std::os::fd::AsRawFd;
/// use mio::net::UnixStream;
///
/// let (stream1, stream2) = UnixStream::pair()?;
@ -243,3 +252,18 @@ impl FromRawFd for UnixStream {
UnixStream::from_std(FromRawFd::from_raw_fd(fd))
}
}
impl From<UnixStream> for net::UnixStream {
fn from(stream: UnixStream) -> Self {
// Safety: This is safe since we are extracting the raw fd from a well-constructed
// mio::net::uds::UnixStream which ensures that we actually pass in a valid file
// descriptor/socket
unsafe { net::UnixStream::from_raw_fd(stream.into_raw_fd()) }
}
}
impl AsFd for UnixStream {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}

View File

@ -1,9 +1,27 @@
use crate::{event, sys, Events, Interest, Token};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(all(
unix,
not(mio_unsupported_force_poll_poll),
not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hermit",
target_os = "hurd",
target_os = "nto",
target_os = "solaris",
target_os = "vita"
)),
))]
use std::os::fd::{AsRawFd, RawFd};
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
use std::sync::Arc;
use std::time::Duration;
use std::{fmt, io};
use crate::{event, sys, Events, Interest, Token};
/// Polls for readiness events on all registered values.
///
/// `Poll` allows a program to monitor a large number of [`event::Source`]s,
@ -252,6 +270,9 @@ pub struct Poll {
/// Registers I/O resources.
pub struct Registry {
selector: sys::Selector,
/// Whether this selector currently has an associated waker.
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
has_waker: Arc<AtomicBool>,
}
impl Poll {
@ -298,7 +319,11 @@ impl Poll {
/// ```
pub fn new() -> io::Result<Poll> {
sys::Selector::new().map(|selector| Poll {
registry: Registry { selector },
registry: Registry {
selector,
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
has_waker: Arc::new(AtomicBool::new(false)),
},
})
}
}
@ -411,7 +436,20 @@ impl Poll {
}
}
#[cfg(unix)]
#[cfg(all(
unix,
not(mio_unsupported_force_poll_poll),
not(any(
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hermit",
target_os = "hurd",
target_os = "nto",
target_os = "solaris",
target_os = "vita"
)),
))]
impl AsRawFd for Poll {
fn as_raw_fd(&self) -> RawFd {
self.registry.as_raw_fd()
@ -668,9 +706,11 @@ impl Registry {
/// Event sources registered with this `Registry` will be registered with
/// the original `Registry` and `Poll` instance.
pub fn try_clone(&self) -> io::Result<Registry> {
self.selector
.try_clone()
.map(|selector| Registry { selector })
self.selector.try_clone().map(|selector| Registry {
selector,
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
has_waker: Arc::clone(&self.has_waker),
})
}
/// Internal check to ensure only a single `Waker` is active per [`Poll`]
@ -678,7 +718,7 @@ impl Registry {
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
pub(crate) fn register_waker(&self) {
assert!(
!self.selector.register_waker(),
!self.has_waker.swap(true, Ordering::AcqRel),
"Only a single `Waker` can be active per `Poll` instance"
);
}
@ -696,7 +736,20 @@ impl fmt::Debug for Registry {
}
}
#[cfg(unix)]
#[cfg(all(
unix,
not(mio_unsupported_force_poll_poll),
not(any(
target_os = "espidf",
target_os = "haiku",
target_os = "fuchsia",
target_os = "hermit",
target_os = "hurd",
target_os = "nto",
target_os = "solaris",
target_os = "vita"
)),
))]
impl AsRawFd for Registry {
fn as_raw_fd(&self) -> RawFd {
self.selector.as_raw_fd()
@ -704,7 +757,18 @@ impl AsRawFd for Registry {
}
cfg_os_poll! {
#[cfg(unix)]
#[cfg(all(
unix,
not(mio_unsupported_force_poll_poll),
not(any(
target_os = "espidf",
target_os = "hermit",
target_os = "hurd",
target_os = "nto",
target_os = "solaris",
target_os = "vita"
)),
))]
#[test]
pub fn as_raw_fd() {
let poll = Poll::new().unwrap();

View File

@ -51,9 +51,10 @@ cfg_os_poll! {
}
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit"))]
cfg_os_poll! {
mod unix;
#[allow(unused_imports)]
pub use self::unix::*;
}
@ -76,11 +77,7 @@ cfg_not_os_poll! {
#[cfg(unix)]
cfg_any_os_ext! {
mod unix;
#[cfg(feature = "os-ext")]
pub use self::unix::SourceFd;
}
#[cfg(unix)]
cfg_net! {
pub use self::unix::SocketAddr;
}
}

View File

@ -21,10 +21,16 @@ cfg_net! {
cfg_io_source! {
use std::io;
#[cfg(any(unix))]
use std::os::fd::RawFd;
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::RawFd;
#[cfg(windows)]
use std::os::windows::io::RawSocket;
#[cfg(windows)]
#[cfg(any(windows, unix, target_os = "hermit"))]
use crate::{Registry, Token, Interest};
pub(crate) struct IoSourceState;
@ -44,6 +50,33 @@ cfg_io_source! {
}
}
#[cfg(any(unix, target_os = "hermit"))]
impl IoSourceState {
pub fn register(
&mut self,
_: &Registry,
_: Token,
_: Interest,
_: RawFd,
) -> io::Result<()> {
os_required!()
}
pub fn reregister(
&mut self,
_: &Registry,
_: Token,
_: Interest,
_: RawFd,
) -> io::Result<()> {
os_required!()
}
pub fn deregister(&mut self, _: &Registry, _: RawFd) -> io::Result<()> {
os_required!()
}
}
#[cfg(windows)]
impl IoSourceState {
pub fn register(

View File

@ -1,6 +1,6 @@
use std::io;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::fd::{AsRawFd, RawFd};
use std::time::Duration;
pub type Event = usize;
@ -18,11 +18,6 @@ impl Selector {
pub fn select(&self, _: &mut Events, _: Option<Duration>) -> io::Result<()> {
os_required!();
}
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
pub fn register_waker(&self) -> bool {
os_required!();
}
}
#[cfg(unix)]

View File

@ -21,7 +21,7 @@ pub(crate) fn listen(_: &net::TcpListener, _: u32) -> io::Result<()> {
os_required!();
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "hermit"))]
pub(crate) fn set_reuseaddr(_: &net::TcpListener, _: bool) -> io::Result<()> {
os_required!();
}

View File

@ -1,10 +1,8 @@
pub(crate) mod datagram {
use crate::net::SocketAddr;
use std::io;
use std::os::unix::net;
use std::path::Path;
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn bind(_: &Path) -> io::Result<net::UnixDatagram> {
pub(crate) fn bind_addr(_: &SocketAddr) -> io::Result<net::UnixDatagram> {
os_required!()
}
@ -15,61 +13,32 @@ pub(crate) mod datagram {
pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> {
os_required!()
}
pub(crate) fn local_addr(_: &net::UnixDatagram) -> io::Result<SocketAddr> {
os_required!()
}
pub(crate) fn peer_addr(_: &net::UnixDatagram) -> io::Result<SocketAddr> {
os_required!()
}
pub(crate) fn recv_from(
_: &net::UnixDatagram,
_: &mut [u8],
) -> io::Result<(usize, SocketAddr)> {
os_required!()
}
}
pub(crate) mod listener {
use crate::net::{SocketAddr, UnixStream};
use std::io;
use std::os::unix::net;
use std::path::Path;
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn bind(_: &Path) -> io::Result<net::UnixListener> {
use crate::net::UnixStream;
pub(crate) fn bind_addr(_: &SocketAddr) -> io::Result<net::UnixListener> {
os_required!()
}
pub(crate) fn accept(_: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> {
os_required!()
}
pub(crate) fn local_addr(_: &net::UnixListener) -> io::Result<SocketAddr> {
os_required!()
}
}
pub(crate) mod stream {
use crate::net::SocketAddr;
use std::io;
use std::os::unix::net;
use std::path::Path;
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn connect(_: &Path) -> io::Result<net::UnixStream> {
pub(crate) fn connect_addr(_: &SocketAddr) -> io::Result<net::UnixStream> {
os_required!()
}
pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> {
os_required!()
}
pub(crate) fn local_addr(_: &net::UnixStream) -> io::Result<SocketAddr> {
os_required!()
}
pub(crate) fn peer_addr(_: &net::UnixStream) -> io::Result<SocketAddr> {
os_required!()
}
}

View File

@ -4,8 +4,9 @@
#[allow(unused_macros)]
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
#[allow(unused_unsafe)]
let res = unsafe { libc::$fn($($arg, )*) };
if res == -1 {
if res < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(res)
@ -14,59 +15,149 @@ macro_rules! syscall {
}
cfg_os_poll! {
#[cfg_attr(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
)
), path = "selector/epoll.rs")]
#[cfg_attr(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)
), path = "selector/kqueue.rs")]
#[cfg_attr(any(
mio_unsupported_force_poll_poll,
target_os = "espidf",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hermit",
target_os = "hurd",
target_os = "nto",
target_os = "solaris",
target_os = "vita",
), path = "selector/poll.rs")]
mod selector;
pub(crate) use self::selector::{event, Event, Events, Selector};
pub(crate) use self::selector::*;
#[cfg_attr(all(
not(mio_unsupported_force_waker_pipe),
any(
target_os = "android",
target_os = "espidf",
target_os = "fuchsia",
target_os = "hermit",
target_os = "illumos",
target_os = "linux",
)
), path = "waker/eventfd.rs")]
#[cfg_attr(all(
not(mio_unsupported_force_waker_pipe),
not(mio_unsupported_force_poll_poll), // `kqueue(2)` based waker doesn't work with `poll(2)`.
any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)
), path = "waker/kqueue.rs")]
#[cfg_attr(any(
// NOTE: also add to the list list for the `pipe` module below.
mio_unsupported_force_waker_pipe,
all(
// `kqueue(2)` based waker doesn't work with `poll(2)`.
mio_unsupported_force_poll_poll,
any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
),
),
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd",
target_os = "nto",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
), path = "waker/pipe.rs")]
mod waker;
// NOTE: the `Waker` type is expected in the selector module as the
// `poll(2)` implementation needs to do some special stuff.
mod sourcefd;
#[cfg(feature = "os-ext")]
pub use self::sourcefd::SourceFd;
mod waker;
pub(crate) use self::waker::Waker;
cfg_net! {
mod net;
pub(crate) mod tcp;
pub(crate) mod udp;
#[cfg(not(target_os = "hermit"))]
pub(crate) mod uds;
pub use self::uds::SocketAddr;
}
cfg_io_source! {
use std::io;
// Both `kqueue` and `epoll` don't need to hold any user space state.
pub(crate) struct IoSourceState;
impl IoSourceState {
pub fn new() -> IoSourceState {
IoSourceState
}
pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R>
where
F: FnOnce(&T) -> io::Result<R>,
{
// We don't hold state, so we can just call the function and
// return.
f(io)
}
}
}
cfg_os_ext! {
pub(crate) mod pipe;
}
#[cfg(all(
any(
// For the public `pipe` module, must match `cfg_os_ext` macro.
feature = "os-ext",
// For the `Waker` type based on a pipe.
mio_unsupported_force_waker_pipe,
all(
// `kqueue(2)` based waker doesn't work with `poll(2)`.
mio_unsupported_force_poll_poll,
any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
),
),
// NOTE: also add to the list list for the `pipe` module below.
target_os = "aix",
target_os = "dragonfly",
target_os = "haiku",
target_os = "hurd",
target_os = "netbsd",
target_os = "nto",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
),
// Hermit doesn't support pipes.
not(target_os = "hermit"),
))]
pub(crate) mod pipe;
}
cfg_not_os_poll! {
cfg_net! {
mod uds;
pub use self::uds::SocketAddr;
}
cfg_any_os_ext! {
mod sourcefd;
#[cfg(feature = "os-ext")]
pub use self::sourcefd::SourceFd;
}
}

View File

@ -17,12 +17,17 @@ pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::R
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "hurd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "hermit",
))]
let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
#[cfg(target_os = "nto")]
let socket_type = socket_type | libc::SOCK_CLOEXEC;
let socket = syscall!(socket(domain, socket_type, 0))?;
@ -31,6 +36,7 @@ pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::R
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
if let Err(err) = syscall!(setsockopt(
@ -44,18 +50,23 @@ pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::R
return Err(err);
}
// Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
// Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
))]
{
if let Err(err) = syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) {
let _ = syscall!(close(socket));
return Err(err);
}
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
if let Err(err) = syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)) {
let _ = syscall!(close(socket));
return Err(err);
@ -95,18 +106,33 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_
sin_family: libc::AF_INET as libc::sa_family_t,
sin_port: addr.port().to_be(),
sin_addr,
#[cfg(not(any(target_os = "haiku", target_os = "vita")))]
sin_zero: [0; 8],
#[cfg(target_os = "haiku")]
sin_zero: [0; 24],
#[cfg(target_os = "vita")]
sin_zero: [0; 6],
#[cfg(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "hurd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "hermit",
target_os = "nto",
))]
sin_len: 0,
#[cfg(target_os = "vita")]
sin_vport: addr.port().to_be(),
};
let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
@ -123,17 +149,26 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_
sin6_flowinfo: addr.flowinfo(),
sin6_scope_id: addr.scope_id(),
#[cfg(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "hurd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
))]
sin6_len: 0,
#[cfg(target_os = "illumos")]
#[cfg(target_os = "vita")]
sin6_vport: addr.port().to_be(),
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
__sin6_src_id: 0,
};

View File

@ -2,9 +2,70 @@
//!
//! See the [`new`] function for documentation.
use std::io;
use std::os::fd::RawFd;
pub(crate) fn new_raw() -> io::Result<[RawFd; 2]> {
let mut fds: [RawFd; 2] = [-1, -1];
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "hurd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "redox",
target_os = "solaris",
target_os = "vita",
))]
unsafe {
if libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) != 0 {
return Err(io::Error::last_os_error());
}
}
#[cfg(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "nto",
))]
unsafe {
// For platforms that don't have `pipe2(2)` we need to manually set the
// correct flags on the file descriptor.
if libc::pipe(fds.as_mut_ptr()) != 0 {
return Err(io::Error::last_os_error());
}
for fd in &fds {
if libc::fcntl(*fd, libc::F_SETFL, libc::O_NONBLOCK) != 0
|| libc::fcntl(*fd, libc::F_SETFD, libc::FD_CLOEXEC) != 0
{
let err = io::Error::last_os_error();
// Don't leak file descriptors. Can't handle closing error though.
let _ = libc::close(fds[0]);
let _ = libc::close(fds[1]);
return Err(err);
}
}
}
Ok(fds)
}
cfg_os_ext! {
use std::fs::File;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::io::{IoSlice, IoSliceMut, Read, Write};
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use std::process::{ChildStderr, ChildStdin, ChildStdout};
use crate::io_source::IoSource;
@ -145,70 +206,10 @@ use crate::{event, Interest, Registry, Token};
/// # }
/// ```
pub fn new() -> io::Result<(Sender, Receiver)> {
let mut fds: [RawFd; 2] = [-1, -1];
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "redox",
))]
unsafe {
if libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) != 0 {
return Err(io::Error::last_os_error());
}
}
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
))]
unsafe {
// For platforms that don't have `pipe2(2)` we need to manually set the
// correct flags on the file descriptor.
if libc::pipe(fds.as_mut_ptr()) != 0 {
return Err(io::Error::last_os_error());
}
for fd in &fds {
if libc::fcntl(*fd, libc::F_SETFL, libc::O_NONBLOCK) != 0
|| libc::fcntl(*fd, libc::F_SETFD, libc::FD_CLOEXEC) != 0
{
let err = io::Error::last_os_error();
// Don't leak file descriptors. Can't handle closing error though.
let _ = libc::close(fds[0]);
let _ = libc::close(fds[1]);
return Err(err);
}
}
}
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "tvos",
target_os = "watchos",
)))]
compile_error!("unsupported target for `mio::unix::pipe`");
// SAFETY: we just initialised the `fds` above.
let fds = new_raw()?;
// SAFETY: `new_raw` initialised the `fds` above.
let r = unsafe { Receiver::from_raw_fd(fds[0]) };
let w = unsafe { Sender::from_raw_fd(fds[1]) };
Ok((w, r))
}
@ -244,7 +245,7 @@ impl Sender {
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// use std::os::unix::io::AsRawFd;
/// use std::os::fd::AsRawFd;
/// use mio::unix::pipe;
///
/// let (sender, receiver) = pipe::new()?;
@ -377,6 +378,12 @@ impl IntoRawFd for Sender {
}
}
impl AsFd for Sender {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
/// Receiving end of an Unix pipe.
///
/// See [`new`] for documentation, including examples.
@ -409,7 +416,7 @@ impl Receiver {
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use std::io;
/// use std::os::unix::io::AsRawFd;
/// use std::os::fd::AsRawFd;
/// use mio::unix::pipe;
///
/// let (sender, receiver) = pipe::new()?;
@ -524,11 +531,9 @@ impl From<ChildStderr> for Receiver {
}
}
impl FromRawFd for Receiver {
unsafe fn from_raw_fd(fd: RawFd) -> Receiver {
Receiver {
inner: IoSource::new(File::from_raw_fd(fd)),
}
impl IntoRawFd for Receiver {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}
@ -538,13 +543,21 @@ impl AsRawFd for Receiver {
}
}
impl IntoRawFd for Receiver {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
impl FromRawFd for Receiver {
unsafe fn from_raw_fd(fd: RawFd) -> Receiver {
Receiver {
inner: IoSource::new(File::from_raw_fd(fd)),
}
}
}
#[cfg(not(target_os = "illumos"))]
impl AsFd for Receiver {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
#[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "vita")))]
fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> {
let value = nonblocking as libc::c_int;
if unsafe { libc::ioctl(fd, libc::FIONBIO, &value) } == -1 {
@ -554,7 +567,7 @@ fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> {
}
}
#[cfg(target_os = "illumos")]
#[cfg(any(target_os = "illumos", target_os = "solaris", target_os = "vita"))]
fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if flags < 0 {
@ -575,3 +588,4 @@ fn set_nonblocking(fd: RawFd, nonblocking: bool) -> io::Result<()> {
Ok(())
}
} // `cfg_os_ext!`.

View File

@ -1,11 +1,12 @@
use crate::{Interest, Token};
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use std::{io, ptr};
use libc::{EPOLLET, EPOLLIN, EPOLLOUT, EPOLLPRI, EPOLLRDHUP};
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::time::Duration;
use std::{cmp, i32, io, ptr};
use crate::{Interest, Token};
/// Unique id for use as `SelectorId`.
#[cfg(debug_assertions)]
@ -15,93 +16,45 @@ static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
pub struct Selector {
#[cfg(debug_assertions)]
id: usize,
ep: RawFd,
#[cfg(debug_assertions)]
has_waker: AtomicBool,
ep: OwnedFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
#[cfg(not(target_os = "android"))]
let res = syscall!(epoll_create1(libc::EPOLL_CLOEXEC));
// On Android < API level 16 `epoll_create1` is not defined, so use a
// raw system call.
// According to libuv, `EPOLL_CLOEXEC` is not defined on Android API <
// 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform,
// so we use it instead.
#[cfg(target_os = "android")]
let res = syscall!(syscall(libc::SYS_epoll_create1, libc::O_CLOEXEC));
let ep = match res {
Ok(ep) => ep as RawFd,
Err(err) => {
// When `epoll_create1` is not available fall back to use
// `epoll_create` followed by `fcntl`.
if let Some(libc::ENOSYS) = err.raw_os_error() {
match syscall!(epoll_create(1024)) {
Ok(ep) => match syscall!(fcntl(ep, libc::F_SETFD, libc::FD_CLOEXEC)) {
Ok(ep) => ep as RawFd,
Err(err) => {
// `fcntl` failed, cleanup `ep`.
let _ = unsafe { libc::close(ep) };
return Err(err);
}
},
Err(err) => return Err(err),
}
} else {
return Err(err);
}
}
};
// SAFETY: `epoll_create1(2)` ensures the fd is valid.
let ep = unsafe { OwnedFd::from_raw_fd(syscall!(epoll_create1(libc::EPOLL_CLOEXEC))?) };
Ok(Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
ep,
#[cfg(debug_assertions)]
has_waker: AtomicBool::new(false),
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
syscall!(fcntl(self.ep, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|ep| Selector {
self.ep.try_clone().map(|ep| Selector {
// It's the same selector, so we use the same id.
#[cfg(debug_assertions)]
id: self.id,
ep,
#[cfg(debug_assertions)]
has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)),
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
// A bug in kernels < 2.6.37 makes timeouts larger than LONG_MAX / CONFIG_HZ
// (approx. 30 minutes with CONFIG_HZ=1200) effectively infinite on 32 bits
// architectures. The magic number is the same constant used by libuv.
#[cfg(target_pointer_width = "32")]
const MAX_SAFE_TIMEOUT: u128 = 1789569;
#[cfg(not(target_pointer_width = "32"))]
const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128;
let timeout = timeout
.map(|to| {
// `Duration::as_millis` truncates, so round up. This avoids
// turning sub-millisecond timeouts into a zero timeout, unless
// the caller explicitly requests that by specifying a zero
// timeout.
let to_ms = to
.checked_add(Duration::from_nanos(999_999))
to.checked_add(Duration::from_nanos(999_999))
.unwrap_or(to)
.as_millis();
cmp::min(MAX_SAFE_TIMEOUT, to_ms) as libc::c_int
.as_millis() as libc::c_int
})
.unwrap_or(-1);
events.clear();
syscall!(epoll_wait(
self.ep,
self.ep.as_raw_fd(),
events.as_mut_ptr(),
events.capacity() as i32,
timeout,
@ -121,7 +74,8 @@ impl Selector {
_pad: 0,
};
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ())
let ep = self.ep.as_raw_fd();
syscall!(epoll_ctl(ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ())
}
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
@ -132,16 +86,13 @@ impl Selector {
_pad: 0,
};
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ())
let ep = self.ep.as_raw_fd();
syscall!(epoll_ctl(ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ())
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
}
#[cfg(debug_assertions)]
pub fn register_waker(&self) -> bool {
self.has_waker.swap(true, Ordering::AcqRel)
let ep = self.ep.as_raw_fd();
syscall!(epoll_ctl(ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
}
}
@ -156,15 +107,7 @@ cfg_io_source! {
impl AsRawFd for Selector {
fn as_raw_fd(&self) -> RawFd {
self.ep
}
}
impl Drop for Selector {
fn drop(&mut self) {
if let Err(err) = syscall!(close(self.ep)) {
error!("error closing epoll: {}", err);
}
self.ep.as_raw_fd()
}
}
@ -265,9 +208,7 @@ pub mod event {
libc::EPOLLET,
libc::EPOLLRDHUP,
libc::EPOLLONESHOT,
#[cfg(target_os = "linux")]
libc::EPOLLEXCLUSIVE,
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::EPOLLWAKEUP,
libc::EPOLL_CLOEXEC,
);
@ -281,9 +222,10 @@ pub mod event {
}
}
#[cfg(target_os = "android")]
#[test]
fn assert_close_on_exec_flag() {
// This assertion need to be true for Selector::new.
assert_eq!(libc::O_CLOEXEC, libc::EPOLL_CLOEXEC);
// No special requirement from the implementation around waking.
pub(crate) use crate::sys::unix::waker::Waker;
cfg_io_source! {
mod stateless_io_source;
pub(crate) use stateless_io_source::IoSourceState;
}

View File

@ -1,9 +1,9 @@
use crate::{Interest, Token};
use std::mem::{self, MaybeUninit};
use std::ops::{Deref, DerefMut};
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use std::{cmp, io, ptr, slice};
@ -24,6 +24,7 @@ type Filter = libc::c_short;
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
type Filter = i16;
@ -37,6 +38,7 @@ type Flags = libc::c_ushort;
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
type Flags = u16;
@ -65,40 +67,33 @@ macro_rules! kevent {
pub struct Selector {
#[cfg(debug_assertions)]
id: usize,
kq: RawFd,
#[cfg(debug_assertions)]
has_waker: AtomicBool,
kq: OwnedFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
let kq = syscall!(kqueue())?;
let selector = Selector {
// SAFETY: `kqueue(2)` ensures the fd is valid.
let kq = unsafe { OwnedFd::from_raw_fd(syscall!(kqueue())?) };
syscall!(fcntl(kq.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?;
Ok(Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
kq,
#[cfg(debug_assertions)]
has_waker: AtomicBool::new(false),
};
syscall!(fcntl(kq, libc::F_SETFD, libc::FD_CLOEXEC))?;
Ok(selector)
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
syscall!(fcntl(self.kq, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|kq| Selector {
self.kq.try_clone().map(|kq| Selector {
// It's the same selector, so we use the same id.
#[cfg(debug_assertions)]
id: self.id,
kq,
#[cfg(debug_assertions)]
has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)),
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
let timeout = timeout.map(|to| libc::timespec {
tv_sec: cmp::min(to.as_secs(), libc::time_t::max_value() as u64) as libc::time_t,
tv_sec: cmp::min(to.as_secs(), libc::time_t::MAX as u64) as libc::time_t,
// `Duration::subsec_nanos` is guaranteed to be less than one
// billion (the number of nanoseconds in a second), making the
// cast to i32 safe. The cast itself is needed for platforms
@ -112,7 +107,7 @@ impl Selector {
events.clear();
syscall!(kevent(
self.kq,
self.kq.as_raw_fd(),
ptr::null(),
0,
events.as_mut_ptr(),
@ -162,7 +157,7 @@ impl Selector {
// the array.
slice::from_raw_parts_mut(changes[0].as_mut_ptr(), n_changes)
};
kevent_register(self.kq, changes, &[libc::EPIPE as i64])
kevent_register(self.kq.as_raw_fd(), changes, &[libc::EPIPE as i64])
}
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
@ -192,7 +187,7 @@ impl Selector {
//
// For the explanation of ignoring `EPIPE` see `register`.
kevent_register(
self.kq,
self.kq.as_raw_fd(),
&mut changes,
&[libc::ENOENT as i64, libc::EPIPE as i64],
)
@ -210,12 +205,7 @@ impl Selector {
// the ENOENT error when it comes up. The ENOENT error informs us that
// the filter wasn't there in first place, but we don't really care
// about that since our goal is to remove it.
kevent_register(self.kq, &mut changes, &[libc::ENOENT as i64])
}
#[cfg(debug_assertions)]
pub fn register_waker(&self) -> bool {
self.has_waker.swap(true, Ordering::AcqRel)
kevent_register(self.kq.as_raw_fd(), &mut changes, &[libc::ENOENT as i64])
}
// Used by `Waker`.
@ -224,6 +214,7 @@ impl Selector {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
pub fn setup_waker(&self, token: Token) -> io::Result<()> {
@ -235,7 +226,8 @@ impl Selector {
token.0
);
syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
let kq = self.kq.as_raw_fd();
syscall!(kevent(kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 {
Err(io::Error::from_raw_os_error(kevent.data as i32))
} else {
@ -250,6 +242,7 @@ impl Selector {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
pub fn wake(&self, token: Token) -> io::Result<()> {
@ -261,7 +254,8 @@ impl Selector {
);
kevent.fflags = libc::NOTE_TRIGGER;
syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
let kq = self.kq.as_raw_fd();
syscall!(kevent(kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 {
Err(io::Error::from_raw_os_error(kevent.data as i32))
} else {
@ -325,15 +319,7 @@ cfg_io_source! {
impl AsRawFd for Selector {
fn as_raw_fd(&self) -> RawFd {
self.kq
}
}
impl Drop for Selector {
fn drop(&mut self) {
if let Err(err) = syscall!(close(self.kq)) {
error!("error closing kqueue: {}", err);
}
self.kq.as_raw_fd()
}
}
@ -387,6 +373,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
// Used by the `Awakener`. On platforms that use `eventfd` or a unix
@ -400,6 +387,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
)))]
{
@ -440,6 +428,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
{
@ -451,6 +440,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
)))]
{
@ -489,6 +479,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::EVFILT_FS,
@ -500,6 +491,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::EVFILT_USER,
@ -513,6 +505,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EVFILT_MACHPORT,
@ -520,6 +513,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EVFILT_VM,
@ -545,11 +539,14 @@ pub mod event {
libc::EV_FLAG1,
libc::EV_ERROR,
libc::EV_EOF,
// Not stable across OS versions on OpenBSD.
#[cfg(not(target_os = "openbsd"))]
libc::EV_SYSFLAGS,
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EV_FLAG0,
@ -557,6 +554,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EV_POLL,
@ -564,6 +562,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::EV_OOBAND,
@ -584,6 +583,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_TRIGGER,
@ -593,6 +593,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFNOP,
@ -602,6 +603,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFAND,
@ -611,6 +613,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFOR,
@ -620,6 +623,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFCOPY,
@ -629,6 +633,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFCTRLMASK,
@ -638,6 +643,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
))]
libc::NOTE_FFLAGSMASK,
@ -652,6 +658,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXTEND,
@ -663,6 +670,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_NONE,
@ -675,6 +683,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_SIGNAL,
@ -682,6 +691,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXITSTATUS,
@ -689,6 +699,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_DETAIL,
@ -719,6 +730,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_DETAIL_MASK,
@ -726,6 +738,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_DECRYPTFAIL,
@ -733,6 +746,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_MEMORY,
@ -740,6 +754,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_EXIT_CSERROR,
@ -747,6 +762,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_PRESSURE,
@ -754,6 +770,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_PRESSURE_TERMINATE,
@ -761,6 +778,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_PRESSURE_SUDDEN_TERMINATE,
@ -768,6 +786,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_VM_ERROR,
@ -776,6 +795,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_SECONDS,
@ -786,6 +806,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_USECONDS,
@ -794,6 +815,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_NSECONDS,
@ -801,6 +823,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_ABSOLUTE,
@ -808,6 +831,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_LEEWAY,
@ -815,6 +839,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_CRITICAL,
@ -822,6 +847,7 @@ pub mod event {
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos"
))]
libc::NOTE_BACKGROUND,
@ -842,6 +868,14 @@ pub mod event {
}
}
// No special requirement from the implementation around waking.
pub(crate) use crate::sys::unix::waker::Waker;
cfg_io_source! {
mod stateless_io_source;
pub(crate) use stateless_io_source::IoSourceState;
}
#[test]
#[cfg(feature = "os-ext")]
fn does_not_register_rw() {

View File

@ -1,49 +0,0 @@
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
))]
mod epoll;
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
))]
pub(crate) use self::epoll::{event, Event, Events, Selector};
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "watchos",
))]
mod kqueue;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "watchos",
))]
pub(crate) use self::kqueue::{event, Event, Events, Selector};
/// Lowest file descriptor used in `Selector::try_clone`.
///
/// # Notes
///
/// Usually fds 0, 1 and 2 are standard in, out and error. Some application
/// blindly assume this to be true, which means using any one of those a select
/// could result in some interesting and unexpected errors. Avoid that by using
/// an fd that doesn't have a pre-determined usage.
const LOWEST_FD: libc::c_int = 3;

View File

@ -0,0 +1,749 @@
// This implementation is based on the one in the `polling` crate.
// Thanks to https://github.com/Kestrer for the original implementation!
// Permission to use this code has been granted by original author:
// https://github.com/tokio-rs/mio/pull/1602#issuecomment-1218441031
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use std::{cmp, fmt, io};
use crate::sys::unix::waker::Waker as WakerInternal;
use crate::{Interest, Token};
/// Unique id for use as `SelectorId`.
#[cfg(debug_assertions)]
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
#[derive(Debug)]
pub struct Selector {
state: Arc<SelectorState>,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
let state = SelectorState::new()?;
Ok(Selector {
state: Arc::new(state),
})
}
pub fn try_clone(&self) -> io::Result<Selector> {
let state = self.state.clone();
Ok(Selector { state })
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
self.state.select(events, timeout)
}
pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.state.register(fd, token, interests)
}
#[allow(dead_code)]
pub(crate) fn register_internal(
&self,
fd: RawFd,
token: Token,
interests: Interest,
) -> io::Result<Arc<RegistrationRecord>> {
self.state.register_internal(fd, token, interests)
}
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.state.reregister(fd, token, interests)
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
self.state.deregister(fd)
}
pub fn wake(&self, token: Token) -> io::Result<()> {
self.state.wake(token)
}
cfg_io_source! {
#[cfg(debug_assertions)]
pub fn id(&self) -> usize {
self.state.id
}
}
}
/// Interface to poll.
#[derive(Debug)]
struct SelectorState {
/// File descriptors to poll.
fds: Mutex<Fds>,
/// File descriptors which will be removed before the next poll call.
///
/// When a file descriptor is deregistered while a poll is running, we need to filter
/// out all removed descriptors after that poll is finished running.
pending_removal: Mutex<Vec<RawFd>>,
/// Token associated with Waker that have recently asked to wake. This will
/// cause a synthetic behaviour where on any wakeup we add all pending tokens
/// to the list of emitted events.
pending_wake_token: Mutex<Option<Token>>,
/// Data is written to this to wake up the current instance of `wait`, which can occur when the
/// user notifies it (in which case `notified` would have been set) or when an operation needs
/// to occur (in which case `waiting_operations` would have been incremented).
notify_waker: WakerInternal,
/// The number of operations (`add`, `modify` or `delete`) that are currently waiting on the
/// mutex to become free. When this is nonzero, `wait` must be suspended until it reaches zero
/// again.
waiting_operations: AtomicUsize,
/// The condition variable that gets notified when `waiting_operations` reaches zero or
/// `notified` becomes true.
///
/// This is used with the `fds` mutex.
operations_complete: Condvar,
/// This selectors id.
#[cfg(debug_assertions)]
#[allow(dead_code)]
id: usize,
}
/// The file descriptors to poll in a `Poller`.
#[derive(Debug, Clone)]
struct Fds {
/// The list of `pollfds` taken by poll.
///
/// The first file descriptor is always present and is used to notify the poller.
poll_fds: Vec<PollFd>,
/// The map of each file descriptor to data associated with it. This does not include the file
/// descriptors created by the internal notify waker.
fd_data: HashMap<RawFd, FdData>,
}
/// Transparent wrapper around `libc::pollfd`, used to support `Debug` derives without adding the
/// `extra_traits` feature of `libc`.
#[repr(transparent)]
#[derive(Clone)]
struct PollFd(libc::pollfd);
impl Debug for PollFd {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("pollfd")
.field("fd", &self.0.fd)
.field("events", &self.0.events)
.field("revents", &self.0.revents)
.finish()
}
}
/// Data associated with a file descriptor in a poller.
#[derive(Debug, Clone)]
struct FdData {
/// The index into `poll_fds` this file descriptor is.
poll_fds_index: usize,
/// The key of the `Event` associated with this file descriptor.
token: Token,
/// Used to communicate with IoSourceState when we need to internally deregister
/// based on a closed fd.
shared_record: Arc<RegistrationRecord>,
}
impl SelectorState {
pub fn new() -> io::Result<SelectorState> {
let notify_waker = WakerInternal::new_unregistered()?;
Ok(Self {
fds: Mutex::new(Fds {
poll_fds: vec![PollFd(libc::pollfd {
fd: notify_waker.as_raw_fd(),
events: libc::POLLIN,
revents: 0,
})],
fd_data: HashMap::new(),
}),
pending_removal: Mutex::new(Vec::new()),
pending_wake_token: Mutex::new(None),
notify_waker,
waiting_operations: AtomicUsize::new(0),
operations_complete: Condvar::new(),
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
events.clear();
let mut fds = self.fds.lock().unwrap();
// Keep track of fds that receive POLLHUP or POLLERR (i.e. won't receive further
// events) and internally deregister them before they are externally deregister'd. See
// IoSourceState below to track how the external deregister call will be handled
// when this state occurs.
let mut closed_raw_fds = Vec::new();
loop {
// Complete all current operations.
loop {
if self.waiting_operations.load(Ordering::SeqCst) == 0 {
break;
}
fds = self.operations_complete.wait(fds).unwrap();
}
// Perform the poll.
trace!("Polling on {:?}", &fds);
let num_events = poll(&mut fds.poll_fds, timeout)?;
trace!("Poll finished: {:?}", &fds);
if num_events == 0 {
return Ok(());
}
let waker_events = fds.poll_fds[0].0.revents;
let notified = waker_events != 0;
let mut num_fd_events = if notified { num_events - 1 } else { num_events };
let pending_wake_token = self.pending_wake_token.lock().unwrap().take();
if notified {
self.notify_waker.ack_and_reset();
if pending_wake_token.is_some() {
num_fd_events += 1;
}
}
// We now check whether this poll was performed with descriptors which were pending
// for removal and filter out any matching.
let mut pending_removal_guard = self.pending_removal.lock().unwrap();
let mut pending_removal = std::mem::replace(pending_removal_guard.as_mut(), Vec::new());
drop(pending_removal_guard);
// Store the events if there were any.
if num_fd_events > 0 {
let fds = &mut *fds;
events.reserve(num_fd_events);
// Add synthetic events we picked up from calls to wake()
if let Some(pending_wake_token) = pending_wake_token {
events.push(Event {
token: pending_wake_token,
events: waker_events,
});
}
for fd_data in fds.fd_data.values_mut() {
let PollFd(poll_fd) = &mut fds.poll_fds[fd_data.poll_fds_index];
if pending_removal.contains(&poll_fd.fd) {
// Fd was removed while poll was running
continue;
}
if poll_fd.revents != 0 {
// Store event
events.push(Event {
token: fd_data.token,
events: poll_fd.revents,
});
if poll_fd.revents & (libc::POLLHUP | libc::POLLERR) != 0 {
pending_removal.push(poll_fd.fd);
closed_raw_fds.push(poll_fd.fd);
}
// Remove the interest which just got triggered the IoSourceState's do_io
// wrapper used with this selector will add back the interest using
// reregister.
poll_fd.events &= !poll_fd.revents;
// Minor optimization to potentially avoid looping n times where n is the
// number of input fds (i.e. we might loop between m and n times where m is
// the number of fds with revents != 0).
if events.len() == num_fd_events {
break;
}
}
}
break; // No more polling.
}
// If we didn't break above it means we got woken up internally (for example for adding an fd), so we poll again.
}
drop(fds);
let _ = self.deregister_all(&closed_raw_fds);
Ok(())
}
pub fn register(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.register_internal(fd, token, interests).map(|_| ())
}
pub fn register_internal(
&self,
fd: RawFd,
token: Token,
interests: Interest,
) -> io::Result<Arc<RegistrationRecord>> {
#[cfg(debug_assertions)]
if fd == self.notify_waker.as_raw_fd() {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
// We must handle the unlikely case that the following order of operations happens:
//
// register(1 as RawFd)
// deregister(1 as RawFd)
// register(1 as RawFd)
// <poll happens>
//
// Fd's pending removal only get cleared when poll has been run. It is possible that
// between registering and deregistering and then _again_ registering the file descriptor
// poll never gets called, thus the fd stays stuck in the pending removal list.
//
// To avoid this scenario we remove an fd from pending removals when registering it.
let mut pending_removal = self.pending_removal.lock().unwrap();
if let Some(idx) = pending_removal.iter().position(|&pending| pending == fd) {
pending_removal.swap_remove(idx);
}
drop(pending_removal);
self.modify_fds(|fds| {
if fds.fd_data.contains_key(&fd) {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"I/O source already registered this `Registry` \
(an old file descriptor might have been closed without deregistration)",
));
}
let poll_fds_index = fds.poll_fds.len();
let record = Arc::new(RegistrationRecord::new());
fds.fd_data.insert(
fd,
FdData {
poll_fds_index,
token,
shared_record: record.clone(),
},
);
fds.poll_fds.push(PollFd(libc::pollfd {
fd,
events: interests_to_poll(interests),
revents: 0,
}));
Ok(record)
})
}
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interest) -> io::Result<()> {
self.modify_fds(|fds| {
let data = fds.fd_data.get_mut(&fd).ok_or(io::ErrorKind::NotFound)?;
data.token = token;
let poll_fds_index = data.poll_fds_index;
fds.poll_fds[poll_fds_index].0.events = interests_to_poll(interests);
Ok(())
})
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
self.deregister_all(&[fd])
.map_err(|_| io::ErrorKind::NotFound)?;
Ok(())
}
/// Perform a modification on `fds`, interrupting the current caller of `wait` if it's running.
fn modify_fds<T>(&self, f: impl FnOnce(&mut Fds) -> T) -> T {
self.waiting_operations.fetch_add(1, Ordering::SeqCst);
// Wake up the current caller of `wait` if there is one.
let sent_notification = self.notify_waker.wake().is_ok();
let mut fds = self.fds.lock().unwrap();
// If there was no caller of `wait` our notification was not removed from the pipe.
if sent_notification {
self.notify_waker.ack_and_reset();
}
let res = f(&mut *fds);
if self.waiting_operations.fetch_sub(1, Ordering::SeqCst) == 1 {
self.operations_complete.notify_one();
}
res
}
/// Special optimized version of [Self::deregister] which handles multiple removals
/// at once. Ok result if all removals were performed, Err if any entries
/// were not found.
fn deregister_all(&self, targets: &[RawFd]) -> Result<(), ()> {
if targets.is_empty() {
return Ok(());
}
let mut pending_removal = self.pending_removal.lock().unwrap();
pending_removal.extend(targets);
drop(pending_removal);
self.modify_fds(|fds| {
let mut all_successful = true;
for target in targets {
match fds.fd_data.remove(target).ok_or(()) {
Ok(data) => {
data.shared_record.mark_unregistered();
fds.poll_fds.swap_remove(data.poll_fds_index);
if let Some(swapped_pollfd) = fds.poll_fds.get(data.poll_fds_index) {
fds.fd_data
.get_mut(&swapped_pollfd.0.fd)
.unwrap()
.poll_fds_index = data.poll_fds_index;
}
}
Err(_) => all_successful = false,
}
}
if all_successful {
Ok(())
} else {
Err(())
}
})
}
pub fn wake(&self, token: Token) -> io::Result<()> {
self.pending_wake_token.lock().unwrap().replace(token);
self.notify_waker.wake()
}
}
/// Shared record between IoSourceState and SelectorState that allows us to internally
/// deregister partially or fully closed fds (i.e. when we get POLLHUP or PULLERR) without
/// confusing IoSourceState and trying to deregister twice. This isn't strictly
/// required as technically deregister is idempotent but it is confusing
/// when trying to debug behaviour as we get imbalanced calls to register/deregister and
/// superfluous NotFound errors.
#[derive(Debug)]
pub(crate) struct RegistrationRecord {
is_unregistered: AtomicBool,
}
impl RegistrationRecord {
pub fn new() -> Self {
Self {
is_unregistered: AtomicBool::new(false),
}
}
pub fn mark_unregistered(&self) {
self.is_unregistered.store(true, Ordering::Relaxed);
}
#[allow(dead_code)]
pub fn is_registered(&self) -> bool {
!self.is_unregistered.load(Ordering::Relaxed)
}
}
#[cfg(target_os = "linux")]
const POLLRDHUP: libc::c_short = libc::POLLRDHUP;
#[cfg(not(target_os = "linux"))]
const POLLRDHUP: libc::c_short = 0;
const READ_EVENTS: libc::c_short = libc::POLLIN | POLLRDHUP;
const WRITE_EVENTS: libc::c_short = libc::POLLOUT;
const PRIORITY_EVENTS: libc::c_short = libc::POLLPRI;
/// Get the input poll events for the given event.
fn interests_to_poll(interest: Interest) -> libc::c_short {
let mut kind = 0;
if interest.is_readable() {
kind |= READ_EVENTS;
}
if interest.is_writable() {
kind |= WRITE_EVENTS;
}
if interest.is_priority() {
kind |= PRIORITY_EVENTS;
}
kind
}
/// Helper function to call poll.
fn poll(fds: &mut [PollFd], timeout: Option<Duration>) -> io::Result<usize> {
loop {
// A bug in kernels < 2.6.37 makes timeouts larger than LONG_MAX / CONFIG_HZ
// (approx. 30 minutes with CONFIG_HZ=1200) effectively infinite on 32 bits
// architectures. The magic number is the same constant used by libuv.
#[cfg(target_pointer_width = "32")]
const MAX_SAFE_TIMEOUT: u128 = 1789569;
#[cfg(not(target_pointer_width = "32"))]
const MAX_SAFE_TIMEOUT: u128 = libc::c_int::MAX as u128;
let timeout = timeout
.map(|to| {
// `Duration::as_millis` truncates, so round up. This avoids
// turning sub-millisecond timeouts into a zero timeout, unless
// the caller explicitly requests that by specifying a zero
// timeout.
let to_ms = to
.checked_add(Duration::from_nanos(999_999))
.unwrap_or(to)
.as_millis();
cmp::min(MAX_SAFE_TIMEOUT, to_ms) as libc::c_int
})
.unwrap_or(-1);
let res = syscall!(poll(
fds.as_mut_ptr() as *mut libc::pollfd,
fds.len() as libc::nfds_t,
timeout,
));
match res {
Ok(num_events) => break Ok(num_events as usize),
// poll returns EAGAIN if we can retry it.
Err(e) if e.raw_os_error() == Some(libc::EAGAIN) => continue,
Err(e) => return Err(e),
}
}
}
#[derive(Debug, Clone)]
pub struct Event {
token: Token,
events: libc::c_short,
}
pub type Events = Vec<Event>;
pub mod event {
use std::fmt;
use crate::sys::Event;
use crate::Token;
use super::POLLRDHUP;
pub fn token(event: &Event) -> Token {
event.token
}
pub fn is_readable(event: &Event) -> bool {
(event.events & libc::POLLIN) != 0 || (event.events & libc::POLLPRI) != 0
}
pub fn is_writable(event: &Event) -> bool {
(event.events & libc::POLLOUT) != 0
}
pub fn is_error(event: &Event) -> bool {
(event.events & libc::POLLERR) != 0
}
pub fn is_read_closed(event: &Event) -> bool {
// Both halves of the socket have closed
(event.events & libc::POLLHUP) != 0
// Socket has received FIN or called shutdown(SHUT_RD)
|| (event.events & POLLRDHUP) != 0
}
pub fn is_write_closed(event: &Event) -> bool {
// Both halves of the socket have closed
(event.events & libc::POLLHUP) != 0
// Unix pipe write end has closed
|| ((event.events & libc::POLLOUT) != 0 && (event.events & libc::POLLERR) != 0)
// The other side (read end) of a Unix pipe has closed.
|| (event.events == libc::POLLERR)
}
pub fn is_priority(event: &Event) -> bool {
(event.events & libc::POLLPRI) != 0
}
pub fn is_aio(_: &Event) -> bool {
// Not supported in the kernel, only in libc.
false
}
pub fn is_lio(_: &Event) -> bool {
// Not supported.
false
}
pub fn debug_details(f: &mut fmt::Formatter<'_>, event: &Event) -> fmt::Result {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn check_events(got: &libc::c_short, want: &libc::c_short) -> bool {
(*got & want) != 0
}
debug_detail!(
EventsDetails(libc::c_short),
check_events,
libc::POLLIN,
libc::POLLPRI,
libc::POLLOUT,
libc::POLLRDNORM,
libc::POLLRDBAND,
libc::POLLWRNORM,
libc::POLLWRBAND,
libc::POLLERR,
libc::POLLHUP,
);
f.debug_struct("poll_event")
.field("token", &event.token)
.field("events", &EventsDetails(event.events))
.finish()
}
}
#[derive(Debug)]
pub(crate) struct Waker {
selector: Selector,
token: Token,
}
impl Waker {
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
Ok(Waker {
selector: selector.try_clone()?,
token,
})
}
pub(crate) fn wake(&self) -> io::Result<()> {
self.selector.wake(self.token)
}
}
cfg_io_source! {
use crate::Registry;
struct InternalState {
selector: Selector,
token: Token,
interests: Interest,
fd: RawFd,
shared_record: Arc<RegistrationRecord>,
}
impl Drop for InternalState {
fn drop(&mut self) {
if self.shared_record.is_registered() {
let _ = self.selector.deregister(self.fd);
}
}
}
pub(crate) struct IoSourceState {
inner: Option<Box<InternalState>>,
}
impl IoSourceState {
pub fn new() -> IoSourceState {
IoSourceState { inner: None }
}
pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R>
where
F: FnOnce(&T) -> io::Result<R>,
{
let result = f(io);
if let Err(err) = &result {
if err.kind() == io::ErrorKind::WouldBlock {
self.inner.as_ref().map_or(Ok(()), |state| {
state
.selector
.reregister(state.fd, state.token, state.interests)
})?;
}
}
result
}
pub fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
if self.inner.is_some() {
Err(io::ErrorKind::AlreadyExists.into())
} else {
let selector = registry.selector().try_clone()?;
selector.register_internal(fd, token, interests).map(move |shared_record| {
let state = InternalState {
selector,
token,
interests,
fd,
shared_record,
};
self.inner = Some(Box::new(state));
})
}
}
pub fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
match self.inner.as_mut() {
Some(state) => registry
.selector()
.reregister(fd, token, interests)
.map(|()| {
state.token = token;
state.interests = interests;
}),
None => Err(io::ErrorKind::NotFound.into()),
}
}
pub fn deregister(&mut self, registry: &Registry, fd: RawFd) -> io::Result<()> {
if let Some(state) = self.inner.take() {
// Marking unregistered will short circuit the drop behaviour of calling
// deregister so the call to deregister below is strictly required.
state.shared_record.mark_unregistered();
}
registry.selector().deregister(fd)
}
}
}

View File

@ -0,0 +1,50 @@
//! Both `kqueue(2)` and `epoll(2)` don't need to hold any user space state.
use std::io;
use std::os::fd::RawFd;
use crate::{Interest, Registry, Token};
pub(crate) struct IoSourceState;
impl IoSourceState {
pub(crate) fn new() -> IoSourceState {
IoSourceState
}
pub(crate) fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R>
where
F: FnOnce(&T) -> io::Result<R>,
{
// We don't hold state, so we can just call the function and
// return.
f(io)
}
pub(crate) fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
// Pass through, we don't have any state.
registry.selector().register(fd, token, interests)
}
pub(crate) fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
// Pass through, we don't have any state.
registry.selector().reregister(fd, token, interests)
}
pub(crate) fn deregister(&mut self, registry: &Registry, fd: RawFd) -> io::Result<()> {
// Pass through, we don't have any state.
registry.selector().deregister(fd)
}
}

View File

@ -1,7 +1,12 @@
use crate::{event, Interest, Registry, Token};
use std::io;
use std::os::unix::io::RawFd;
#[cfg(not(target_os = "hermit"))]
use std::os::fd::RawFd;
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::RawFd;
use crate::{event, Interest, Registry, Token};
/// Adapter for [`RawFd`] providing an [`event::Source`] implementation.
///
@ -38,7 +43,7 @@ use std::os::unix::io::RawFd;
/// use mio::{Interest, Poll, Token};
/// use mio::unix::SourceFd;
///
/// use std::os::unix::io::AsRawFd;
/// use std::os::fd::AsRawFd;
/// use std::net::TcpListener;
///
/// // Bind a std listener
@ -62,7 +67,7 @@ use std::os::unix::io::RawFd;
/// use mio::{event, Interest, Registry, Token};
/// use mio::unix::SourceFd;
///
/// use std::os::unix::io::RawFd;
/// use std::os::fd::RawFd;
/// use std::io;
///
/// # #[allow(dead_code)]

View File

@ -2,7 +2,12 @@ use std::convert::TryInto;
use std::io;
use std::mem::{size_of, MaybeUninit};
use std::net::{self, SocketAddr};
use std::os::unix::io::{AsRawFd, FromRawFd};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd};
use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr};
@ -34,7 +39,7 @@ pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<(
}
pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> {
let backlog = backlog.try_into().unwrap_or(i32::max_value());
let backlog = backlog.try_into().unwrap_or(i32::MAX);
syscall!(listen(socket.as_raw_fd(), backlog))?;
Ok(())
}
@ -63,10 +68,13 @@ pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream,
all(not(target_arch="x86"), target_os = "android"),
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "hurd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
))]
let stream = {
syscall!(accept4(
@ -79,14 +87,21 @@ pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream,
}?;
// But not all platforms have the `accept4(2)` call. Luckily BSD (derived)
// OSes inherit the non-blocking flag from the listener, so we just have to
// OSs inherit the non-blocking flag from the listener, so we just have to
// set `CLOEXEC`.
#[cfg(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "redox",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "hermit",
target_os = "nto",
all(target_arch = "x86", target_os = "android"),
))]
let stream = {
@ -97,10 +112,17 @@ pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream,
))
.map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) })
.and_then(|s| {
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
syscall!(fcntl(s.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?;
// See https://github.com/tokio-rs/mio/issues/1450
#[cfg(all(target_arch = "x86", target_os = "android"))]
#[cfg(any(
all(target_arch = "x86", target_os = "android"),
target_os = "espidf",
target_os = "vita",
target_os = "hermit",
target_os = "nto",
))]
syscall!(fcntl(s.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK))?;
Ok(s)

View File

@ -1,9 +1,14 @@
use crate::sys::unix::net::{new_ip_socket, socket_addr};
use std::io;
use std::mem;
use std::net::{self, SocketAddr};
use std::os::unix::io::{AsRawFd, FromRawFd};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd};
use crate::sys::unix::net::{new_ip_socket, socket_addr};
pub fn bind(addr: SocketAddr) -> io::Result<net::UdpSocket> {
let fd = new_ip_socket(addr, libc::SOCK_DGRAM)?;

View File

@ -1,17 +1,16 @@
use super::{socket_addr, SocketAddr};
use crate::sys::unix::net::new_socket;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::net;
use std::path::Path;
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn bind(path: &Path) -> io::Result<net::UnixDatagram> {
let (sockaddr, socklen) = socket_addr(path)?;
let sockaddr = &sockaddr as *const libc::sockaddr_un as *const _;
use crate::sys::unix::net::new_socket;
use crate::sys::unix::uds::unix_addr;
pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result<net::UnixDatagram> {
let socket = unbound()?;
syscall!(bind(socket.as_raw_fd(), sockaddr, socklen))?;
let (unix_address, addrlen) = unix_addr(address);
let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
syscall!(bind(socket.as_raw_fd(), sockaddr, addrlen))?;
Ok(socket)
}
@ -24,33 +23,3 @@ pub(crate) fn unbound() -> io::Result<net::UnixDatagram> {
pub(crate) fn pair() -> io::Result<(net::UnixDatagram, net::UnixDatagram)> {
super::pair(libc::SOCK_DGRAM)
}
pub(crate) fn local_addr(socket: &net::UnixDatagram) -> io::Result<SocketAddr> {
super::local_addr(socket.as_raw_fd())
}
pub(crate) fn peer_addr(socket: &net::UnixDatagram) -> io::Result<SocketAddr> {
super::peer_addr(socket.as_raw_fd())
}
pub(crate) fn recv_from(
socket: &net::UnixDatagram,
dst: &mut [u8],
) -> io::Result<(usize, SocketAddr)> {
let mut count = 0;
let socketaddr = SocketAddr::new(|sockaddr, socklen| {
syscall!(recvfrom(
socket.as_raw_fd(),
dst.as_mut_ptr() as *mut _,
dst.len(),
0,
sockaddr,
socklen,
))
.map(|c| {
count = c;
c as libc::c_int
})
})?;
Ok((count as usize, socketaddr))
}

View File

@ -1,46 +1,51 @@
use super::socket_addr;
use crate::net::{SocketAddr, UnixStream};
use crate::sys::unix::net::new_socket;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::net;
use std::ffi::OsStr;
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::net::{self, SocketAddr};
use std::path::Path;
use std::{io, mem};
pub(crate) fn bind(path: &Path) -> io::Result<net::UnixListener> {
let (sockaddr, socklen) = socket_addr(path)?;
let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr;
use crate::net::UnixStream;
use crate::sys::unix::net::new_socket;
use crate::sys::unix::uds::{path_offset, unix_addr};
pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result<net::UnixListener> {
let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
let socket = unsafe { net::UnixListener::from_raw_fd(fd) };
syscall!(bind(fd, sockaddr, socklen))?;
let (unix_address, addrlen) = unix_addr(address);
let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
syscall!(bind(fd, sockaddr, addrlen))?;
syscall!(listen(fd, 1024))?;
Ok(socket)
}
pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, SocketAddr)> {
let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
// This is safe to assume because a `libc::sockaddr_un` filled with `0`
// bytes is properly initialized.
// SAFETY: `libc::sockaddr_un` zero filled is properly initialized.
//
// `0` is a valid value for `sockaddr_un::sun_family`; it is
// `libc::AF_UNSPEC`.
//
// `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
// abstract path.
let mut sockaddr = unsafe { sockaddr.assume_init() };
let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() };
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
// Android x86's seccomp profile forbids calls to `accept4(2)`
// See https://github.com/tokio-rs/mio/issues/1445 for details
all(target_arch = "x86", target_os = "android"),
@ -57,12 +62,18 @@ pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, So
};
#[cfg(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
target_os = "nto",
all(target_arch = "x86", target_os = "android")
))]
let socket = syscall!(accept(
@ -74,20 +85,37 @@ pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, So
// Ensure the socket is closed if either of the `fcntl` calls
// error below.
let s = unsafe { net::UnixStream::from_raw_fd(socket) };
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?;
// See https://github.com/tokio-rs/mio/issues/1450
#[cfg(all(target_arch = "x86", target_os = "android"))]
#[cfg(any(
all(target_arch = "x86", target_os = "android"),
target_os = "espidf",
target_os = "vita",
target_os = "nto",
))]
syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?;
Ok(s)
});
socket
.map(UnixStream::from_std)
.map(|stream| (stream, SocketAddr::from_parts(sockaddr, socklen)))
}
let socket = socket.map(UnixStream::from_std)?;
pub(crate) fn local_addr(listener: &net::UnixListener) -> io::Result<SocketAddr> {
super::local_addr(listener.as_raw_fd())
#[allow(unused_mut)] // See below.
let mut path_len = socklen as usize - path_offset(&sockaddr);
// On FreeBSD and Darwin, it returns a length of 14/16, but an unnamed (all
// zero) address. Map that to a length of 0 to match other OS.
if sockaddr.sun_path[0] == 0 {
path_len = 0;
}
// SAFETY: going from i8 to u8 is fine in this context.
let mut path =
unsafe { &*(&sockaddr.sun_path[..path_len] as *const [libc::c_char] as *const [u8]) };
// Remove last null as `SocketAddr::from_pathname` doesn't accept it.
if let Some(0) = path.last() {
path = &path[..path.len() - 1];
}
let address = SocketAddr::from_pathname(Path::new(OsStr::from_bytes(path)))?;
Ok((socket, address))
}

View File

@ -1,5 +1,17 @@
mod socketaddr;
pub use self::socketaddr::SocketAddr;
#[cfg(target_os = "android")]
use std::os::android::net::SocketAddrExt;
#[cfg(target_os = "linux")]
use std::os::linux::net::SocketAddrExt;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::FromRawFd;
use std::os::unix::net::SocketAddr;
use std::{io, mem, ptr};
pub(crate) mod datagram;
pub(crate) mod listener;
pub(crate) mod stream;
const UNNAMED_ADDRESS: &[u8] = &[];
/// Get the `sun_path` field offset of `sockaddr_un` for the target OS.
///
@ -7,153 +19,156 @@ pub use self::socketaddr::SocketAddr;
/// `size_of::<sa_family_t>()`, but some other implementations include
/// other fields before `sun_path`, so the expression more portably
/// describes the size of the address structure.
pub(in crate::sys) fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
let base = sockaddr as *const _ as usize;
let path = &sockaddr.sun_path as *const _ as usize;
path - base
}
cfg_os_poll! {
use std::cmp::Ordering;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{RawFd, FromRawFd};
use std::path::Path;
use std::{io, mem};
/// Converts a Rust `SocketAddr` into the system representation.
fn unix_addr(address: &SocketAddr) -> (libc::sockaddr_un, libc::socklen_t) {
// SAFETY: `libc::sockaddr_un` zero filled is properly initialized.
//
// `0` is a valid value for `sockaddr_un::sun_family`; it is
// `libc::AF_UNSPEC`.
//
// `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
// abstract path.
let mut sockaddr = unsafe { mem::zeroed::<libc::sockaddr_un>() };
pub(crate) mod datagram;
pub(crate) mod listener;
pub(crate) mod stream;
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
pub(in crate::sys) fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
// This is safe to assume because a `libc::sockaddr_un` filled with `0`
// bytes is properly initialized.
//
// `0` is a valid value for `sockaddr_un::sun_family`; it is
// `libc::AF_UNSPEC`.
//
// `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
// abstract path.
let mut sockaddr = unsafe { sockaddr.assume_init() };
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
let bytes = path.as_os_str().as_bytes();
match (bytes.first(), bytes.len().cmp(&sockaddr.sun_path.len())) {
// Abstract paths don't need a null terminator
(Some(&0), Ordering::Greater) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be no longer than libc::sockaddr_un.sun_path",
));
#[allow(unused_mut)] // Only used with abstract namespaces.
let mut offset = 0;
let addr = match address.as_pathname() {
Some(path) => path.as_os_str().as_bytes(),
#[cfg(any(target_os = "android", target_os = "linux"))]
None => match address.as_abstract_name() {
Some(name) => {
offset += 1;
name
}
(_, Ordering::Greater) | (_, Ordering::Equal) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be shorter than libc::sockaddr_un.sun_path",
));
}
_ => {}
}
None => UNNAMED_ADDRESS,
},
#[cfg(not(any(target_os = "android", target_os = "linux")))]
None => UNNAMED_ADDRESS,
};
for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) {
*dst = *src as libc::c_char;
}
// SAFETY: `addr` and `sockaddr.sun_path` are not overlapping and both point
// to valid memory.
// SAFETY: since `addr` is a valid Unix address, it must not be larger than
// `SUN_LEN` bytes, thus we won't overwrite the size of sockaddr.sun_path.
// SAFETY: null byte is already written because we zeroed the address above.
debug_assert!(offset + addr.len() <= sockaddr.sun_path.len());
unsafe {
ptr::copy_nonoverlapping(
addr.as_ptr(),
sockaddr.sun_path.as_mut_ptr().add(offset).cast(),
addr.len(),
)
};
let offset = path_offset(&sockaddr);
let mut socklen = offset + bytes.len();
match bytes.first() {
// The struct has already been zeroes so the null byte for pathname
// addresses is already there.
Some(&0) | None => {}
Some(_) => socklen += 1,
}
Ok((sockaddr, socklen as libc::socklen_t))
let mut addrlen = path_offset(&sockaddr) + addr.len();
// +1 for null byte at the end of the path, not needed for abstract
// namespaces (which start with a null byte).
match addr.first() {
Some(&0) | None => {}
Some(_) => addrlen += 1,
}
fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
where T: FromRawFd,
// SAFETY: the length is fine to cast to `socklen_t` as it's 32 bits and the
// address can be at most `SUN_LEN` bytes.
(sockaddr, addrlen as _)
}
fn pair<T>(flags: libc::c_int) -> io::Result<(T, T)>
where
T: FromRawFd,
{
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "nto",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
)))]
let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
let mut fds = [-1; 2];
syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
// Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
//
// In order to set those flags, additional `fcntl` sys calls must be
// performed. If a `fnctl` fails after the sockets have been created,
// the file descriptors will leak. Creating `pair` above ensures that if
// there is an error, the file descriptors are closed.
#[cfg(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "nto",
target_os = "tvos",
target_os = "visionos",
target_os = "watchos",
target_os = "espidf",
target_os = "vita",
))]
{
#[cfg(not(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
)))]
let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
let mut fds = [-1; 2];
syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) };
// Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
//
// In order to set those flags, additional `fcntl` sys calls must be
// performed. If a `fnctl` fails after the sockets have been created,
// the file descriptors will leak. Creating `pair` above ensures that if
// there is an error, the file descriptors are closed.
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
))]
{
syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
}
Ok(pair)
syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
}
// The following functions can't simply be replaced with a call to
// `net::UnixDatagram` because of our `SocketAddr` type.
Ok(pair)
}
fn local_addr(socket: RawFd) -> io::Result<SocketAddr> {
SocketAddr::new(|sockaddr, socklen| syscall!(getsockname(socket, sockaddr, socklen)))
#[cfg(test)]
mod tests {
use std::os::unix::net::SocketAddr;
use std::path::Path;
use std::str;
use super::{path_offset, unix_addr};
#[test]
fn pathname_address() {
const PATH: &str = "./foo/bar.txt";
const PATH_LEN: usize = 13;
// Pathname addresses do have a null terminator, so `socklen` is
// expected to be `PATH_LEN` + `offset` + 1.
let address = SocketAddr::from_pathname(Path::new(PATH)).unwrap();
let (sockaddr, actual) = unix_addr(&address);
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset + 1;
assert_eq!(expected as libc::socklen_t, actual)
}
fn peer_addr(socket: RawFd) -> io::Result<SocketAddr> {
SocketAddr::new(|sockaddr, socklen| syscall!(getpeername(socket, sockaddr, socklen)))
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn abstract_address() {
use std::os::linux::net::SocketAddrExt;
#[cfg(test)]
mod tests {
use super::{path_offset, socket_addr};
use std::path::Path;
use std::str;
const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
const PATH_LEN: usize = 6;
#[test]
fn pathname_address() {
const PATH: &str = "./foo/bar.txt";
const PATH_LEN: usize = 13;
// Pathname addresses do have a null terminator, so `socklen` is
// expected to be `PATH_LEN` + `offset` + 1.
let path = Path::new(PATH);
let (sockaddr, actual) = socket_addr(path).unwrap();
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset + 1;
assert_eq!(expected as libc::socklen_t, actual)
}
#[test]
fn abstract_address() {
const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
const PATH_LEN: usize = 6;
// Abstract addresses do not have a null terminator, so `socklen` is
// expected to be `PATH_LEN` + `offset`.
let abstract_path = str::from_utf8(PATH).unwrap();
let path = Path::new(abstract_path);
let (sockaddr, actual) = socket_addr(path).unwrap();
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset;
assert_eq!(expected as libc::socklen_t, actual)
}
// Abstract addresses do not have a null terminator, so `socklen` is
// expected to be `PATH_LEN` + `offset`.
let address = SocketAddr::from_abstract_name(PATH).unwrap();
let (sockaddr, actual) = unix_addr(&address);
let offset = path_offset(&sockaddr);
let expected = PATH_LEN + offset;
assert_eq!(expected as libc::socklen_t, actual)
}
}

View File

@ -1,130 +0,0 @@
use super::path_offset;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::{ascii, fmt};
/// An address associated with a `mio` specific Unix socket.
///
/// This is implemented instead of imported from [`net::SocketAddr`] because
/// there is no way to create a [`net::SocketAddr`]. One must be returned by
/// [`accept`], so this is returned instead.
///
/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr
/// [`accept`]: #method.accept
pub struct SocketAddr {
sockaddr: libc::sockaddr_un,
socklen: libc::socklen_t,
}
struct AsciiEscaped<'a>(&'a [u8]);
enum AddressKind<'a> {
Unnamed,
Pathname(&'a Path),
Abstract(&'a [u8]),
}
impl SocketAddr {
fn address(&self) -> AddressKind<'_> {
let offset = path_offset(&self.sockaddr);
// Don't underflow in `len` below.
if (self.socklen as usize) < offset {
return AddressKind::Unnamed;
}
let len = self.socklen as usize - offset;
let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };
// macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
if len == 0
|| (cfg!(not(any(target_os = "linux", target_os = "android")))
&& self.sockaddr.sun_path[0] == 0)
{
AddressKind::Unnamed
} else if self.sockaddr.sun_path[0] == 0 {
AddressKind::Abstract(&path[1..len])
} else {
AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
}
}
}
cfg_os_poll! {
use std::{io, mem};
impl SocketAddr {
pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr>
where
F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>,
{
let mut sockaddr = {
let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
unsafe { sockaddr.assume_init() }
};
let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr;
let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
f(raw_sockaddr, &mut socklen)?;
Ok(SocketAddr::from_parts(sockaddr, socklen))
}
pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr {
SocketAddr { sockaddr, socklen }
}
/// Returns `true` if the address is unnamed.
///
/// Documentation reflected in [`SocketAddr`]
///
/// [`SocketAddr`]: std::os::unix::net::SocketAddr
pub fn is_unnamed(&self) -> bool {
matches!(self.address(), AddressKind::Unnamed)
}
/// Returns the contents of this address if it is a `pathname` address.
///
/// Documentation reflected in [`SocketAddr`]
///
/// [`SocketAddr`]: std::os::unix::net::SocketAddr
pub fn as_pathname(&self) -> Option<&Path> {
if let AddressKind::Pathname(path) = self.address() {
Some(path)
} else {
None
}
}
/// Returns the contents of this address if it is an abstract namespace
/// without the leading null byte.
// Link to std::os::unix::net::SocketAddr pending
// https://github.com/rust-lang/rust/issues/85410.
pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
if let AddressKind::Abstract(path) = self.address() {
Some(path)
} else {
None
}
}
}
}
impl fmt::Debug for SocketAddr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.address() {
AddressKind::Unnamed => write!(fmt, "(unnamed)"),
AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
}
}
}
impl<'a> fmt::Display for AsciiEscaped<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "\"")?;
for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
write!(fmt, "{}", byte as char)?;
}
write!(fmt, "\"")
}
}

View File

@ -1,18 +1,17 @@
use super::{socket_addr, SocketAddr};
use crate::sys::unix::net::new_socket;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::net;
use std::path::Path;
use std::os::fd::FromRawFd;
use std::os::unix::net::{self, SocketAddr};
pub(crate) fn connect(path: &Path) -> io::Result<net::UnixStream> {
let (sockaddr, socklen) = socket_addr(path)?;
let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr;
use crate::sys::unix::net::new_socket;
use crate::sys::unix::uds::unix_addr;
pub(crate) fn connect_addr(address: &SocketAddr) -> io::Result<net::UnixStream> {
let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
let socket = unsafe { net::UnixStream::from_raw_fd(fd) };
match syscall!(connect(fd, sockaddr, socklen)) {
let (unix_address, addrlen) = unix_addr(address);
let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr;
match syscall!(connect(fd, sockaddr, addrlen)) {
Ok(_) => {}
Err(ref err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {}
Err(e) => return Err(e),
@ -24,11 +23,3 @@ pub(crate) fn connect(path: &Path) -> io::Result<net::UnixStream> {
pub(crate) fn pair() -> io::Result<(net::UnixStream, net::UnixStream)> {
super::pair(libc::SOCK_STREAM)
}
pub(crate) fn local_addr(socket: &net::UnixStream) -> io::Result<SocketAddr> {
super::local_addr(socket.as_raw_fd())
}
pub(crate) fn peer_addr(socket: &net::UnixStream) -> io::Result<SocketAddr> {
super::peer_addr(socket.as_raw_fd())
}

View File

@ -1,185 +0,0 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
mod eventfd {
use crate::sys::Selector;
use crate::{Interest, Token};
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::io::FromRawFd;
/// Waker backed by `eventfd`.
///
/// `eventfd` is effectively an 64 bit counter. All writes must be of 8
/// bytes (64 bits) and are converted (native endian) into an 64 bit
/// unsigned integer and added to the count. Reads must also be 8 bytes and
/// reset the count to 0, returning the count.
#[derive(Debug)]
pub struct Waker {
fd: File,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let fd = syscall!(eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK))?;
let file = unsafe { File::from_raw_fd(fd) };
selector.register(fd, token, Interest::READABLE)?;
Ok(Waker { fd: file })
}
pub fn wake(&self) -> io::Result<()> {
let buf: [u8; 8] = 1u64.to_ne_bytes();
match (&self.fd).write(&buf) {
Ok(_) => Ok(()),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
// Writing only blocks if the counter is going to overflow.
// So we'll reset the counter to 0 and wake it again.
self.reset()?;
self.wake()
}
Err(err) => Err(err),
}
}
/// Reset the eventfd object, only need to call this if `wake` fails.
fn reset(&self) -> io::Result<()> {
let mut buf: [u8; 8] = 0u64.to_ne_bytes();
match (&self.fd).read(&mut buf) {
Ok(_) => Ok(()),
// If the `Waker` hasn't been awoken yet this will return a
// `WouldBlock` error which we can safely ignore.
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()),
Err(err) => Err(err),
}
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::eventfd::Waker;
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
))]
mod kqueue {
use crate::sys::Selector;
use crate::Token;
use std::io;
/// Waker backed by kqueue user space notifications (`EVFILT_USER`).
///
/// The implementation is fairly simple, first the kqueue must be setup to
/// receive waker events this done by calling `Selector.setup_waker`. Next
/// we need access to kqueue, thus we need to duplicate the file descriptor.
/// Now waking is as simple as adding an event to the kqueue.
#[derive(Debug)]
pub struct Waker {
selector: Selector,
token: Token,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let selector = selector.try_clone()?;
selector.setup_waker(token)?;
Ok(Waker { selector, token })
}
pub fn wake(&self) -> io::Result<()> {
self.selector.wake(self.token)
}
}
}
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "tvos",
target_os = "watchos",
))]
pub use self::kqueue::Waker;
#[cfg(any(
target_os = "dragonfly",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
))]
mod pipe {
use crate::sys::unix::Selector;
use crate::{Interest, Token};
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::io::FromRawFd;
/// Waker backed by a unix pipe.
///
/// Waker controls both the sending and receiving ends and empties the pipe
/// if writing to it (waking) fails.
#[derive(Debug)]
pub struct Waker {
sender: File,
receiver: File,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let mut fds = [-1; 2];
syscall!(pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK | libc::O_CLOEXEC))?;
let sender = unsafe { File::from_raw_fd(fds[1]) };
let receiver = unsafe { File::from_raw_fd(fds[0]) };
selector.register(fds[0], token, Interest::READABLE)?;
Ok(Waker { sender, receiver })
}
pub fn wake(&self) -> io::Result<()> {
// The epoll emulation on some illumos systems currently requires
// the pipe buffer to be completely empty for an edge-triggered
// wakeup on the pipe read side.
#[cfg(target_os = "illumos")]
self.empty();
match (&self.sender).write(&[1]) {
Ok(_) => Ok(()),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
// The reading end is full so we'll empty the buffer and try
// again.
self.empty();
self.wake()
}
Err(ref err) if err.kind() == io::ErrorKind::Interrupted => self.wake(),
Err(err) => Err(err),
}
}
/// Empty the pipe's buffer, only need to call this if `wake` fails.
/// This ignores any errors.
fn empty(&self) {
let mut buf = [0; 4096];
loop {
match (&self.receiver).read(&mut buf) {
Ok(n) if n > 0 => continue,
_ => return,
}
}
}
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
))]
pub use self::pipe::Waker;

View File

@ -0,0 +1,81 @@
use std::fs::File;
use std::io::{self, Read, Write};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd, RawFd};
use crate::sys::Selector;
use crate::{Interest, Token};
/// Waker backed by `eventfd`.
///
/// `eventfd` is effectively an 64 bit counter. All writes must be of 8
/// bytes (64 bits) and are converted (native endian) into an 64 bit
/// unsigned integer and added to the count. Reads must also be 8 bytes and
/// reset the count to 0, returning the count.
#[derive(Debug)]
pub(crate) struct Waker {
fd: File,
}
impl Waker {
#[allow(dead_code)] // Not used by the `poll(2)` implementation.
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let waker = Waker::new_unregistered()?;
selector.register(waker.fd.as_raw_fd(), token, Interest::READABLE)?;
Ok(waker)
}
pub(crate) fn new_unregistered() -> io::Result<Waker> {
#[cfg(not(target_os = "espidf"))]
let flags = libc::EFD_CLOEXEC | libc::EFD_NONBLOCK;
// ESP-IDF is EFD_NONBLOCK by default and errors if you try to pass this flag.
#[cfg(target_os = "espidf")]
let flags = 0;
let fd = syscall!(eventfd(0, flags))?;
let file = unsafe { File::from_raw_fd(fd) };
Ok(Waker { fd: file })
}
#[allow(clippy::unused_io_amount)] // Don't care about partial writes.
pub(crate) fn wake(&self) -> io::Result<()> {
let buf: [u8; 8] = 1u64.to_ne_bytes();
match (&self.fd).write(&buf) {
Ok(_) => Ok(()),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
// Writing only blocks if the counter is going to overflow.
// So we'll reset the counter to 0 and wake it again.
self.reset()?;
self.wake()
}
Err(err) => Err(err),
}
}
#[allow(dead_code)] // Only used by the `poll(2)` implementation.
pub(crate) fn ack_and_reset(&self) {
let _ = self.reset();
}
/// Reset the eventfd object, only need to call this if `wake` fails.
#[allow(clippy::unused_io_amount)] // Don't care about partial reads.
fn reset(&self) -> io::Result<()> {
let mut buf: [u8; 8] = 0u64.to_ne_bytes();
match (&self.fd).read(&mut buf) {
Ok(_) => Ok(()),
// If the `Waker` hasn't been awoken yet this will return a
// `WouldBlock` error which we can safely ignore.
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()),
Err(err) => Err(err),
}
}
}
impl AsRawFd for Waker {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}

View File

@ -0,0 +1,28 @@
use std::io;
use crate::sys::Selector;
use crate::Token;
/// Waker backed by kqueue user space notifications (`EVFILT_USER`).
///
/// The implementation is fairly simple, first the kqueue must be setup to
/// receive waker events this done by calling `Selector.setup_waker`. Next
/// we need access to kqueue, thus we need to duplicate the file descriptor.
/// Now waking is as simple as adding an event to the kqueue.
#[derive(Debug)]
pub(crate) struct Waker {
selector: Selector,
token: Token,
}
impl Waker {
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let selector = selector.try_clone()?;
selector.setup_waker(token)?;
Ok(Waker { selector, token })
}
pub(crate) fn wake(&self) -> io::Result<()> {
self.selector.wake(self.token)
}
}

View File

@ -0,0 +1,81 @@
use std::fs::File;
use std::io::{self, Read, Write};
#[cfg(not(target_os = "hermit"))]
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
// TODO: once <https://github.com/rust-lang/rust/issues/126198> is fixed this
// can use `std::os::fd` and be merged with the above.
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsRawFd, FromRawFd, RawFd};
use crate::sys::unix::pipe;
use crate::sys::Selector;
use crate::{Interest, Token};
/// Waker backed by a unix pipe.
///
/// Waker controls both the sending and receiving ends and empties the pipe
/// if writing to it (waking) fails.
#[derive(Debug)]
pub(crate) struct Waker {
sender: File,
receiver: File,
}
impl Waker {
#[allow(dead_code)] // Not used by the `poll(2)` implementation.
pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let waker = Waker::new_unregistered()?;
selector.register(waker.receiver.as_raw_fd(), token, Interest::READABLE)?;
Ok(waker)
}
pub(crate) fn new_unregistered() -> io::Result<Waker> {
let [receiver, sender] = pipe::new_raw()?;
let sender = unsafe { File::from_raw_fd(sender) };
let receiver = unsafe { File::from_raw_fd(receiver) };
Ok(Waker { sender, receiver })
}
pub(crate) fn wake(&self) -> io::Result<()> {
// The epoll emulation on some illumos systems currently requires
// the pipe buffer to be completely empty for an edge-triggered
// wakeup on the pipe read side.
#[cfg(target_os = "illumos")]
self.empty();
match (&self.sender).write(&[1]) {
Ok(_) => Ok(()),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
// The reading end is full so we'll empty the buffer and try
// again.
self.empty();
self.wake()
}
Err(ref err) if err.kind() == io::ErrorKind::Interrupted => self.wake(),
Err(err) => Err(err),
}
}
#[allow(dead_code)] // Only used by the `poll(2)` implementation.
pub(crate) fn ack_and_reset(&self) {
self.empty();
}
/// Empty the pipe's buffer, only need to call this if `wake` fails.
/// This ignores any errors.
fn empty(&self) {
let mut buf = [0; 4096];
loop {
match (&self.receiver).read(&mut buf) {
Ok(n) if n > 0 => continue,
_ => return,
}
}
}
}
impl AsRawFd for Waker {
fn as_raw_fd(&self) -> RawFd {
self.receiver.as_raw_fd()
}
}

View File

@ -207,7 +207,7 @@ impl Selector {
}
/// Token used to a add a timeout subscription, also used in removing it again.
const TIMEOUT_TOKEN: wasi::Userdata = wasi::Userdata::max_value();
const TIMEOUT_TOKEN: wasi::Userdata = wasi::Userdata::MAX;
/// Returns a `wasi::Subscription` for `timeout`.
fn timeout_subscription(timeout: Duration) -> wasi::Subscription {
@ -334,7 +334,7 @@ pub(crate) mod event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventFdReadwrite")
.field("nbytes", &self.0.nbytes)
.field("flags", &self.0.flags)
.field("flags", &EventrwflagsDetails(self.0.flags))
.finish()
}
}

View File

@ -41,6 +41,14 @@ impl Event {
pub(super) fn to_completion_status(&self) -> CompletionStatus {
CompletionStatus::new(self.flags, self.data as usize, std::ptr::null_mut())
}
#[cfg(feature = "os-ext")]
pub(super) fn to_completion_status_with_overlapped(
&self,
overlapped: *mut super::Overlapped,
) -> CompletionStatus {
CompletionStatus::new(self.flags, self.data as usize, overlapped)
}
}
pub(crate) const READABLE_FLAGS: u32 = afd::POLL_RECEIVE

View File

@ -96,7 +96,7 @@ impl CompletionPort {
);
let mut removed = 0;
let timeout = duration_millis(timeout);
let len = cmp::min(list.len(), <u32>::max_value() as usize) as u32;
let len = cmp::min(list.len(), u32::MAX as usize) as u32;
let ret = unsafe {
GetQueuedCompletionStatusEx(
self.handle.raw(),

View File

@ -13,7 +13,7 @@ mod overlapped;
use overlapped::Overlapped;
mod selector;
pub use selector::{Selector, SelectorInner, SockState};
pub use selector::Selector;
// Macros must be defined before the modules that use them
cfg_net! {
@ -35,6 +35,8 @@ cfg_net! {
pub(crate) mod tcp;
pub(crate) mod udp;
pub use selector::{SelectorInner, SockState};
}
cfg_os_ext! {

View File

@ -84,6 +84,7 @@ struct Inner {
connect: Overlapped,
read: Overlapped,
write: Overlapped,
event: Overlapped,
// END NOTE.
handle: Handle,
connecting: AtomicBool,
@ -110,10 +111,16 @@ impl Inner {
/// Same as [`ptr_from_conn_overlapped`] but for `Inner.write`.
unsafe fn ptr_from_write_overlapped(ptr: *mut OVERLAPPED) -> *const Inner {
// `read` is after `connect: Overlapped` and `read: Overlapped`.
// `write` is after `connect: Overlapped` and `read: Overlapped`.
(ptr as *mut Overlapped).wrapping_sub(2) as *const Inner
}
/// Same as [`ptr_from_conn_overlapped`] but for `Inner.event`.
unsafe fn ptr_from_event_overlapped(ptr: *mut OVERLAPPED) -> *const Inner {
// `event` is after `connect: Overlapped`, `read: Overlapped`, and `write: Overlapped`.
(ptr as *mut Overlapped).wrapping_sub(3) as *const Inner
}
/// Issue a connection request with the specified overlapped operation.
///
/// This function will issue a request to connect a client to this server,
@ -478,6 +485,7 @@ impl FromRawHandle for NamedPipe {
connecting: AtomicBool::new(false),
read: Overlapped::new(read_done),
write: Overlapped::new(write_done),
event: Overlapped::new(event_done),
io: Mutex::new(Io {
cp: None,
token: None,
@ -724,7 +732,7 @@ impl Inner {
// out the error.
Err(e) => {
io.read = State::Err(e);
io.notify_readable(events);
io.notify_readable(me, events);
true
}
}
@ -787,7 +795,7 @@ impl Inner {
Ok(None) => (),
Err(e) => {
io.write = State::Err(e);
io.notify_writable(events);
io.notify_writable(me, events);
}
}
}
@ -797,7 +805,7 @@ impl Inner {
#[allow(clippy::needless_option_as_deref)]
if Inner::schedule_read(me, &mut io, events.as_deref_mut()) {
if let State::None = io.write {
io.notify_writable(events);
io.notify_writable(me, events);
}
}
}
@ -877,7 +885,7 @@ fn read_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
}
// Flag our readiness that we've got data.
io.notify_readable(events);
io.notify_readable(&me, events);
}
fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
@ -895,7 +903,7 @@ fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
// `Ok` here means, that the operation was completed immediately
// `bytes_transferred` is already reported to a client
State::Ok(..) => {
io.notify_writable(events);
io.notify_writable(&me, events);
return;
}
State::Pending(buf, pos) => (buf, pos),
@ -909,7 +917,7 @@ fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
let new_pos = pos + (status.bytes_transferred() as usize);
if new_pos == buf.len() {
me.put_buffer(buf);
io.notify_writable(events);
io.notify_writable(&me, events);
} else {
Inner::schedule_write(&me, buf, new_pos, &mut io, events);
}
@ -917,12 +925,38 @@ fn write_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
Err(e) => {
debug_assert_eq!(status.bytes_transferred(), 0);
io.write = State::Err(e);
io.notify_writable(events);
io.notify_writable(&me, events);
}
}
}
}
fn event_done(status: &OVERLAPPED_ENTRY, events: Option<&mut Vec<Event>>) {
let status = CompletionStatus::from_entry(status);
// Acquire the `Arc<Inner>`. Note that we should be guaranteed that
// the refcount is available to us due to the `mem::forget` in
// `schedule_write` above.
let me = unsafe { Arc::from_raw(Inner::ptr_from_event_overlapped(status.overlapped())) };
let io = me.io.lock().unwrap();
// Make sure the I/O handle is still registered with the selector
if io.token.is_some() {
// This method is also called during `Selector::drop` to perform
// cleanup. In this case, `events` is `None` and we don't need to track
// the event.
if let Some(events) = events {
let mut ev = Event::from_completion_status(&status);
// Reverse the `.data` alteration done in `schedule_event`. This
// alteration was done so the selector recognized the event as one from
// a named pipe.
ev.data >>= 1;
events.push(ev);
}
}
}
impl Io {
fn check_association(&self, registry: &Registry, required: bool) -> io::Result<()> {
match self.cp {
@ -938,7 +972,7 @@ impl Io {
}
}
fn notify_readable(&self, events: Option<&mut Vec<Event>>) {
fn notify_readable(&self, me: &Arc<Inner>, events: Option<&mut Vec<Event>>) {
if let Some(token) = self.token {
let mut ev = Event::new(token);
ev.set_readable();
@ -946,12 +980,12 @@ impl Io {
if let Some(events) = events {
events.push(ev);
} else {
let _ = self.cp.as_ref().unwrap().post(ev.to_completion_status());
self.schedule_event(me, ev);
}
}
}
fn notify_writable(&self, events: Option<&mut Vec<Event>>) {
fn notify_writable(&self, me: &Arc<Inner>, events: Option<&mut Vec<Event>>) {
if let Some(token) = self.token {
let mut ev = Event::new(token);
ev.set_writable();
@ -959,7 +993,32 @@ impl Io {
if let Some(events) = events {
events.push(ev);
} else {
let _ = self.cp.as_ref().unwrap().post(ev.to_completion_status());
self.schedule_event(me, ev);
}
}
}
fn schedule_event(&self, me: &Arc<Inner>, mut event: Event) {
// Alter the token so that the selector will identify the IOCP event as
// one for a named pipe. This will be reversed in `event_done`
//
// `data` for named pipes is an auto-incrementing counter. Because
// `data` is `u64` we do not risk losing the most-significant bit
// (unless a user creates 2^62 named pipes during the lifetime of the
// process).
event.data <<= 1;
event.data += 1;
let completion_status =
event.to_completion_status_with_overlapped(me.event.as_ptr() as *mut _);
match self.cp.as_ref().unwrap().post(completion_status) {
Ok(_) => {
// Increase the ref count of `Inner` for the completion event.
mem::forget(me.clone());
}
Err(_) => {
// Nothing to do here
}
}
}

View File

@ -50,7 +50,7 @@ impl AfdGroup {
}
cfg_io_source! {
const POLL_GROUP__MAX_GROUP_SIZE: usize = 32;
const POLL_GROUP_MAX_GROUP_SIZE: usize = 32;
impl AfdGroup {
pub fn acquire(&self) -> io::Result<Arc<Afd>> {
@ -59,7 +59,7 @@ cfg_io_source! {
self._alloc_afd_group(&mut afd_group)?;
} else {
// + 1 reference in Vec
if Arc::strong_count(afd_group.last().unwrap()) > POLL_GROUP__MAX_GROUP_SIZE {
if Arc::strong_count(afd_group.last().unwrap()) > POLL_GROUP_MAX_GROUP_SIZE {
self._alloc_afd_group(&mut afd_group)?;
}
}
@ -328,8 +328,6 @@ pub struct Selector {
#[cfg(debug_assertions)]
id: usize,
pub(super) inner: Arc<SelectorInner>,
#[cfg(debug_assertions)]
has_waker: AtomicBool,
}
impl Selector {
@ -341,8 +339,6 @@ impl Selector {
#[cfg(debug_assertions)]
id,
inner: Arc::new(inner),
#[cfg(debug_assertions)]
has_waker: AtomicBool::new(false),
}
})
}
@ -352,8 +348,6 @@ impl Selector {
#[cfg(debug_assertions)]
id: self.id,
inner: Arc::clone(&self.inner),
#[cfg(debug_assertions)]
has_waker: AtomicBool::new(self.has_waker.load(Ordering::Acquire)),
})
}
@ -365,11 +359,6 @@ impl Selector {
self.inner.select(events, timeout)
}
#[cfg(debug_assertions)]
pub fn register_waker(&self) -> bool {
self.has_waker.swap(true, Ordering::AcqRel)
}
pub(super) fn clone_port(&self) -> Arc<CompletionPort> {
self.inner.cp.clone()
}

View File

@ -50,7 +50,7 @@ pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()>
use std::convert::TryInto;
use WinSock::listen;
let backlog = backlog.try_into().unwrap_or(i32::max_value());
let backlog = backlog.try_into().unwrap_or(i32::MAX);
syscall!(
listen(socket.as_raw_socket() as _, backlog),
PartialEq::eq,

View File

@ -1 +1 @@
{"files":{"CHANGELOG.md":"0fa977ef55cfe5c26907f5f3c8700a777d205608ab9edf7fd98762b60ba95790","Cargo.toml":"8133ec808652d1d7aa1eb39b135c2afa559265bb46ebbaf2a3cfe8b552fa9202","LICENSE":"9797ea525350e8779aab3771e0276bbeea8b824893882172acfc94743b8d953d","README.md":"6094ea500349ce239a12b07d7dfd4ea965a7f14c993da2abc4b3c39a0479683a","src/entry.rs":"237a4a8e159e841aaa2b49ec1697a68fef6165542ac0197dba1ee963f688dc1d","src/lib.rs":"8ada51fda322e36e6b52a3e1fcbeab7c72332f79e54af111584f5a17b961afb5","src/select.rs":"e01c34fe0fdbc49a40b15b5b42816eea7d7b13db6e3a2774de92eb87f6e48231"},"package":"630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"}
{"files":{"CHANGELOG.md":"bc308e94c0a0c9a8c595fdb708dec60d08dca5f4fc67b64287676b619675f91b","Cargo.toml":"b86acb2f048fd43ade67bc01187f9eea2df4855a0261102cca651453a23d5cba","LICENSE":"0b83dc40cba89b9922bb84b0a9c7d2768ce37c1d7e138b7424fd4549915778c9","README.md":"6094ea500349ce239a12b07d7dfd4ea965a7f14c993da2abc4b3c39a0479683a","src/entry.rs":"4b7e392119553a795508aaf021eac5311da50702ac47f536753f8c8e7141593a","src/lib.rs":"b146a3bcf92aaf042e0ff7fd5387b69bc9882d2b5e0ab70298df83ee54ad6d7c","src/select.rs":"8fdef69056b4c24d109439e68535b51c0781505c4b6fc49cacf10d32097c54f1"},"package":"693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"}

View File

@ -1,3 +1,25 @@
# 2.4.0 (July 22nd, 2024)
- msrv: increase MSRV to 1.70 ([#6645])
- macros: allow `unhandled_panic` behavior for `#[tokio::main]` and `#[tokio::test]` ([#6593])
[#6593]: https://github.com/tokio-rs/tokio/pull/6593
[#6645]: https://github.com/tokio-rs/tokio/pull/6645
# 2.3.0 (May 30th, 2024)
- macros: make `#[tokio::test]` append `#[test]` at the end of the attribute list ([#6497])
[#6497]: https://github.com/tokio-rs/tokio/pull/6497
# 2.2.0 (November 19th, 2023)
### Changed
- use `::core` qualified imports instead of `::std` inside `tokio::test` macro ([#5973])
[#5973]: https://github.com/tokio-rs/tokio/pull/5973
# 2.1.0 (April 25th, 2023)
- macros: fix typo in `#[tokio::test]` docs ([#5636])

View File

@ -10,10 +10,10 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
rust-version = "1.56"
edition = "2021"
rust-version = "1.70"
name = "tokio-macros"
version = "2.1.0"
version = "2.4.0"
authors = ["Tokio Contributors <team@tokio.rs>"]
description = """
Tokio's proc macros.
@ -31,7 +31,7 @@ all-features = true
proc-macro = true
[dependencies.proc-macro2]
version = "1.0.7"
version = "1.0.60"
[dependencies.quote]
version = "1"

View File

@ -1,32 +1,7 @@
Copyright (c) 2023 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The MIT License (MIT)
MIT License
Copyright (c) 2019 Yoshua Wuyts
Copyright (c) Tokio Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -25,11 +25,37 @@ impl RuntimeFlavor {
}
}
#[derive(Clone, Copy, PartialEq)]
enum UnhandledPanic {
Ignore,
ShutdownRuntime,
}
impl UnhandledPanic {
fn from_str(s: &str) -> Result<UnhandledPanic, String> {
match s {
"ignore" => Ok(UnhandledPanic::Ignore),
"shutdown_runtime" => Ok(UnhandledPanic::ShutdownRuntime),
_ => Err(format!("No such unhandled panic behavior `{}`. The unhandled panic behaviors are `ignore` and `shutdown_runtime`.", s)),
}
}
fn into_tokens(self, crate_path: &TokenStream) -> TokenStream {
match self {
UnhandledPanic::Ignore => quote! { #crate_path::runtime::UnhandledPanic::Ignore },
UnhandledPanic::ShutdownRuntime => {
quote! { #crate_path::runtime::UnhandledPanic::ShutdownRuntime }
}
}
}
}
struct FinalConfig {
flavor: RuntimeFlavor,
worker_threads: Option<usize>,
start_paused: Option<bool>,
crate_name: Option<Path>,
unhandled_panic: Option<UnhandledPanic>,
}
/// Config used in case of the attribute not being able to build a valid config
@ -38,6 +64,7 @@ const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
worker_threads: None,
start_paused: None,
crate_name: None,
unhandled_panic: None,
};
struct Configuration {
@ -48,6 +75,7 @@ struct Configuration {
start_paused: Option<(bool, Span)>,
is_test: bool,
crate_name: Option<Path>,
unhandled_panic: Option<(UnhandledPanic, Span)>,
}
impl Configuration {
@ -63,6 +91,7 @@ impl Configuration {
start_paused: None,
is_test,
crate_name: None,
unhandled_panic: None,
}
}
@ -117,6 +146,25 @@ impl Configuration {
Ok(())
}
fn set_unhandled_panic(
&mut self,
unhandled_panic: syn::Lit,
span: Span,
) -> Result<(), syn::Error> {
if self.unhandled_panic.is_some() {
return Err(syn::Error::new(
span,
"`unhandled_panic` set multiple times.",
));
}
let unhandled_panic = parse_string(unhandled_panic, span, "unhandled_panic")?;
let unhandled_panic =
UnhandledPanic::from_str(&unhandled_panic).map_err(|err| syn::Error::new(span, err))?;
self.unhandled_panic = Some((unhandled_panic, span));
Ok(())
}
fn macro_name(&self) -> &'static str {
if self.is_test {
"tokio::test"
@ -126,22 +174,22 @@ impl Configuration {
}
fn build(&self) -> Result<FinalConfig, syn::Error> {
let flavor = self.flavor.unwrap_or(self.default_flavor);
use RuntimeFlavor::*;
use RuntimeFlavor as F;
let flavor = self.flavor.unwrap_or(self.default_flavor);
let worker_threads = match (flavor, self.worker_threads) {
(CurrentThread, Some((_, worker_threads_span))) => {
(F::CurrentThread, Some((_, worker_threads_span))) => {
let msg = format!(
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(worker_threads_span, msg));
}
(CurrentThread, None) => None,
(Threaded, worker_threads) if self.rt_multi_thread_available => {
(F::CurrentThread, None) => None,
(F::Threaded, worker_threads) if self.rt_multi_thread_available => {
worker_threads.map(|(val, _span)| val)
}
(Threaded, _) => {
(F::Threaded, _) => {
let msg = if self.flavor.is_none() {
"The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled."
} else {
@ -152,14 +200,26 @@ impl Configuration {
};
let start_paused = match (flavor, self.start_paused) {
(Threaded, Some((_, start_paused_span))) => {
(F::Threaded, Some((_, start_paused_span))) => {
let msg = format!(
"The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(start_paused_span, msg));
}
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
(F::CurrentThread, Some((start_paused, _))) => Some(start_paused),
(_, None) => None,
};
let unhandled_panic = match (flavor, self.unhandled_panic) {
(F::Threaded, Some((_, unhandled_panic_span))) => {
let msg = format!(
"The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
self.macro_name(),
);
return Err(syn::Error::new(unhandled_panic_span, msg));
}
(F::CurrentThread, Some((unhandled_panic, _))) => Some(unhandled_panic),
(_, None) => None,
};
@ -168,6 +228,7 @@ impl Configuration {
flavor,
worker_threads,
start_paused,
unhandled_panic,
})
}
}
@ -275,9 +336,13 @@ fn build_config(
"crate" => {
config.set_crate_name(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
"unhandled_panic" => {
config
.set_unhandled_panic(lit.clone(), syn::spanned::Spanned::span(lit))?;
}
name => {
let msg = format!(
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`",
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`",
name,
);
return Err(syn::Error::new_spanned(namevalue, msg));
@ -303,11 +368,11 @@ fn build_config(
macro_name
)
}
"flavor" | "worker_threads" | "start_paused" => {
"flavor" | "worker_threads" | "start_paused" | "crate" | "unhandled_panic" => {
format!("The `{}` attribute requires an argument.", name)
}
name => {
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`", name)
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.", name)
}
};
return Err(syn::Error::new_spanned(path, msg));
@ -354,13 +419,17 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
},
};
if let Some(v) = config.worker_threads {
rt = quote! { #rt.worker_threads(#v) };
rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) };
}
if let Some(v) = config.start_paused {
rt = quote! { #rt.start_paused(#v) };
rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) };
}
if let Some(v) = config.unhandled_panic {
let unhandled_panic = v.into_tokens(&crate_path);
rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) };
}
let header = if is_test {
let generated_attrs = if is_test {
quote! {
#[::core::prelude::v1::test]
}
@ -402,7 +471,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
quote! {
let body = async #body;
#crate_path::pin!(body);
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
let body: ::core::pin::Pin<&mut dyn ::core::future::Future<Output = #output_type>> = body;
}
} else {
quote! {
@ -410,7 +479,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
}
};
input.into_tokens(header, body, last_block)
input.into_tokens(generated_attrs, body, last_block)
}
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
@ -418,7 +487,6 @@ fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenS
tokens
}
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
// If any of the steps for this macro fail, we still want to expand to an item that is as close
// to the expected output as possible. This helps out IDEs such that completions and other
@ -443,6 +511,35 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
}
}
// Check whether given attribute is a test attribute of forms:
// * `#[test]`
// * `#[core::prelude::*::test]` or `#[::core::prelude::*::test]`
// * `#[std::prelude::*::test]` or `#[::std::prelude::*::test]`
fn is_test_attribute(attr: &Attribute) -> bool {
let path = match &attr.meta {
syn::Meta::Path(path) => path,
_ => return false,
};
let candidates = [
["core", "prelude", "*", "test"],
["std", "prelude", "*", "test"],
];
if path.leading_colon.is_none()
&& path.segments.len() == 1
&& path.segments[0].arguments.is_none()
&& path.segments[0].ident == "test"
{
return true;
} else if path.segments.len() != candidates[0].len() {
return false;
}
candidates.into_iter().any(|segments| {
path.segments.iter().zip(segments).all(|(segment, path)| {
segment.arguments.is_none() && (path == "*" || segment.ident == path)
})
})
}
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
// If any of the steps for this macro fail, we still want to expand to an item that is as close
// to the expected output as possible. This helps out IDEs such that completions and other
@ -451,8 +548,8 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
Ok(it) => it,
Err(e) => return token_stream_with_error(item, e),
};
let config = if let Some(attr) = input.attrs().find(|attr| attr.meta.path().is_ident("test")) {
let msg = "second test attribute is supplied";
let config = if let Some(attr) = input.attrs().find(|attr| is_test_attribute(attr)) {
let msg = "second test attribute is supplied, consider removing or changing the order of your test attributes";
Err(syn::Error::new_spanned(attr, msg))
} else {
AttributeArgs::parse_terminated
@ -493,13 +590,11 @@ impl ItemFn {
/// Convert our local function item into a token stream.
fn into_tokens(
self,
header: proc_macro2::TokenStream,
generated_attrs: proc_macro2::TokenStream,
body: proc_macro2::TokenStream,
last_block: proc_macro2::TokenStream,
) -> TokenStream {
let mut tokens = proc_macro2::TokenStream::new();
header.to_tokens(&mut tokens);
// Outer attributes are simply streamed as-is.
for attr in self.outer_attrs {
attr.to_tokens(&mut tokens);
@ -507,12 +602,15 @@ impl ItemFn {
// Inner attributes require extra care, since they're not supported on
// blocks (which is what we're expanded into) we instead lift them
// outside of the function. This matches the behaviour of `syn`.
// outside of the function. This matches the behavior of `syn`.
for mut attr in self.inner_attrs {
attr.style = syn::AttrStyle::Outer;
attr.to_tokens(&mut tokens);
}
// Add generated macros at the end, so macros processed later are aware of them.
generated_attrs.to_tokens(&mut tokens);
self.vis.to_tokens(&mut tokens);
self.sig.to_tokens(&mut tokens);
@ -586,6 +684,6 @@ impl ToTokens for Body<'_> {
for stmt in self.stmts {
stmt.to_tokens(tokens);
}
})
});
}
}

View File

@ -1,3 +1,4 @@
#![allow(unknown_lints, unexpected_cfgs)]
#![allow(clippy::needless_doctest_main)]
#![warn(
missing_debug_implementations,
@ -201,15 +202,60 @@ use proc_macro::TokenStream;
/// })
/// }
/// ```
///
/// ### Configure unhandled panic behavior
///
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
/// [`Builder::unhandled_panic`].
///
/// This option is only compatible with the `current_thread` runtime.
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// #[tokio::main(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
/// async fn main() {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// Equivalent code not using `#[tokio::main]`
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// fn main() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
/// .build()
/// .unwrap()
/// .block_on(async {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// })
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
/// documentation on unstable features][unstable] for details on how to enable
/// Tokio's unstable features.
///
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
/// [unstable]: ../tokio/index.html#unstable-features
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args.into(), item.into(), true).into()
}
/// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime`
/// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or
/// [Builder](../tokio/runtime/struct.builder.html) directly.
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
///
/// ## Function arguments:
///
@ -267,7 +313,6 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
/// }
/// ```
#[proc_macro_attribute]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
entry::main(args.into(), item.into(), false).into()
}
@ -365,7 +410,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// ### Set number of worker threads
///
/// ```no_run
/// #[tokio::test(flavor ="multi_thread", worker_threads = 2)]
/// #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
/// async fn my_test() {
/// assert!(true);
/// }
@ -424,6 +469,53 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
/// println!("Hello world");
/// }
/// ```
///
/// ### Configure unhandled panic behavior
///
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
/// [`Builder::unhandled_panic`].
///
/// This option is only compatible with the `current_thread` runtime.
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// #[tokio::test(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
/// async fn my_test() {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// Equivalent code not using `#[tokio::test]`
///
/// ```no_run
/// #[cfg(tokio_unstable)]
/// #[test]
/// fn my_test() {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
/// .build()
/// .unwrap()
/// .block_on(async {
/// let _ = tokio::spawn(async {
/// panic!("This panic will shutdown the runtime.");
/// }).await;
/// })
/// }
/// # #[cfg(not(tokio_unstable))]
/// # fn main() { }
/// ```
///
/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
/// documentation on unstable features][unstable] for details on how to enable
/// Tokio's unstable features.
///
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
/// [unstable]: ../tokio/index.html#unstable-features
#[proc_macro_attribute]
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::test(args.into(), item.into(), true).into()

View File

@ -73,27 +73,27 @@ fn clean_pattern(pat: &mut syn::Pat) {
}
}
syn::Pat::Or(or) => {
for case in or.cases.iter_mut() {
for case in &mut or.cases {
clean_pattern(case);
}
}
syn::Pat::Slice(slice) => {
for elem in slice.elems.iter_mut() {
for elem in &mut slice.elems {
clean_pattern(elem);
}
}
syn::Pat::Struct(struct_pat) => {
for field in struct_pat.fields.iter_mut() {
for field in &mut struct_pat.fields {
clean_pattern(&mut field.pat);
}
}
syn::Pat::Tuple(tuple) => {
for elem in tuple.elems.iter_mut() {
for elem in &mut tuple.elems {
clean_pattern(elem);
}
}
syn::Pat::TupleStruct(tuple) => {
for elem in tuple.elems.iter_mut() {
for elem in &mut tuple.elems {
clean_pattern(elem);
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,598 @@
# 1.39.2 (July 27th, 2024)
This release fixes a regression where the `select!` macro stopped accepting
expressions that make use of temporary lifetime extension. ([#6722])
[#6722]: https://github.com/tokio-rs/tokio/pull/6722
# 1.39.1 (July 23rd, 2024)
This release reverts "time: avoid traversing entries in the time wheel twice"
because it contains a bug. ([#6715])
[#6715]: https://github.com/tokio-rs/tokio/pull/6715
# 1.39.0 (July 23rd, 2024)
Yanked. Please use 1.39.1 instead.
- This release bumps the MSRV to 1.70. ([#6645])
- This release upgrades to mio v1. ([#6635])
- This release upgrades to windows-sys v0.52 ([#6154])
### Added
- io: implement `AsyncSeek` for `Empty` ([#6663])
- metrics: stabilize `num_alive_tasks` ([#6619], [#6667])
- process: add `Command::as_std_mut` ([#6608])
- sync: add `watch::Sender::same_channel` ([#6637])
- sync: add `{Receiver,UnboundedReceiver}::{sender_strong_count,sender_weak_count}` ([#6661])
- sync: implement `Default` for `watch::Sender` ([#6626])
- task: implement `Clone` for `AbortHandle` ([#6621])
- task: stabilize `consume_budget` ([#6622])
### Changed
- io: improve panic message of `ReadBuf::put_slice()` ([#6629])
- io: read during write in `copy_bidirectional` and `copy` ([#6532])
- runtime: replace `num_cpus` with `available_parallelism` ([#6709])
- task: avoid stack overflow when passing large future to `block_on` ([#6692])
- time: avoid traversing entries in the time wheel twice ([#6584])
- time: support `IntoFuture` with `timeout` ([#6666])
- macros: support `IntoFuture` with `join!` and `select!` ([#6710])
### Fixed
- docs: fix docsrs builds with the fs feature enabled ([#6585])
- io: only use short-read optimization on known-to-be-compatible platforms ([#6668])
- time: fix overflow panic when using large durations with `Interval` ([#6612])
### Added (unstable)
- macros: allow `unhandled_panic` behavior for `#[tokio::main]` and `#[tokio::test]` ([#6593])
- metrics: add `spawned_tasks_count` ([#6114])
- metrics: add `worker_park_unpark_count` ([#6696])
- metrics: add worker thread id ([#6695])
### Documented
- io: update `tokio::io::stdout` documentation ([#6674])
- macros: typo fix in `join.rs` and `try_join.rs` ([#6641])
- runtime: fix typo in `unhandled_panic` ([#6660])
- task: document behavior of `JoinSet::try_join_next` when all tasks are running ([#6671])
[#6114]: https://github.com/tokio-rs/tokio/pull/6114
[#6154]: https://github.com/tokio-rs/tokio/pull/6154
[#6532]: https://github.com/tokio-rs/tokio/pull/6532
[#6584]: https://github.com/tokio-rs/tokio/pull/6584
[#6585]: https://github.com/tokio-rs/tokio/pull/6585
[#6593]: https://github.com/tokio-rs/tokio/pull/6593
[#6608]: https://github.com/tokio-rs/tokio/pull/6608
[#6612]: https://github.com/tokio-rs/tokio/pull/6612
[#6619]: https://github.com/tokio-rs/tokio/pull/6619
[#6621]: https://github.com/tokio-rs/tokio/pull/6621
[#6622]: https://github.com/tokio-rs/tokio/pull/6622
[#6626]: https://github.com/tokio-rs/tokio/pull/6626
[#6629]: https://github.com/tokio-rs/tokio/pull/6629
[#6635]: https://github.com/tokio-rs/tokio/pull/6635
[#6637]: https://github.com/tokio-rs/tokio/pull/6637
[#6641]: https://github.com/tokio-rs/tokio/pull/6641
[#6645]: https://github.com/tokio-rs/tokio/pull/6645
[#6660]: https://github.com/tokio-rs/tokio/pull/6660
[#6661]: https://github.com/tokio-rs/tokio/pull/6661
[#6663]: https://github.com/tokio-rs/tokio/pull/6663
[#6666]: https://github.com/tokio-rs/tokio/pull/6666
[#6667]: https://github.com/tokio-rs/tokio/pull/6667
[#6668]: https://github.com/tokio-rs/tokio/pull/6668
[#6671]: https://github.com/tokio-rs/tokio/pull/6671
[#6674]: https://github.com/tokio-rs/tokio/pull/6674
[#6692]: https://github.com/tokio-rs/tokio/pull/6692
[#6695]: https://github.com/tokio-rs/tokio/pull/6695
[#6696]: https://github.com/tokio-rs/tokio/pull/6696
[#6709]: https://github.com/tokio-rs/tokio/pull/6709
[#6710]: https://github.com/tokio-rs/tokio/pull/6710
# 1.38.1 (July 16th, 2024)
This release fixes the bug identified as ([#6682]), which caused timers not
to fire when they should.
### Fixed
- time: update `wake_up` while holding all the locks of sharded time wheels ([#6683])
[#6682]: https://github.com/tokio-rs/tokio/pull/6682
[#6683]: https://github.com/tokio-rs/tokio/pull/6683
# 1.38.0 (May 30th, 2024)
This release marks the beginning of stabilization for runtime metrics. It
stabilizes `RuntimeMetrics::worker_count`. Future releases will continue to
stabilize more metrics.
### Added
- fs: add `File::create_new` ([#6573])
- io: add `copy_bidirectional_with_sizes` ([#6500])
- io: implement `AsyncBufRead` for `Join` ([#6449])
- net: add Apple visionOS support ([#6465])
- net: implement `Clone` for `NamedPipeInfo` ([#6586])
- net: support QNX OS ([#6421])
- sync: add `Notify::notify_last` ([#6520])
- sync: add `mpsc::Receiver::{capacity,max_capacity}` ([#6511])
- sync: add `split` method to the semaphore permit ([#6472], [#6478])
- task: add `tokio::task::join_set::Builder::spawn_blocking` ([#6578])
- wasm: support rt-multi-thread with wasm32-wasi-preview1-threads ([#6510])
### Changed
- macros: make `#[tokio::test]` append `#[test]` at the end of the attribute list ([#6497])
- metrics: fix `blocking_threads` count ([#6551])
- metrics: stabilize `RuntimeMetrics::worker_count` ([#6556])
- runtime: move task out of the `lifo_slot` in `block_in_place` ([#6596])
- runtime: panic if `global_queue_interval` is zero ([#6445])
- sync: always drop message in destructor for oneshot receiver ([#6558])
- sync: instrument `Semaphore` for task dumps ([#6499])
- sync: use FIFO ordering when waking batches of wakers ([#6521])
- task: make `LocalKey::get` work with Clone types ([#6433])
- tests: update nix and mio-aio dev-dependencies ([#6552])
- time: clean up implementation ([#6517])
- time: lazily init timers on first poll ([#6512])
- time: remove the `true_when` field in `TimerShared` ([#6563])
- time: use sharding for timer implementation ([#6534])
### Fixed
- taskdump: allow building taskdump docs on non-unix machines ([#6564])
- time: check for overflow in `Interval::poll_tick` ([#6487])
- sync: fix incorrect `is_empty` on mpsc block boundaries ([#6603])
### Documented
- fs: rewrite file system docs ([#6467])
- io: fix `stdin` documentation ([#6581])
- io: fix obsolete reference in `ReadHalf::unsplit()` documentation ([#6498])
- macros: render more comprehensible documentation for `select!` ([#6468])
- net: add missing types to module docs ([#6482])
- net: fix misleading `NamedPipeServer` example ([#6590])
- sync: add examples for `SemaphorePermit`, `OwnedSemaphorePermit` ([#6477])
- sync: document that `Barrier::wait` is not cancel safe ([#6494])
- sync: explain relation between `watch::Sender::{subscribe,closed}` ([#6490])
- task: clarify that you can't abort `spawn_blocking` tasks ([#6571])
- task: fix a typo in doc of `LocalSet::run_until` ([#6599])
- time: fix test-util requirement for pause and resume in docs ([#6503])
[#6421]: https://github.com/tokio-rs/tokio/pull/6421
[#6433]: https://github.com/tokio-rs/tokio/pull/6433
[#6445]: https://github.com/tokio-rs/tokio/pull/6445
[#6449]: https://github.com/tokio-rs/tokio/pull/6449
[#6465]: https://github.com/tokio-rs/tokio/pull/6465
[#6467]: https://github.com/tokio-rs/tokio/pull/6467
[#6468]: https://github.com/tokio-rs/tokio/pull/6468
[#6472]: https://github.com/tokio-rs/tokio/pull/6472
[#6477]: https://github.com/tokio-rs/tokio/pull/6477
[#6478]: https://github.com/tokio-rs/tokio/pull/6478
[#6482]: https://github.com/tokio-rs/tokio/pull/6482
[#6487]: https://github.com/tokio-rs/tokio/pull/6487
[#6490]: https://github.com/tokio-rs/tokio/pull/6490
[#6494]: https://github.com/tokio-rs/tokio/pull/6494
[#6497]: https://github.com/tokio-rs/tokio/pull/6497
[#6498]: https://github.com/tokio-rs/tokio/pull/6498
[#6499]: https://github.com/tokio-rs/tokio/pull/6499
[#6500]: https://github.com/tokio-rs/tokio/pull/6500
[#6503]: https://github.com/tokio-rs/tokio/pull/6503
[#6510]: https://github.com/tokio-rs/tokio/pull/6510
[#6511]: https://github.com/tokio-rs/tokio/pull/6511
[#6512]: https://github.com/tokio-rs/tokio/pull/6512
[#6517]: https://github.com/tokio-rs/tokio/pull/6517
[#6520]: https://github.com/tokio-rs/tokio/pull/6520
[#6521]: https://github.com/tokio-rs/tokio/pull/6521
[#6534]: https://github.com/tokio-rs/tokio/pull/6534
[#6551]: https://github.com/tokio-rs/tokio/pull/6551
[#6552]: https://github.com/tokio-rs/tokio/pull/6552
[#6556]: https://github.com/tokio-rs/tokio/pull/6556
[#6558]: https://github.com/tokio-rs/tokio/pull/6558
[#6563]: https://github.com/tokio-rs/tokio/pull/6563
[#6564]: https://github.com/tokio-rs/tokio/pull/6564
[#6571]: https://github.com/tokio-rs/tokio/pull/6571
[#6573]: https://github.com/tokio-rs/tokio/pull/6573
[#6578]: https://github.com/tokio-rs/tokio/pull/6578
[#6581]: https://github.com/tokio-rs/tokio/pull/6581
[#6586]: https://github.com/tokio-rs/tokio/pull/6586
[#6590]: https://github.com/tokio-rs/tokio/pull/6590
[#6596]: https://github.com/tokio-rs/tokio/pull/6596
[#6599]: https://github.com/tokio-rs/tokio/pull/6599
[#6603]: https://github.com/tokio-rs/tokio/pull/6603
# 1.37.0 (March 28th, 2024)
### Added
- fs: add `set_max_buf_size` to `tokio::fs::File` ([#6411])
- io: add `try_new` and `try_with_interest` to `AsyncFd` ([#6345])
- sync: add `forget_permits` method to semaphore ([#6331])
- sync: add `is_closed`, `is_empty`, and `len` to mpsc receivers ([#6348])
- sync: add a `rwlock()` method to owned `RwLock` guards ([#6418])
- sync: expose strong and weak counts of mpsc sender handles ([#6405])
- sync: implement `Clone` for `watch::Sender` ([#6388])
- task: add `TaskLocalFuture::take_value` ([#6340])
- task: implement `FromIterator` for `JoinSet` ([#6300])
### Changed
- io: make `io::split` use a mutex instead of a spinlock ([#6403])
### Fixed
- docs: fix docsrs build without net feature ([#6360])
- macros: allow select with only else branch ([#6339])
- runtime: fix leaking registration entries when os registration fails ([#6329])
### Documented
- io: document cancel safety of `AsyncBufReadExt::fill_buf` ([#6431])
- io: document cancel safety of `AsyncReadExt`'s primitive read functions ([#6337])
- runtime: add doc link from `Runtime` to `#[tokio::main]` ([#6366])
- runtime: make the `enter` example deterministic ([#6351])
- sync: add Semaphore example for limiting the number of outgoing requests ([#6419])
- sync: fix missing period in broadcast docs ([#6377])
- sync: mark `mpsc::Sender::downgrade` with `#[must_use]` ([#6326])
- sync: reorder `const_new` before `new_with` ([#6392])
- sync: update watch channel docs ([#6395])
- task: fix documentation links ([#6336])
### Changed (unstable)
- runtime: include task `Id` in taskdumps ([#6328])
- runtime: panic if `unhandled_panic` is enabled when not supported ([#6410])
[#6300]: https://github.com/tokio-rs/tokio/pull/6300
[#6326]: https://github.com/tokio-rs/tokio/pull/6326
[#6328]: https://github.com/tokio-rs/tokio/pull/6328
[#6329]: https://github.com/tokio-rs/tokio/pull/6329
[#6331]: https://github.com/tokio-rs/tokio/pull/6331
[#6336]: https://github.com/tokio-rs/tokio/pull/6336
[#6337]: https://github.com/tokio-rs/tokio/pull/6337
[#6339]: https://github.com/tokio-rs/tokio/pull/6339
[#6340]: https://github.com/tokio-rs/tokio/pull/6340
[#6345]: https://github.com/tokio-rs/tokio/pull/6345
[#6348]: https://github.com/tokio-rs/tokio/pull/6348
[#6351]: https://github.com/tokio-rs/tokio/pull/6351
[#6360]: https://github.com/tokio-rs/tokio/pull/6360
[#6366]: https://github.com/tokio-rs/tokio/pull/6366
[#6377]: https://github.com/tokio-rs/tokio/pull/6377
[#6388]: https://github.com/tokio-rs/tokio/pull/6388
[#6392]: https://github.com/tokio-rs/tokio/pull/6392
[#6395]: https://github.com/tokio-rs/tokio/pull/6395
[#6403]: https://github.com/tokio-rs/tokio/pull/6403
[#6405]: https://github.com/tokio-rs/tokio/pull/6405
[#6410]: https://github.com/tokio-rs/tokio/pull/6410
[#6411]: https://github.com/tokio-rs/tokio/pull/6411
[#6418]: https://github.com/tokio-rs/tokio/pull/6418
[#6419]: https://github.com/tokio-rs/tokio/pull/6419
[#6431]: https://github.com/tokio-rs/tokio/pull/6431
# 1.36.0 (February 2nd, 2024)
### Added
- io: add `tokio::io::Join` ([#6220])
- io: implement `AsyncWrite` for `Empty` ([#6235])
- net: add support for anonymous unix pipes ([#6127])
- net: add `UnixSocket` ([#6290])
- net: expose keepalive option on `TcpSocket` ([#6311])
- sync: add `{Receiver,UnboundedReceiver}::poll_recv_many` ([#6236])
- sync: add `Sender::{try_,}reserve_many` ([#6205])
- sync: add `watch::Receiver::mark_unchanged` ([#6252])
- task: add `JoinSet::try_join_next` ([#6280])
### Changed
- io: make `copy` cooperative ([#6265])
- io: make `repeat` and `sink` cooperative ([#6254])
- io: simplify check for empty slice ([#6293])
- process: use pidfd on Linux when available ([#6152])
- sync: use AtomicBool in broadcast channel future ([#6298])
### Documented
- io: clarify `clear_ready` docs ([#6304])
- net: document that `*Fd` traits on `TcpSocket` are unix-only ([#6294])
- sync: document FIFO behavior of `tokio::sync::Mutex` ([#6279])
- chore: typographic improvements ([#6262])
- runtime: remove obsolete comment ([#6303])
- task: fix typo ([#6261])
[#6220]: https://github.com/tokio-rs/tokio/pull/6220
[#6235]: https://github.com/tokio-rs/tokio/pull/6235
[#6127]: https://github.com/tokio-rs/tokio/pull/6127
[#6290]: https://github.com/tokio-rs/tokio/pull/6290
[#6311]: https://github.com/tokio-rs/tokio/pull/6311
[#6236]: https://github.com/tokio-rs/tokio/pull/6236
[#6205]: https://github.com/tokio-rs/tokio/pull/6205
[#6252]: https://github.com/tokio-rs/tokio/pull/6252
[#6280]: https://github.com/tokio-rs/tokio/pull/6280
[#6265]: https://github.com/tokio-rs/tokio/pull/6265
[#6254]: https://github.com/tokio-rs/tokio/pull/6254
[#6293]: https://github.com/tokio-rs/tokio/pull/6293
[#6238]: https://github.com/tokio-rs/tokio/pull/6238
[#6152]: https://github.com/tokio-rs/tokio/pull/6152
[#6298]: https://github.com/tokio-rs/tokio/pull/6298
[#6262]: https://github.com/tokio-rs/tokio/pull/6262
[#6303]: https://github.com/tokio-rs/tokio/pull/6303
[#6261]: https://github.com/tokio-rs/tokio/pull/6261
[#6304]: https://github.com/tokio-rs/tokio/pull/6304
[#6294]: https://github.com/tokio-rs/tokio/pull/6294
[#6279]: https://github.com/tokio-rs/tokio/pull/6279
# 1.35.1 (December 19, 2023)
This is a forward part of a change that was backported to 1.25.3.
### Fixed
- io: add budgeting to `tokio::runtime::io::registration::async_io` ([#6221])
[#6221]: https://github.com/tokio-rs/tokio/pull/6221
# 1.35.0 (December 8th, 2023)
### Added
- net: add Apple watchOS support ([#6176])
### Changed
- io: drop the `Sized` requirements from `AsyncReadExt.read_buf` ([#6169])
- runtime: make `Runtime` unwind safe ([#6189])
- runtime: reduce the lock contention in task spawn ([#6001])
- tokio: update nix dependency to 0.27.1 ([#6190])
### Fixed
- chore: make `--cfg docsrs` work without net feature ([#6166])
- chore: use relaxed load for `unsync_load` on miri ([#6179])
- runtime: handle missing context on wake ([#6148])
- taskdump: fix taskdump cargo config example ([#6150])
- taskdump: skip notified tasks during taskdumps ([#6194])
- tracing: avoid creating resource spans with current parent, use a None parent instead ([#6107])
- tracing: make task span explicit root ([#6158])
### Documented
- io: flush in `AsyncWriteExt` examples ([#6149])
- runtime: document fairness guarantees and current behavior ([#6145])
- task: document cancel safety of `LocalSet::run_until` ([#6147])
[#6001]: https://github.com/tokio-rs/tokio/pull/6001
[#6107]: https://github.com/tokio-rs/tokio/pull/6107
[#6144]: https://github.com/tokio-rs/tokio/pull/6144
[#6145]: https://github.com/tokio-rs/tokio/pull/6145
[#6147]: https://github.com/tokio-rs/tokio/pull/6147
[#6148]: https://github.com/tokio-rs/tokio/pull/6148
[#6149]: https://github.com/tokio-rs/tokio/pull/6149
[#6150]: https://github.com/tokio-rs/tokio/pull/6150
[#6158]: https://github.com/tokio-rs/tokio/pull/6158
[#6166]: https://github.com/tokio-rs/tokio/pull/6166
[#6169]: https://github.com/tokio-rs/tokio/pull/6169
[#6176]: https://github.com/tokio-rs/tokio/pull/6176
[#6179]: https://github.com/tokio-rs/tokio/pull/6179
[#6189]: https://github.com/tokio-rs/tokio/pull/6189
[#6190]: https://github.com/tokio-rs/tokio/pull/6190
[#6194]: https://github.com/tokio-rs/tokio/pull/6194
# 1.34.0 (November 19th, 2023)
### Fixed
- io: allow `clear_readiness` after io driver shutdown ([#6067])
- io: fix integer overflow in `take` ([#6080])
- io: fix I/O resource hang ([#6134])
- sync: fix `broadcast::channel` link ([#6100])
### Changed
- macros: use `::core` qualified imports instead of `::std` inside `tokio::test` macro ([#5973])
### Added
- fs: update cfg attr in `fs::read_dir` to include `aix` ([#6075])
- sync: add `mpsc::Receiver::recv_many` ([#6010])
- tokio: added vita target support ([#6094])
[#5973]: https://github.com/tokio-rs/tokio/pull/5973
[#6067]: https://github.com/tokio-rs/tokio/pull/6067
[#6080]: https://github.com/tokio-rs/tokio/pull/6080
[#6134]: https://github.com/tokio-rs/tokio/pull/6134
[#6100]: https://github.com/tokio-rs/tokio/pull/6100
[#6075]: https://github.com/tokio-rs/tokio/pull/6075
[#6010]: https://github.com/tokio-rs/tokio/pull/6010
[#6094]: https://github.com/tokio-rs/tokio/pull/6094
# 1.33.0 (October 9, 2023)
### Fixed
- io: mark `Interest::add` with `#[must_use]` ([#6037])
- runtime: fix cache line size for RISC-V ([#5994])
- sync: prevent lock poisoning in `watch::Receiver::wait_for` ([#6021])
- task: fix `spawn_local` source location ([#5984])
### Changed
- sync: use Acquire/Release orderings instead of SeqCst in `watch` ([#6018])
### Added
- fs: add vectored writes to `tokio::fs::File` ([#5958])
- io: add `Interest::remove` method ([#5906])
- io: add vectored writes to `DuplexStream` ([#5985])
- net: add Apple tvOS support ([#6045])
- sync: add `?Sized` bound to `{MutexGuard,OwnedMutexGuard}::map` ([#5997])
- sync: add `watch::Receiver::mark_unseen` ([#5962], [#6014], [#6017])
- sync: add `watch::Sender::new` ([#5998])
- sync: add const fn `OnceCell::from_value` ([#5903])
### Removed
- remove unused `stats` feature ([#5952])
### Documented
- add missing backticks in code examples ([#5938], [#6056])
- fix typos ([#5988], [#6030])
- process: document that `Child::wait` is cancel safe ([#5977])
- sync: add examples for `Semaphore` ([#5939], [#5956], [#5978], [#6031], [#6032], [#6050])
- sync: document that `broadcast` capacity is a lower bound ([#6042])
- sync: document that `const_new` is not instrumented ([#6002])
- sync: improve cancel-safety documentation for `mpsc::Sender::send` ([#5947])
- sync: improve docs for `watch` channel ([#5954])
- taskdump: render taskdump documentation on docs.rs ([#5972])
### Unstable
- taskdump: fix potential deadlock ([#6036])
[#5903]: https://github.com/tokio-rs/tokio/pull/5903
[#5906]: https://github.com/tokio-rs/tokio/pull/5906
[#5938]: https://github.com/tokio-rs/tokio/pull/5938
[#5939]: https://github.com/tokio-rs/tokio/pull/5939
[#5947]: https://github.com/tokio-rs/tokio/pull/5947
[#5952]: https://github.com/tokio-rs/tokio/pull/5952
[#5954]: https://github.com/tokio-rs/tokio/pull/5954
[#5956]: https://github.com/tokio-rs/tokio/pull/5956
[#5958]: https://github.com/tokio-rs/tokio/pull/5958
[#5960]: https://github.com/tokio-rs/tokio/pull/5960
[#5962]: https://github.com/tokio-rs/tokio/pull/5962
[#5971]: https://github.com/tokio-rs/tokio/pull/5971
[#5972]: https://github.com/tokio-rs/tokio/pull/5972
[#5977]: https://github.com/tokio-rs/tokio/pull/5977
[#5978]: https://github.com/tokio-rs/tokio/pull/5978
[#5984]: https://github.com/tokio-rs/tokio/pull/5984
[#5985]: https://github.com/tokio-rs/tokio/pull/5985
[#5988]: https://github.com/tokio-rs/tokio/pull/5988
[#5994]: https://github.com/tokio-rs/tokio/pull/5994
[#5997]: https://github.com/tokio-rs/tokio/pull/5997
[#5998]: https://github.com/tokio-rs/tokio/pull/5998
[#6002]: https://github.com/tokio-rs/tokio/pull/6002
[#6014]: https://github.com/tokio-rs/tokio/pull/6014
[#6017]: https://github.com/tokio-rs/tokio/pull/6017
[#6018]: https://github.com/tokio-rs/tokio/pull/6018
[#6021]: https://github.com/tokio-rs/tokio/pull/6021
[#6030]: https://github.com/tokio-rs/tokio/pull/6030
[#6031]: https://github.com/tokio-rs/tokio/pull/6031
[#6032]: https://github.com/tokio-rs/tokio/pull/6032
[#6036]: https://github.com/tokio-rs/tokio/pull/6036
[#6037]: https://github.com/tokio-rs/tokio/pull/6037
[#6042]: https://github.com/tokio-rs/tokio/pull/6042
[#6045]: https://github.com/tokio-rs/tokio/pull/6045
[#6050]: https://github.com/tokio-rs/tokio/pull/6050
[#6056]: https://github.com/tokio-rs/tokio/pull/6056
[#6058]: https://github.com/tokio-rs/tokio/pull/6058
# 1.32.1 (December 19, 2023)
This is a forward part of a change that was backported to 1.25.3.
### Fixed
- io: add budgeting to `tokio::runtime::io::registration::async_io` ([#6221])
[#6221]: https://github.com/tokio-rs/tokio/pull/6221
# 1.32.0 (August 16, 2023)
### Fixed
- sync: fix potential quadratic behavior in `broadcast::Receiver` ([#5925])
### Added
- process: stabilize `Command::raw_arg` ([#5930])
- io: enable awaiting error readiness ([#5781])
### Unstable
- rt(alt): improve scalability of alt runtime as the number of cores grows ([#5935])
[#5925]: https://github.com/tokio-rs/tokio/pull/5925
[#5930]: https://github.com/tokio-rs/tokio/pull/5930
[#5781]: https://github.com/tokio-rs/tokio/pull/5781
[#5935]: https://github.com/tokio-rs/tokio/pull/5935
# 1.31.0 (August 10, 2023)
### Fixed
* io: delegate `WriteHalf::poll_write_vectored` ([#5914])
### Unstable
* rt(alt): fix memory leak in unstable next-gen scheduler prototype ([#5911])
* rt: expose mean task poll time metric ([#5927])
[#5914]: https://github.com/tokio-rs/tokio/pull/5914
[#5911]: https://github.com/tokio-rs/tokio/pull/5911
[#5927]: https://github.com/tokio-rs/tokio/pull/5927
# 1.30.0 (August 9, 2023)
This release bumps the MSRV of Tokio to 1.63. ([#5887])
### Changed
- tokio: reduce LLVM code generation ([#5859])
- io: support `--cfg mio_unsupported_force_poll_poll` flag ([#5881])
- sync: make `const_new` methods always available ([#5885])
- sync: avoid false sharing in mpsc channel ([#5829])
- rt: pop at least one task from inject queue ([#5908])
### Added
- sync: add `broadcast::Sender::new` ([#5824])
- net: implement `UCred` for espidf ([#5868])
- fs: add `File::options()` ([#5869])
- time: implement extra reset variants for `Interval` ([#5878])
- process: add `{ChildStd*}::into_owned_{fd, handle}` ([#5899])
### Removed
- tokio: removed unused `tokio_*` cfgs ([#5890])
- remove build script to speed up compilation ([#5887])
### Documented
- sync: mention lagging in docs for `broadcast::send` ([#5820])
- runtime: expand on sharing runtime docs ([#5858])
- io: use vec in example for `AsyncReadExt::read_exact` ([#5863])
- time: mark `Sleep` as `!Unpin` in docs ([#5916])
- process: fix `raw_arg` not showing up in docs ([#5865])
### Unstable
- rt: add runtime ID ([#5864])
- rt: initial implementation of new threaded runtime ([#5823])
[#5820]: https://github.com/tokio-rs/tokio/pull/5820
[#5823]: https://github.com/tokio-rs/tokio/pull/5823
[#5824]: https://github.com/tokio-rs/tokio/pull/5824
[#5829]: https://github.com/tokio-rs/tokio/pull/5829
[#5858]: https://github.com/tokio-rs/tokio/pull/5858
[#5859]: https://github.com/tokio-rs/tokio/pull/5859
[#5863]: https://github.com/tokio-rs/tokio/pull/5863
[#5864]: https://github.com/tokio-rs/tokio/pull/5864
[#5865]: https://github.com/tokio-rs/tokio/pull/5865
[#5868]: https://github.com/tokio-rs/tokio/pull/5868
[#5869]: https://github.com/tokio-rs/tokio/pull/5869
[#5878]: https://github.com/tokio-rs/tokio/pull/5878
[#5881]: https://github.com/tokio-rs/tokio/pull/5881
[#5885]: https://github.com/tokio-rs/tokio/pull/5885
[#5887]: https://github.com/tokio-rs/tokio/pull/5887
[#5890]: https://github.com/tokio-rs/tokio/pull/5890
[#5899]: https://github.com/tokio-rs/tokio/pull/5899
[#5908]: https://github.com/tokio-rs/tokio/pull/5908
[#5916]: https://github.com/tokio-rs/tokio/pull/5916
# 1.29.1 (June 29, 2023)
### Fixed
@ -267,6 +862,23 @@ This release bumps the MSRV of Tokio to 1.56. ([#5559])
[#5513]: https://github.com/tokio-rs/tokio/pull/5513
[#5517]: https://github.com/tokio-rs/tokio/pull/5517
# 1.25.3 (December 17th, 2023)
### Fixed
- io: add budgeting to `tokio::runtime::io::registration::async_io` ([#6221])
[#6221]: https://github.com/tokio-rs/tokio/pull/6221
# 1.25.2 (September 22, 2023)
Forward ports 1.20.6 changes.
### Changed
- io: use `memchr` from `libc` ([#5960])
[#5960]: https://github.com/tokio-rs/tokio/pull/5960
# 1.25.1 (May 28, 2023)
Forward ports 1.18.6 changes.
@ -613,6 +1225,16 @@ wasm32-wasi target is given unstable support for the `net` feature.
[#4956]: https://github.com/tokio-rs/tokio/pull/4956
[#4959]: https://github.com/tokio-rs/tokio/pull/4959
# 1.20.6 (September 22, 2023)
This is a backport of a change from 1.27.0.
### Changed
- io: use `memchr` from `libc` ([#5960])
[#5960]: https://github.com/tokio-rs/tokio/pull/5960
# 1.20.5 (May 28, 2023)
Forward ports 1.18.6 changes.
@ -1325,7 +1947,9 @@ Fixes a missed edge case from 1.8.1.
### Fixed
- runtime: drop canceled future on next poll (#3965)
- runtime: drop canceled future on next poll ([#3965])
[#3965]: https://github.com/tokio-rs/tokio/pull/3965
# 1.8.1 (July 6, 2021)
@ -1346,7 +1970,7 @@ Forward ports 1.5.1 fixes.
- net: add ready/try methods to `NamedPipe{Client,Server}` ([#3866], [#3899])
- sync: add `watch::Receiver::borrow_and_update` ([#3813])
- sync: implement `From<T>` for `OnceCell<T>` ([#3877])
- time: allow users to specify Interval behaviour when delayed ([#3721])
- time: allow users to specify Interval behavior when delayed ([#3721])
### Added (unstable)
@ -1360,7 +1984,7 @@ Forward ports 1.5.1 fixes.
- doc: document cancellation safety ([#3900])
- time: add wait alias to sleep ([#3897])
- time: document auto-advancing behaviour of runtime ([#3763])
- time: document auto-advancing behavior of runtime ([#3763])
[#3163]: https://github.com/tokio-rs/tokio/pull/3163
[#3721]: https://github.com/tokio-rs/tokio/pull/3721
@ -1607,7 +2231,7 @@ a kernel bug. ([#3803])
- doc: doc aliases for pre-1.0 function names ([#3523])
- io: fix typos ([#3541])
- io: note the EOF behaviour of `read_until` ([#3536])
- io: note the EOF behavior of `read_until` ([#3536])
- io: update `AsyncRead::poll_read` doc ([#3557])
- net: update `UdpSocket` splitting doc ([#3517])
- runtime: add link to `LocalSet` on `new_current_thread` ([#3508])

View File

@ -11,9 +11,9 @@
[package]
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
name = "tokio"
version = "1.29.1"
version = "1.39.2"
authors = ["Tokio Contributors <team@tokio.rs>"]
description = """
An event-driven, non-blocking I/O platform for writing asynchronous I/O
@ -34,17 +34,28 @@ categories = [
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"bytes::buf::buf_impl::Buf",
"bytes::buf::buf_mut::BufMut",
"tokio_macros::*",
]
[package.metadata.docs.rs]
all-features = true
rustc-args = [
"--cfg",
"tokio_unstable",
"--cfg",
"tokio_taskdump",
]
rustdoc-args = [
"--cfg",
"docsrs",
"--cfg",
"tokio_unstable",
"--cfg",
"tokio_taskdump",
]
[package.metadata.playground]
@ -58,23 +69,19 @@ version = "1.0.0"
optional = true
[dependencies.mio]
version = "0.8.6"
version = "1.0.1"
optional = true
default-features = false
[dependencies.num_cpus]
version = "1.8.0"
optional = true
[dependencies.parking_lot]
version = "0.12.0"
optional = true
[dependencies.pin-project-lite]
version = "0.2.7"
version = "0.2.11"
[dependencies.tokio-macros]
version = "~2.1.0"
version = "~2.4.0"
optional = true
[dev-dependencies.async-stream]
@ -93,9 +100,6 @@ version = "0.1"
[dev-dependencies.tokio-test]
version = "0.4.0"
[build-dependencies.autocfg]
version = "1.1"
[features]
default = []
fs = []
@ -140,10 +144,7 @@ process = [
"windows-sys/Win32_System_WindowsProgramming",
]
rt = []
rt-multi-thread = [
"num_cpus",
"rt",
]
rt-multi-thread = ["rt"]
signal = [
"libc",
"mio/os-poll",
@ -153,7 +154,6 @@ signal = [
"windows-sys/Win32_Foundation",
"windows-sys/Win32_System_Console",
]
stats = []
sync = []
test-util = [
"rt",
@ -162,32 +162,32 @@ test-util = [
]
time = []
[target."cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), not(target_os = \"wasi\")))".dev-dependencies.wasm-bindgen-test]
[target."cfg(all(target_family = \"wasm\", not(target_os = \"wasi\")))".dev-dependencies.wasm-bindgen-test]
version = "0.3.0"
[target."cfg(loom)".dev-dependencies.loom]
version = "0.5.2"
version = "0.7"
features = [
"futures",
"checkpoint",
]
[target."cfg(not(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\")))".dev-dependencies.rand]
[target."cfg(not(all(target_family = \"wasm\", target_os = \"unknown\")))".dev-dependencies.rand]
version = "0.8.0"
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dependencies.socket2]
version = "0.4.9"
[target."cfg(not(target_family = \"wasm\"))".dependencies.socket2]
version = "0.5.5"
features = ["all"]
optional = true
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dev-dependencies.socket2]
version = "0.4.9"
[target."cfg(not(target_family = \"wasm\"))".dev-dependencies.socket2]
version = "0.5.5"
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dev-dependencies.tempfile]
[target."cfg(not(target_family = \"wasm\"))".dev-dependencies.tempfile]
version = "3.1.0"
[target."cfg(target_os = \"freebsd\")".dev-dependencies.mio-aio]
version = "0.7.0"
version = "0.9.0"
features = ["tokio"]
[target."cfg(tokio_taskdump)".dependencies.backtrace]
@ -200,7 +200,7 @@ optional = true
default-features = false
[target."cfg(unix)".dependencies.libc]
version = "0.2.145"
version = "0.2.149"
optional = true
[target."cfg(unix)".dependencies.signal-hook-registry]
@ -208,22 +208,23 @@ version = "1.1.1"
optional = true
[target."cfg(unix)".dev-dependencies.libc]
version = "0.2.145"
version = "0.2.149"
[target."cfg(unix)".dev-dependencies.nix]
version = "0.26"
version = "0.29.0"
features = [
"aio",
"fs",
"socket",
]
default-features = false
[target."cfg(windows)".dependencies.windows-sys]
version = "0.48"
version = "0.52"
optional = true
[target."cfg(windows)".dev-dependencies.windows-sys]
version = "0.48"
version = "0.52"
features = [
"Win32_Foundation",
"Win32_Security_Authorization",

View File

@ -1,25 +1,21 @@
Copyright (c) 2023 Tokio Contributors
MIT License
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
Copyright (c) Tokio Contributors
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.29.1", features = ["full"] }
tokio = { version = "1.39.2", features = ["full"] }
```
Then, on your main.rs:
@ -132,6 +132,8 @@ project.
In addition to the crates in this repository, the Tokio project also maintains
several other libraries, including:
* [`axum`]: A web application framework that focuses on ergonomics and modularity.
* [`hyper`]: A fast and correct HTTP/1.1 and HTTP/2 implementation for Rust.
* [`tonic`]: A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility.
@ -142,21 +144,18 @@ several other libraries, including:
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
`tokio`.
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers `tokio`.
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
* [`loom`]: A testing tool for concurrent Rust code
* [`loom`]: A testing tool for concurrent Rust code.
[`axum`]: https://github.com/tokio-rs/axum
[`warp`]: https://github.com/seanmonstar/warp
[`hyper`]: https://github.com/hyperium/hyper
[`tonic`]: https://github.com/hyperium/tonic
[`tower`]: https://github.com/tower-rs/tower
[`loom`]: https://github.com/tokio-rs/loom
[`rdbc`]: https://github.com/tokio-rs/rdbc
[`tracing`]: https://github.com/tokio-rs/tracing
[`mio`]: https://github.com/tokio-rs/mio
[`bytes`]: https://github.com/tokio-rs/bytes
@ -187,12 +186,14 @@ When updating this, also update:
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
least** 6 months. When increasing the MSRV, the new Rust version must have been
released at least six months ago. The current MSRV is 1.56.0.
released at least six months ago. The current MSRV is 1.70.
Note that the MSRV is not increased automatically, and only as part of a minor
release. The MSRV history for past minor releases can be found below:
* 1.27 to now - Rust 1.56
* 1.39 to now - Rust 1.70
* 1.30 to 1.38 - Rust 1.63
* 1.27 to 1.29 - Rust 1.56
* 1.17 to 1.26 - Rust 1.49
* 1.15 to 1.16 - Rust 1.46
* 1.0 to 1.14 - Rust 1.45
@ -215,21 +216,29 @@ warrants a patch release with a fix for the bug, it will be backported and
released as a new patch release for each LTS minor version. Our current LTS
releases are:
* `1.18.x` - LTS release until June 2023. (MSRV 1.49)
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
* `1.32.x` - LTS release until September 2024. (MSRV 1.63)
* `1.36.x` - LTS release until March 2025. (MSRV 1.63)
* `1.38.x` - LTS release until July 2025. (MSRV 1.63)
Each LTS release will continue to receive backported fixes for at least a year.
If you wish to use a fixed minor release in your project, we recommend that you
use an LTS release.
To use a fixed minor version, you can specify the version with a tilde. For
example, to specify that you wish to use the newest `1.18.x` patch release, you
example, to specify that you wish to use the newest `1.32.x` patch release, you
can use the following dependency specification:
```text
tokio = { version = "~1.18", features = [...] }
tokio = { version = "~1.32", features = [...] }
```
### Previous LTS releases
* `1.8.x` - LTS release until February 2022.
* `1.14.x` - LTS release until June 2022.
* `1.18.x` - LTS release until June 2023.
* `1.20.x` - LTS release until September 2023.
* `1.25.x` - LTS release until March 2024.
## License
This project is licensed under the [MIT license].

View File

@ -1,192 +0,0 @@
use autocfg::AutoCfg;
const CONST_THREAD_LOCAL_PROBE: &str = r#"
{
thread_local! {
static MY_PROBE: usize = const { 10 };
}
MY_PROBE.with(|val| *val)
}
"#;
const CONST_MUTEX_NEW_PROBE: &str = r#"
{
static MY_MUTEX: ::std::sync::Mutex<i32> = ::std::sync::Mutex::new(1);
*MY_MUTEX.lock().unwrap()
}
"#;
const AS_FD_PROBE: &str = r#"
{
#[allow(unused_imports)]
#[cfg(unix)]
use std::os::unix::prelude::AsFd as _;
#[allow(unused_imports)]
#[cfg(windows)]
use std::os::windows::prelude::AsSocket as _;
#[allow(unused_imports)]
#[cfg(target_os = "wasi")]
use std::os::wasi::prelude::AsFd as _;
}
"#;
const TARGET_HAS_ATOMIC_PROBE: &str = r#"
{
#[cfg(target_has_atomic = "ptr")]
let _ = ();
}
"#;
const TARGET_ATOMIC_U64_PROBE: &str = r#"
{
#[allow(unused_imports)]
use std::sync::atomic::AtomicU64 as _;
}
"#;
fn main() {
let mut enable_const_thread_local = false;
let mut enable_target_has_atomic = false;
let mut enable_const_mutex_new = false;
let mut enable_as_fd = false;
let mut target_needs_atomic_u64_fallback = false;
match AutoCfg::new() {
Ok(ac) => {
// These checks prefer to call only `probe_rustc_version` if that is
// enough to determine whether the feature is supported. This is
// because the `probe_expression` call involves a call to rustc,
// which the `probe_rustc_version` call avoids.
// Const-initialized thread locals were stabilized in 1.59.
if ac.probe_rustc_version(1, 60) {
enable_const_thread_local = true;
} else if ac.probe_rustc_version(1, 59) {
// This compiler claims to be 1.59, but there are some nightly
// compilers that claim to be 1.59 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2021-12-06.
if ac.probe_expression(CONST_THREAD_LOCAL_PROBE) {
enable_const_thread_local = true;
}
}
// The `target_has_atomic` cfg was stabilized in 1.60.
if ac.probe_rustc_version(1, 61) {
enable_target_has_atomic = true;
} else if ac.probe_rustc_version(1, 60) {
// This compiler claims to be 1.60, but there are some nightly
// compilers that claim to be 1.60 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2022-02-11.
if ac.probe_expression(TARGET_HAS_ATOMIC_PROBE) {
enable_target_has_atomic = true;
}
}
// If we can't tell using `target_has_atomic`, tell if the target
// has `AtomicU64` by trying to use it.
if !enable_target_has_atomic && !ac.probe_expression(TARGET_ATOMIC_U64_PROBE) {
target_needs_atomic_u64_fallback = true;
}
// The `Mutex::new` method was made const in 1.63.
if ac.probe_rustc_version(1, 64) {
enable_const_mutex_new = true;
} else if ac.probe_rustc_version(1, 63) {
// This compiler claims to be 1.63, but there are some nightly
// compilers that claim to be 1.63 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2022-06-20.
if ac.probe_expression(CONST_MUTEX_NEW_PROBE) {
enable_const_mutex_new = true;
}
}
// The `AsFd` family of traits were made stable in 1.63.
if ac.probe_rustc_version(1, 64) {
enable_as_fd = true;
} else if ac.probe_rustc_version(1, 63) {
// This compiler claims to be 1.63, but there are some nightly
// compilers that claim to be 1.63 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2022-06-16.
if ac.probe_expression(AS_FD_PROBE) {
enable_as_fd = true;
}
}
}
Err(e) => {
// If we couldn't detect the compiler version and features, just
// print a warning. This isn't a fatal error: we can still build
// Tokio, we just can't enable cfgs automatically.
println!(
"cargo:warning=tokio: failed to detect compiler features: {}",
e
);
}
}
if !enable_const_thread_local {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_const_thread_local"
autocfg::emit("tokio_no_const_thread_local")
}
if !enable_target_has_atomic {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_target_has_atomic"
autocfg::emit("tokio_no_target_has_atomic")
}
if !enable_const_mutex_new {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_const_mutex_new"
autocfg::emit("tokio_no_const_mutex_new")
}
if !enable_as_fd {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_as_fd"
autocfg::emit("tokio_no_as_fd");
}
if target_needs_atomic_u64_fallback {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_atomic_u64"
autocfg::emit("tokio_no_atomic_u64")
}
let target = ::std::env::var("TARGET").unwrap_or_default();
// We emit cfgs instead of using `target_family = "wasm"` that requires Rust 1.54.
// Note that these cfgs are unavailable in `Cargo.toml`.
if target.starts_with("wasm") {
autocfg::emit("tokio_wasm");
if target.contains("wasi") {
autocfg::emit("tokio_wasi");
} else {
autocfg::emit("tokio_wasm_not_wasi");
}
}
}

View File

@ -1,11 +0,0 @@
# This config file is for the `cargo-check-external-types` tool that is run in CI.
# The following are types that are allowed to be exposed in Tokio's public API.
# The standard library is allowed by default.
allowed_external_types = [
"bytes::buf::buf_impl::Buf",
"bytes::buf::buf_mut::BufMut",
"tokio_macros::*",
]

View File

@ -20,4 +20,28 @@
#[derive(Debug)]
pub enum NotDefinedHere {}
#[cfg(feature = "net")]
impl mio::event::Source for NotDefinedHere {
fn register(
&mut self,
registry: &mio::Registry,
token: mio::Token,
interests: mio::Interest,
) -> std::io::Result<()> {
Ok(())
}
fn reregister(
&mut self,
registry: &mio::Registry,
token: mio::Token,
interests: mio::Interest,
) -> std::io::Result<()> {
Ok(())
}
fn deregister(&mut self, registry: &mio::Registry) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(any(feature = "net", feature = "fs"))]
pub mod os;

View File

@ -1,64 +1,67 @@
//! See [std::os](https://doc.rust-lang.org/std/os/index.html).
//! See [`std::os`](https://doc.rust-lang.org/std/os/index.html).
/// Platform-specific extensions to `std` for Windows.
///
/// See [std::os::windows](https://doc.rust-lang.org/std/os/windows/index.html).
/// See [`std::os::windows`](https://doc.rust-lang.org/std/os/windows/index.html).
pub mod windows {
/// Windows-specific extensions to general I/O primitives.
///
/// See [std::os::windows::io](https://doc.rust-lang.org/std/os/windows/io/index.html).
/// See [`std::os::windows::io`](https://doc.rust-lang.org/std/os/windows/io/index.html).
pub mod io {
/// See [std::os::windows::io::RawHandle](https://doc.rust-lang.org/std/os/windows/io/type.RawHandle.html)
/// See [`std::os::windows::io::RawHandle`](https://doc.rust-lang.org/std/os/windows/io/type.RawHandle.html)
pub type RawHandle = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
/// See [`std::os::windows::io::OwnedHandle`](https://doc.rust-lang.org/std/os/windows/io/struct.OwnedHandle.html)
pub type OwnedHandle = crate::doc::NotDefinedHere;
/// See [`std::os::windows::io::AsRawHandle`](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
pub trait AsRawHandle {
/// See [std::os::windows::io::AsRawHandle::as_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
/// See [`std::os::windows::io::AsRawHandle::as_raw_handle`](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
fn as_raw_handle(&self) -> RawHandle;
}
/// See [std::os::windows::io::FromRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html)
/// See [`std::os::windows::io::FromRawHandle`](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html)
pub trait FromRawHandle {
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
/// See [`std::os::windows::io::FromRawHandle::from_raw_handle`](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
}
/// See [std::os::windows::io::RawSocket](https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html)
/// See [`std::os::windows::io::RawSocket`](https://doc.rust-lang.org/std/os/windows/io/type.RawSocket.html)
pub type RawSocket = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html)
/// See [`std::os::windows::io::AsRawSocket`](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html)
pub trait AsRawSocket {
/// See [std::os::windows::io::AsRawSocket::as_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html#tymethod.as_raw_socket)
/// See [`std::os::windows::io::AsRawSocket::as_raw_socket`](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawSocket.html#tymethod.as_raw_socket)
fn as_raw_socket(&self) -> RawSocket;
}
/// See [std::os::windows::io::FromRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html)
/// See [`std::os::windows::io::FromRawSocket`](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html)
pub trait FromRawSocket {
/// See [std::os::windows::io::FromRawSocket::from_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket)
/// See [`std::os::windows::io::FromRawSocket::from_raw_socket`](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket)
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
}
/// See [std::os::windows::io::IntoRawSocket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html)
/// See [`std::os::windows::io::IntoRawSocket`](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html)
pub trait IntoRawSocket {
/// See [std::os::windows::io::IntoRawSocket::into_raw_socket](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html#tymethod.into_raw_socket)
/// See [`std::os::windows::io::IntoRawSocket::into_raw_socket`](https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawSocket.html#tymethod.into_raw_socket)
fn into_raw_socket(self) -> RawSocket;
}
/// See [std::os::windows::io::BorrowedHandle](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedHandle.html)
/// See [`std::os::windows::io::BorrowedHandle`](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedHandle.html)
pub type BorrowedHandle<'handle> = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html)
/// See [`std::os::windows::io::AsHandle`](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html)
pub trait AsHandle {
/// See [std::os::windows::io::AsHandle::as_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html#tymethod.as_handle)
/// See [`std::os::windows::io::AsHandle::as_handle`](https://doc.rust-lang.org/std/os/windows/io/trait.AsHandle.html#tymethod.as_handle)
fn as_handle(&self) -> BorrowedHandle<'_>;
}
/// See [std::os::windows::io::BorrowedSocket](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedSocket.html)
/// See [`std::os::windows::io::BorrowedSocket`](https://doc.rust-lang.org/std/os/windows/io/struct.BorrowedSocket.html)
pub type BorrowedSocket<'socket> = crate::doc::NotDefinedHere;
/// See [std::os::windows::io::AsSocket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html)
/// See [`std::os::windows::io::AsSocket`](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html)
pub trait AsSocket {
/// See [std::os::windows::io::AsSocket::as_socket](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html#tymethod.as_socket)
/// See [`std::os::windows::io::AsSocket::as_socket`](https://doc.rust-lang.org/std/os/windows/io/trait.AsSocket.html#tymethod.as_socket)
fn as_socket(&self) -> BorrowedSocket<'_>;
}
}

View File

@ -6,9 +6,7 @@ use std::path::{Path, PathBuf};
/// Returns the canonical, absolute form of a path with all intermediate
/// components normalized and symbolic links resolved.
///
/// This is an async version of [`std::fs::canonicalize`][std]
///
/// [std]: std::fs::canonicalize
/// This is an async version of [`std::fs::canonicalize`].
///
/// # Platform-specific behavior
///

View File

@ -5,9 +5,7 @@ use std::path::Path;
/// of the original file to the destination file.
/// This function will overwrite the contents of to.
///
/// This is the async equivalent of [`std::fs::copy`][std].
///
/// [std]: fn@std::fs::copy
/// This is the async equivalent of [`std::fs::copy`].
///
/// # Examples
///

View File

@ -5,9 +5,7 @@ use std::path::Path;
/// Creates a new, empty directory at the provided path.
///
/// This is an async version of [`std::fs::create_dir`][std]
///
/// [std]: std::fs::create_dir
/// This is an async version of [`std::fs::create_dir`].
///
/// # Platform-specific behavior
///

View File

@ -6,9 +6,7 @@ use std::path::Path;
/// Recursively creates a directory and all of its parent components if they
/// are missing.
///
/// This is an async version of [`std::fs::create_dir_all`][std]
///
/// [std]: std::fs::create_dir_all
/// This is an async version of [`std::fs::create_dir_all`].
///
/// # Platform-specific behavior
///

View File

@ -7,8 +7,6 @@ use std::path::Path;
///
/// This is a specialized version of [`std::fs::DirBuilder`] for usage on
/// the Tokio runtime.
///
/// [std::fs::DirBuilder]: std::fs::DirBuilder
#[derive(Debug, Default)]
pub struct DirBuilder {
/// Indicates whether to create parent directories if they are missing.
@ -23,9 +21,7 @@ impl DirBuilder {
/// Creates a new set of options with default mode/security settings for all
/// platforms and also non-recursive.
///
/// This is an async version of [`std::fs::DirBuilder::new`][std]
///
/// [std]: std::fs::DirBuilder::new
/// This is an async version of [`std::fs::DirBuilder::new`].
///
/// # Examples
///
@ -35,7 +31,7 @@ impl DirBuilder {
/// let builder = DirBuilder::new();
/// ```
pub fn new() -> Self {
Default::default()
DirBuilder::default()
}
/// Indicates whether to create directories recursively (including all parent directories).
@ -43,9 +39,7 @@ impl DirBuilder {
///
/// This option defaults to `false`.
///
/// This is an async version of [`std::fs::DirBuilder::recursive`][std]
///
/// [std]: std::fs::DirBuilder::recursive
/// This is an async version of [`std::fs::DirBuilder::recursive`].
///
/// # Examples
///
@ -65,9 +59,7 @@ impl DirBuilder {
/// It is considered an error if the directory already exists unless
/// recursive mode is enabled.
///
/// This is an async version of [`std::fs::DirBuilder::create`][std]
///
/// [std]: std::fs::DirBuilder::create
/// This is an async version of [`std::fs::DirBuilder::create`].
///
/// # Errors
///

View File

@ -2,9 +2,8 @@
//!
//! [`File`]: File
use self::State::*;
use crate::fs::asyncify;
use crate::io::blocking::Buf;
use crate::fs::{asyncify, OpenOptions};
use crate::io::blocking::{Buf, DEFAULT_MAX_BUF_SIZE};
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
use crate::sync::Mutex;
@ -17,7 +16,6 @@ use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
use std::task::Poll::*;
#[cfg(test)]
use super::mocks::JoinHandle;
@ -34,7 +32,7 @@ use std::fs::File as StdFile;
/// A reference to an open file on the filesystem.
///
/// This is a specialized version of [`std::fs::File`][std] for usage from the
/// This is a specialized version of [`std::fs::File`] for usage from the
/// Tokio runtime.
///
/// An instance of a `File` can be read and/or written depending on what options
@ -52,7 +50,6 @@ use std::fs::File as StdFile;
/// Reading and writing to a `File` is usually done using the convenience
/// methods found on the [`AsyncReadExt`] and [`AsyncWriteExt`] traits.
///
/// [std]: struct@std::fs::File
/// [`AsyncSeek`]: trait@crate::io::AsyncSeek
/// [`flush`]: fn@crate::io::AsyncWriteExt::flush
/// [`sync_all`]: fn@crate::fs::File::sync_all
@ -93,6 +90,7 @@ use std::fs::File as StdFile;
pub struct File {
std: Arc<StdFile>,
inner: Mutex<Inner>,
max_buf_size: usize,
}
struct Inner {
@ -124,13 +122,11 @@ impl File {
///
/// See [`OpenOptions`] for more details.
///
/// [`OpenOptions`]: super::OpenOptions
///
/// # Errors
///
/// This function will return an error if called from outside of the Tokio
/// runtime or if path does not already exist. Other errors may also be
/// returned according to OpenOptions::open.
/// returned according to `OpenOptions::open`.
///
/// # Examples
///
@ -167,8 +163,6 @@ impl File {
///
/// See [`OpenOptions`] for more details.
///
/// [`OpenOptions`]: super::OpenOptions
///
/// # Errors
///
/// Results in an error if called from outside of the Tokio runtime or if
@ -199,10 +193,78 @@ impl File {
Ok(File::from_std(std_file))
}
/// Converts a [`std::fs::File`][std] to a [`tokio::fs::File`][file].
/// Opens a file in read-write mode.
///
/// [std]: std::fs::File
/// [file]: File
/// This function will create a file if it does not exist, or return an error
/// if it does. This way, if the call succeeds, the file returned is guaranteed
/// to be new.
///
/// This option is useful because it is atomic. Otherwise between checking
/// whether a file exists and creating a new one, the file may have been
/// created by another process (a TOCTOU race condition / attack).
///
/// This can also be written using `File::options().read(true).write(true).create_new(true).open(...)`.
///
/// See [`OpenOptions`] for more details.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::File;
/// use tokio::io::AsyncWriteExt;
///
/// # async fn dox() -> std::io::Result<()> {
/// let mut file = File::create_new("foo.txt").await?;
/// file.write_all(b"hello, world!").await?;
/// # Ok(())
/// # }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn create_new<P: AsRef<Path>>(path: P) -> std::io::Result<File> {
Self::options()
.read(true)
.write(true)
.create_new(true)
.open(path)
.await
}
/// Returns a new [`OpenOptions`] object.
///
/// This function returns a new `OpenOptions` object that you can use to
/// open or create a file with specific options if `open()` or `create()`
/// are not appropriate.
///
/// It is equivalent to `OpenOptions::new()`, but allows you to write more
/// readable code. Instead of
/// `OpenOptions::new().append(true).open("example.log")`,
/// you can write `File::options().append(true).open("example.log")`. This
/// also avoids the need to import `OpenOptions`.
///
/// See the [`OpenOptions::new`] function for more details.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::File;
/// use tokio::io::AsyncWriteExt;
///
/// # async fn dox() -> std::io::Result<()> {
/// let mut f = File::options().append(true).open("example.log").await?;
/// f.write_all(b"new line\n").await?;
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn options() -> OpenOptions {
OpenOptions::new()
}
/// Converts a [`std::fs::File`] to a [`tokio::fs::File`](File).
///
/// # Examples
///
@ -220,6 +282,7 @@ impl File {
last_write_err: None,
pos: 0,
}),
max_buf_size: DEFAULT_MAX_BUF_SIZE,
}
}
@ -324,7 +387,7 @@ impl File {
inner.complete_inflight().await;
let mut buf = match inner.state {
Idle(ref mut buf_cell) => buf_cell.take().unwrap(),
State::Idle(ref mut buf_cell) => buf_cell.take().unwrap(),
_ => unreachable!(),
};
@ -336,24 +399,24 @@ impl File {
let std = self.std.clone();
inner.state = Busy(spawn_blocking(move || {
inner.state = State::Busy(spawn_blocking(move || {
let res = if let Some(seek) = seek {
(&*std).seek(seek).and_then(|_| std.set_len(size))
} else {
std.set_len(size)
}
.map(|_| 0); // the value is discarded later
.map(|()| 0); // the value is discarded later
// Return the result as a seek
(Operation::Seek(res), buf)
}));
let (op, buf) = match inner.state {
Idle(_) => unreachable!(),
Busy(ref mut rx) => rx.await?,
State::Idle(_) => unreachable!(),
State::Busy(ref mut rx) => rx.await?,
};
inner.state = Idle(Some(buf));
inner.state = State::Idle(Some(buf));
match op {
Operation::Seek(res) => res.map(|pos| {
@ -405,13 +468,11 @@ impl File {
Ok(File::from_std(std_file))
}
/// Destructures `File` into a [`std::fs::File`][std]. This function is
/// Destructures `File` into a [`std::fs::File`]. This function is
/// async to allow any in-flight operations to complete.
///
/// Use `File::try_into_std` to attempt conversion immediately.
///
/// [std]: std::fs::File
///
/// # Examples
///
/// ```no_run
@ -428,9 +489,7 @@ impl File {
Arc::try_unwrap(self.std).expect("Arc::try_unwrap failed")
}
/// Tries to immediately destructure `File` into a [`std::fs::File`][std].
///
/// [std]: std::fs::File
/// Tries to immediately destructure `File` into a [`std::fs::File`].
///
/// # Errors
///
@ -491,6 +550,34 @@ impl File {
let std = self.std.clone();
asyncify(move || std.set_permissions(perm)).await
}
/// Set the maximum buffer size for the underlying [`AsyncRead`] / [`AsyncWrite`] operation.
///
/// Although Tokio uses a sensible default value for this buffer size, this function would be
/// useful for changing that default depending on the situation.
///
/// # Examples
///
/// ```no_run
/// use tokio::fs::File;
/// use tokio::io::AsyncWriteExt;
///
/// # async fn dox() -> std::io::Result<()> {
/// let mut file = File::open("foo.txt").await?;
///
/// // Set maximum buffer size to 8 MiB
/// file.set_max_buf_size(8 * 1024 * 1024);
///
/// let mut buf = vec![1; 1024 * 1024 * 1024];
///
/// // Write the 1 GiB buffer in chunks up to 8 MiB each.
/// file.write_all(&mut buf).await?;
/// # Ok(())
/// # }
/// ```
pub fn set_max_buf_size(&mut self, max_buf_size: usize) {
self.max_buf_size = max_buf_size;
}
}
impl AsyncRead for File {
@ -505,51 +592,51 @@ impl AsyncRead for File {
loop {
match inner.state {
Idle(ref mut buf_cell) => {
State::Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
if !buf.is_empty() {
buf.copy_to(dst);
*buf_cell = Some(buf);
return Ready(Ok(()));
return Poll::Ready(Ok(()));
}
buf.ensure_capacity_for(dst);
buf.ensure_capacity_for(dst, me.max_buf_size);
let std = me.std.clone();
inner.state = Busy(spawn_blocking(move || {
inner.state = State::Busy(spawn_blocking(move || {
let res = buf.read_from(&mut &*std);
(Operation::Read(res), buf)
}));
}
Busy(ref mut rx) => {
State::Busy(ref mut rx) => {
let (op, mut buf) = ready!(Pin::new(rx).poll(cx))?;
match op {
Operation::Read(Ok(_)) => {
buf.copy_to(dst);
inner.state = Idle(Some(buf));
return Ready(Ok(()));
inner.state = State::Idle(Some(buf));
return Poll::Ready(Ok(()));
}
Operation::Read(Err(e)) => {
assert!(buf.is_empty());
inner.state = Idle(Some(buf));
return Ready(Err(e));
inner.state = State::Idle(Some(buf));
return Poll::Ready(Err(e));
}
Operation::Write(Ok(_)) => {
Operation::Write(Ok(())) => {
assert!(buf.is_empty());
inner.state = Idle(Some(buf));
inner.state = State::Idle(Some(buf));
continue;
}
Operation::Write(Err(e)) => {
assert!(inner.last_write_err.is_none());
inner.last_write_err = Some(e.kind());
inner.state = Idle(Some(buf));
inner.state = State::Idle(Some(buf));
}
Operation::Seek(result) => {
assert!(buf.is_empty());
inner.state = Idle(Some(buf));
inner.state = State::Idle(Some(buf));
if let Ok(pos) = result {
inner.pos = pos;
}
@ -568,11 +655,11 @@ impl AsyncSeek for File {
let inner = me.inner.get_mut();
match inner.state {
Busy(_) => Err(io::Error::new(
State::Busy(_) => Err(io::Error::new(
io::ErrorKind::Other,
"other file operation is pending, call poll_complete before start_seek",
)),
Idle(ref mut buf_cell) => {
State::Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
// Factor in any unread data from the buf
@ -586,7 +673,7 @@ impl AsyncSeek for File {
let std = me.std.clone();
inner.state = Busy(spawn_blocking(move || {
inner.state = State::Busy(spawn_blocking(move || {
let res = (&*std).seek(pos);
(Operation::Seek(res), buf)
}));
@ -601,10 +688,10 @@ impl AsyncSeek for File {
loop {
match inner.state {
Idle(_) => return Poll::Ready(Ok(inner.pos)),
Busy(ref mut rx) => {
State::Idle(_) => return Poll::Ready(Ok(inner.pos)),
State::Busy(ref mut rx) => {
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
inner.state = Idle(Some(buf));
inner.state = State::Idle(Some(buf));
match op {
Operation::Read(_) => {}
@ -617,7 +704,7 @@ impl AsyncSeek for File {
if let Ok(pos) = res {
inner.pos = pos;
}
return Ready(res);
return Poll::Ready(res);
}
}
}
@ -637,12 +724,12 @@ impl AsyncWrite for File {
let inner = me.inner.get_mut();
if let Some(e) = inner.last_write_err.take() {
return Ready(Err(e.into()));
return Poll::Ready(Err(e.into()));
}
loop {
match inner.state {
Idle(ref mut buf_cell) => {
State::Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
let seek = if !buf.is_empty() {
@ -651,7 +738,7 @@ impl AsyncWrite for File {
None
};
let n = buf.copy_from(src);
let n = buf.copy_from(src, me.max_buf_size);
let std = me.std.clone();
let blocking_task_join_handle = spawn_mandatory_blocking(move || {
@ -667,13 +754,13 @@ impl AsyncWrite for File {
io::Error::new(io::ErrorKind::Other, "background task failed")
})?;
inner.state = Busy(blocking_task_join_handle);
inner.state = State::Busy(blocking_task_join_handle);
return Ready(Ok(n));
return Poll::Ready(Ok(n));
}
Busy(ref mut rx) => {
State::Busy(ref mut rx) => {
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
inner.state = Idle(Some(buf));
inner.state = State::Idle(Some(buf));
match op {
Operation::Read(_) => {
@ -698,6 +785,81 @@ impl AsyncWrite for File {
}
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
ready!(crate::trace::trace_leaf(cx));
let me = self.get_mut();
let inner = me.inner.get_mut();
if let Some(e) = inner.last_write_err.take() {
return Poll::Ready(Err(e.into()));
}
loop {
match inner.state {
State::Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
let seek = if !buf.is_empty() {
Some(SeekFrom::Current(buf.discard_read()))
} else {
None
};
let n = buf.copy_from_bufs(bufs, me.max_buf_size);
let std = me.std.clone();
let blocking_task_join_handle = spawn_mandatory_blocking(move || {
let res = if let Some(seek) = seek {
(&*std).seek(seek).and_then(|_| buf.write_to(&mut &*std))
} else {
buf.write_to(&mut &*std)
};
(Operation::Write(res), buf)
})
.ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "background task failed")
})?;
inner.state = State::Busy(blocking_task_join_handle);
return Poll::Ready(Ok(n));
}
State::Busy(ref mut rx) => {
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
inner.state = State::Idle(Some(buf));
match op {
Operation::Read(_) => {
// We don't care about the result here. The fact
// that the cursor has advanced will be reflected in
// the next iteration of the loop
continue;
}
Operation::Write(res) => {
// If the previous write was successful, continue.
// Otherwise, error.
res?;
continue;
}
Operation::Seek(_) => {
// Ignore the seek
continue;
}
}
}
}
}
}
fn is_write_vectored(&self) -> bool {
true
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
ready!(crate::trace::trace_leaf(cx));
let inner = self.inner.get_mut();
@ -731,7 +893,7 @@ impl std::os::unix::io::AsRawFd for File {
}
}
#[cfg(all(unix, not(tokio_no_as_fd)))]
#[cfg(unix)]
impl std::os::unix::io::AsFd for File {
fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> {
unsafe {
@ -748,9 +910,7 @@ impl std::os::unix::io::FromRawFd for File {
}
cfg_windows! {
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
#[cfg(not(tokio_no_as_fd))]
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle};
impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle {
@ -758,7 +918,6 @@ cfg_windows! {
}
}
#[cfg(not(tokio_no_as_fd))]
impl AsHandle for File {
fn as_handle(&self) -> BorrowedHandle<'_> {
unsafe {
@ -780,7 +939,7 @@ impl Inner {
async fn complete_inflight(&mut self) {
use crate::future::poll_fn;
poll_fn(|cx| self.poll_complete_inflight(cx)).await
poll_fn(|cx| self.poll_complete_inflight(cx)).await;
}
fn poll_complete_inflight(&mut self, cx: &mut Context<'_>) -> Poll<()> {
@ -797,21 +956,21 @@ impl Inner {
fn poll_flush(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
if let Some(e) = self.last_write_err.take() {
return Ready(Err(e.into()));
return Poll::Ready(Err(e.into()));
}
let (op, buf) = match self.state {
Idle(_) => return Ready(Ok(())),
Busy(ref mut rx) => ready!(Pin::new(rx).poll(cx))?,
State::Idle(_) => return Poll::Ready(Ok(())),
State::Busy(ref mut rx) => ready!(Pin::new(rx).poll(cx))?,
};
// The buffer is not used here
self.state = Idle(Some(buf));
self.state = State::Idle(Some(buf));
match op {
Operation::Read(_) => Ready(Ok(())),
Operation::Write(res) => Ready(res),
Operation::Seek(_) => Ready(Ok(())),
Operation::Read(_) => Poll::Ready(Ok(())),
Operation::Write(res) => Poll::Ready(res),
Operation::Seek(_) => Poll::Ready(Ok(())),
}
}
}

View File

@ -231,7 +231,7 @@ fn flush_while_idle() {
#[cfg_attr(miri, ignore)] // takes a really long time with miri
fn read_with_buffer_larger_than_max() {
// Chunks
let chunk_a = crate::io::blocking::MAX_BUF;
let chunk_a = crate::io::blocking::DEFAULT_MAX_BUF_SIZE;
let chunk_b = chunk_a * 2;
let chunk_c = chunk_a * 3;
let chunk_d = chunk_a * 4;
@ -303,7 +303,7 @@ fn read_with_buffer_larger_than_max() {
#[cfg_attr(miri, ignore)] // takes a really long time with miri
fn write_with_buffer_larger_than_max() {
// Chunks
let chunk_a = crate::io::blocking::MAX_BUF;
let chunk_a = crate::io::blocking::DEFAULT_MAX_BUF_SIZE;
let chunk_b = chunk_a * 2;
let chunk_c = chunk_a * 3;
let chunk_d = chunk_a * 4;

View File

@ -5,9 +5,7 @@ use std::path::Path;
/// Creates a new hard link on the filesystem.
///
/// This is an async version of [`std::fs::hard_link`][std]
///
/// [std]: std::fs::hard_link
/// This is an async version of [`std::fs::hard_link`].
///
/// The `dst` path will be a link pointing to the `src` path. Note that systems
/// often require these two paths to both be located on the same filesystem.

View File

@ -7,7 +7,7 @@ use std::path::Path;
/// Given a path, queries the file system to get information about a file,
/// directory, etc.
///
/// This is an async version of [`std::fs::metadata`][std]
/// This is an async version of [`std::fs::metadata`].
///
/// This function will traverse symbolic links to query information about the
/// destination file.
@ -18,7 +18,6 @@ use std::path::Path;
/// `GetFileAttributesEx` function on Windows. Note that, this [may change in
/// the future][changes].
///
/// [std]: std::fs::metadata
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
///
/// # Errors

View File

@ -30,6 +30,7 @@ mock! {
pub fn open(pb: PathBuf) -> io::Result<Self>;
pub fn set_len(&self, size: u64) -> io::Result<()>;
pub fn set_permissions(&self, _perm: Permissions) -> io::Result<()>;
pub fn set_max_buf_size(&self, max_buf_size: usize);
pub fn sync_all(&self) -> io::Result<()>;
pub fn sync_data(&self) -> io::Result<()>;
pub fn try_clone(&self) -> io::Result<Self>;
@ -124,12 +125,12 @@ impl<T> Future for JoinHandle<T> {
type Output = Result<T, io::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
use std::task::Poll::*;
use std::task::Poll;
match Pin::new(&mut self.rx).poll(cx) {
Ready(Ok(v)) => Ready(Ok(v)),
Ready(Err(e)) => panic!("error = {:?}", e),
Pending => Pending,
Poll::Ready(Ok(v)) => Poll::Ready(Ok(v)),
Poll::Ready(Err(e)) => panic!("error = {:?}", e),
Poll::Pending => Poll::Pending,
}
}
}

View File

@ -1,46 +1,218 @@
#![cfg(not(loom))]
//! Asynchronous file and standard stream adaptation.
//! Asynchronous file utilities.
//!
//! This module contains utility methods and adapter types for input/output to
//! files or standard streams (`Stdin`, `Stdout`, `Stderr`), and
//! filesystem manipulation, for use within (and only within) a Tokio runtime.
//! This module contains utility methods for working with the file system
//! asynchronously. This includes reading/writing to files, and working with
//! directories.
//!
//! Tasks run by *worker* threads should not block, as this could delay
//! servicing reactor events. Portable filesystem operations are blocking,
//! however. This module offers adapters which use a `blocking` annotation
//! to inform the runtime that a blocking operation is required. When
//! necessary, this allows the runtime to convert the current thread from a
//! *worker* to a *backup* thread, where blocking is acceptable.
//! Be aware that most operating systems do not provide asynchronous file system
//! APIs. Because of that, Tokio will use ordinary blocking file operations
//! behind the scenes. This is done using the [`spawn_blocking`] threadpool to
//! run them in the background.
//!
//! ## Usage
//! The `tokio::fs` module should only be used for ordinary files. Trying to use
//! it with e.g., a named pipe on Linux can result in surprising behavior,
//! such as hangs during runtime shutdown. For special files, you should use a
//! dedicated type such as [`tokio::net::unix::pipe`] or [`AsyncFd`] instead.
//!
//! Where possible, users should prefer the provided asynchronous-specific
//! traits such as [`AsyncRead`], or methods returning a `Future` or `Poll`
//! type. Adaptions also extend to traits like `std::io::Read` where methods
//! return `std::io::Result`. Be warned that these adapted methods may return
//! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
//! to a *backup* thread immediately.
//! Currently, Tokio will always use [`spawn_blocking`] on all platforms, but it
//! may be changed to use asynchronous file system APIs such as io_uring in the
//! future.
//!
//! **Warning**: These adapters may create a large number of temporary tasks,
//! especially when reading large files. When performing a lot of operations
//! in one batch, it may be significantly faster to use [`spawn_blocking`]
//! directly:
//! # Usage
//!
//! The easiest way to use this module is to use the utility functions that
//! operate on entire files:
//!
//! * [`tokio::fs::read`](fn@crate::fs::read)
//! * [`tokio::fs::read_to_string`](fn@crate::fs::read_to_string)
//! * [`tokio::fs::write`](fn@crate::fs::write)
//!
//! The two `read` functions reads the entire file and returns its contents.
//! The `write` function takes the contents of the file and writes those
//! contents to the file. It overwrites the existing file, if any.
//!
//! For example, to read the file:
//!
//! ```
//! # async fn dox() -> std::io::Result<()> {
//! let contents = tokio::fs::read_to_string("my_file.txt").await?;
//!
//! println!("File has {} lines.", contents.lines().count());
//! # Ok(())
//! # }
//! ```
//!
//! To overwrite the file:
//!
//! ```
//! # async fn dox() -> std::io::Result<()> {
//! let contents = "First line.\nSecond line.\nThird line.\n";
//!
//! tokio::fs::write("my_file.txt", contents.as_bytes()).await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Using `File`
//!
//! The main type for interacting with files is [`File`]. It can be used to read
//! from and write to a given file. This is done using the [`AsyncRead`] and
//! [`AsyncWrite`] traits. This type is generally used when you want to do
//! something more complex than just reading or writing the entire contents in
//! one go.
//!
//! **Note:** It is important to use [`flush`] when writing to a Tokio
//! [`File`]. This is because calls to `write` will return before the write has
//! finished, and [`flush`] will wait for the write to finish. (The write will
//! happen even if you don't flush; it will just happen later.) This is
//! different from [`std::fs::File`], and is due to the fact that `File` uses
//! `spawn_blocking` behind the scenes.
//!
//! For example, to count the number of lines in a file without loading the
//! entire file into memory:
//!
//! ```no_run
//! use tokio::fs::File;
//! use std::io::{BufReader, BufRead};
//! async fn count_lines(file: File) -> Result<usize, std::io::Error> {
//! let file = file.into_std().await;
//! tokio::task::spawn_blocking(move || {
//! let line_count = BufReader::new(file).lines().count();
//! Ok(line_count)
//! }).await?
//! use tokio::io::AsyncReadExt;
//!
//! # async fn dox() -> std::io::Result<()> {
//! let mut file = File::open("my_file.txt").await?;
//!
//! let mut chunk = vec![0; 4096];
//! let mut number_of_lines = 0;
//! loop {
//! let len = file.read(&mut chunk).await?;
//! if len == 0 {
//! // Length of zero means end of file.
//! break;
//! }
//! for &b in &chunk[..len] {
//! if b == b'\n' {
//! number_of_lines += 1;
//! }
//! }
//! }
//!
//! println!("File has {} lines.", number_of_lines);
//! # Ok(())
//! # }
//! ```
//!
//! For example, to write a file line-by-line:
//!
//! ```no_run
//! use tokio::fs::File;
//! use tokio::io::AsyncWriteExt;
//!
//! # async fn dox() -> std::io::Result<()> {
//! let mut file = File::create("my_file.txt").await?;
//!
//! file.write_all(b"First line.\n").await?;
//! file.write_all(b"Second line.\n").await?;
//! file.write_all(b"Third line.\n").await?;
//!
//! // Remember to call `flush` after writing!
//! file.flush().await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Tuning your file IO
//!
//! Tokio's file uses [`spawn_blocking`] behind the scenes, and this has serious
//! performance consequences. To get good performance with file IO on Tokio, it
//! is recommended to batch your operations into as few `spawn_blocking` calls
//! as possible.
//!
//! One example of this difference can be seen by comparing the two reading
//! examples above. The first example uses [`tokio::fs::read`], which reads the
//! entire file in a single `spawn_blocking` call, and then returns it. The
//! second example will read the file in chunks using many `spawn_blocking`
//! calls. This means that the second example will most likely be more expensive
//! for large files. (Of course, using chunks may be necessary for very large
//! files that don't fit in memory.)
//!
//! The following examples will show some strategies for this:
//!
//! When creating a file, write the data to a `String` or `Vec<u8>` and then
//! write the entire file in a single `spawn_blocking` call with
//! `tokio::fs::write`.
//!
//! ```no_run
//! # async fn dox() -> std::io::Result<()> {
//! let mut contents = String::new();
//!
//! contents.push_str("First line.\n");
//! contents.push_str("Second line.\n");
//! contents.push_str("Third line.\n");
//!
//! tokio::fs::write("my_file.txt", contents.as_bytes()).await?;
//! # Ok(())
//! # }
//! ```
//!
//! Use [`BufReader`] and [`BufWriter`] to buffer many small reads or writes
//! into a few large ones. This example will most likely only perform one
//! `spawn_blocking` call.
//!
//! ```no_run
//! use tokio::fs::File;
//! use tokio::io::{AsyncWriteExt, BufWriter};
//!
//! # async fn dox() -> std::io::Result<()> {
//! let mut file = BufWriter::new(File::create("my_file.txt").await?);
//!
//! file.write_all(b"First line.\n").await?;
//! file.write_all(b"Second line.\n").await?;
//! file.write_all(b"Third line.\n").await?;
//!
//! // Due to the BufWriter, the actual write and spawn_blocking
//! // call happens when you flush.
//! file.flush().await?;
//! # Ok(())
//! # }
//! ```
//!
//! Manually use [`std::fs`] inside [`spawn_blocking`].
//!
//! ```no_run
//! use std::fs::File;
//! use std::io::{self, Write};
//! use tokio::task::spawn_blocking;
//!
//! # async fn dox() -> std::io::Result<()> {
//! spawn_blocking(move || {
//! let mut file = File::create("my_file.txt")?;
//!
//! file.write_all(b"First line.\n")?;
//! file.write_all(b"Second line.\n")?;
//! file.write_all(b"Third line.\n")?;
//!
//! // Unlike Tokio's file, the std::fs file does
//! // not need flush.
//!
//! io::Result::Ok(())
//! }).await.unwrap()?;
//! # Ok(())
//! # }
//! ```
//!
//! It's also good to be aware of [`File::set_max_buf_size`], which controls the
//! maximum amount of bytes that Tokio's [`File`] will read or write in a single
//! [`spawn_blocking`] call. The default is two megabytes, but this is subject
//! to change.
//!
//! [`spawn_blocking`]: fn@crate::task::spawn_blocking
//! [`AsyncRead`]: trait@crate::io::AsyncRead
//! [`AsyncWrite`]: trait@crate::io::AsyncWrite
//! [`BufReader`]: struct@crate::io::BufReader
//! [`BufWriter`]: struct@crate::io::BufWriter
//! [`tokio::net::unix::pipe`]: crate::net::unix::pipe
//! [`AsyncFd`]: crate::io::unix::AsyncFd
//! [`flush`]: crate::io::AsyncWriteExt::flush
//! [`tokio::fs::read`]: fn@crate::fs::read
mod canonicalize;
pub use self::canonicalize::canonicalize;

View File

@ -25,7 +25,7 @@ use std::os::windows::fs::OpenOptionsExt;
/// Generally speaking, when using `OpenOptions`, you'll first call [`new`],
/// then chain calls to methods to set each option, then call [`open`], passing
/// the path of the file you're trying to open. This will give you a
/// [`io::Result`][result] with a [`File`] inside that you can further operate
/// [`io::Result`] with a [`File`] inside that you can further operate
/// on.
///
/// This is a specialized version of [`std::fs::OpenOptions`] for usage from
@ -36,11 +36,9 @@ use std::os::windows::fs::OpenOptionsExt;
///
/// [`new`]: OpenOptions::new
/// [`open`]: OpenOptions::open
/// [result]: std::io::Result
/// [`File`]: File
/// [`File::open`]: File::open
/// [`File::create`]: File::create
/// [`std::fs::OpenOptions`]: std::fs::OpenOptions
///
/// # Examples
///
@ -444,7 +442,6 @@ feature! {
/// # Examples
///
/// ```no_run
/// use libc;
/// use tokio::fs::OpenOptions;
/// use std::io;
///

View File

@ -1,5 +1,5 @@
#![allow(unreachable_pub)]
//! Mock version of std::fs::OpenOptions;
//! Mock version of `std::fs::OpenOptions`;
use mockall::mock;
use crate::fs::mocks::MockFile;

View File

@ -4,9 +4,7 @@ use std::{io, path::Path};
/// Reads the entire contents of a file into a bytes vector.
///
/// This is an async version of [`std::fs::read`][std]
///
/// [std]: std::fs::read
/// This is an async version of [`std::fs::read`].
///
/// This is a convenience function for using [`File::open`] and [`read_to_end`]
/// with fewer imports and without an intermediate variable. It pre-allocates a

View File

@ -24,7 +24,7 @@ const CHUNK_SIZE: usize = 32;
/// Returns a stream over the entries within a directory.
///
/// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
/// This is an async version of [`std::fs::read_dir`].
///
/// This operation is implemented by running the equivalent blocking
/// operation on a separate thread pool using [`spawn_blocking`].
@ -140,6 +140,7 @@ impl ReadDir {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
target_os = "aix",
target_os = "nto",
target_os = "vita",
)))]
@ -203,6 +204,7 @@ pub struct DirEntry {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
target_os = "aix",
target_os = "nto",
target_os = "vita",
)))]
@ -336,6 +338,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
target_os = "aix",
target_os = "nto",
target_os = "vita",
)))]

View File

@ -5,9 +5,7 @@ use std::path::{Path, PathBuf};
/// Reads a symbolic link, returning the file that the link points to.
///
/// This is an async version of [`std::fs::read_link`][std]
///
/// [std]: std::fs::read_link
/// This is an async version of [`std::fs::read_link`].
pub async fn read_link(path: impl AsRef<Path>) -> io::Result<PathBuf> {
let path = path.as_ref().to_owned();
asyncify(move || std::fs::read_link(path)).await

View File

@ -5,7 +5,7 @@ use std::path::Path;
/// Removes an existing, empty directory.
///
/// This is an async version of [`std::fs::remove_dir`](std::fs::remove_dir)
/// This is an async version of [`std::fs::remove_dir`].
pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
let path = path.as_ref().to_owned();
asyncify(move || std::fs::remove_dir(path)).await

View File

@ -9,9 +9,7 @@ use std::path::Path;
/// depending on platform, other open file descriptors may prevent immediate
/// removal).
///
/// This is an async version of [`std::fs::remove_file`][std]
///
/// [std]: std::fs::remove_file
/// This is an async version of [`std::fs::remove_file`].
pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
let path = path.as_ref().to_owned();
asyncify(move || std::fs::remove_file(path)).await

View File

@ -8,7 +8,7 @@ use std::path::Path;
///
/// This will not work if the new name is on a different mount point.
///
/// This is an async version of [`std::fs::rename`](std::fs::rename)
/// This is an async version of [`std::fs::rename`].
pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned();

Some files were not shown because too many files have changed in this diff Show More