Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: FoundationEssentials: add Windows ProcessInfo changes #561

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
90 changes: 89 additions & 1 deletion Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift
Expand Up @@ -16,6 +16,8 @@ internal import _CShims
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif os(Windows)
import WinSDK
#endif

#if !NO_PROCESS
Expand Down Expand Up @@ -103,9 +105,13 @@ final class _ProcessInfo: Sendable {

var globallyUniqueString: String {
let uuid = UUID().uuidString
let pid = UInt64(getpid())
let pid = processIdentifier
#if canImport(Darwin)
let time: UInt64 = mach_absolute_time()
#elseif os(Windows)
var ullTime: ULONGLONG = 0
QueryUnbiasedInterruptTimePrecise(&ullTime)
let time: UInt64 = ullTime
#else
var ts: timespec = timespec()
clock_gettime(CLOCK_MONOTONIC_RAW, &ts)
Expand All @@ -117,7 +123,11 @@ final class _ProcessInfo: Sendable {
}

var processIdentifier: Int32 {
#if os(Windows)
return numericCast(GetProcessId(GetCurrentProcess()))
#else
return getpid()
#endif
}

var processName: String {
Expand Down Expand Up @@ -183,9 +193,61 @@ extension _ProcessInfo {
}
}

#if os(Windows)
internal var _rawOSVersion: RTL_OSVERSIONINFOEXW? {
typealias RtlGetVersionTy = @convention(c) (UnsafeMutablePointer<RTL_OSVERSIONINFOEXW>) -> NTSTATUS

guard let NTDLL = "ntdll.dll".withCString(encodedAs: UTF16.self, LoadLibraryW) else {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is less secure than the version that @jmschonfeld ported over from SCF. The primary difference being that DLL paths could impact the resolved library here vs the original version I had used in CF which would ensure that we pick up the version in the system. However, that also has a downside that it may not be the version of the DLL that is actually loaded which makes this a slightly better option.

return nil
}
defer { FreeLibrary(NTDLL) }

guard let pfnRtlGetVersion = unsafeBitCast(GetProcAddress(NTDLL, "RtlGetVersion"), to: RtlGetVersionTy?.self) else {
return nil
}

var version: RTL_OSVERSIONINFOEXW = .init()
version.dwOSVersionInfoSize = DWORD(MemoryLayout<RTL_OSVERSIONINFOEXW>.size)
guard pfnRtlGetVersion(&version) == 0 else {
return nil
}
return version
}
#endif

var operatingSystemVersionString: String {
// TODO: Check for `/etc/os-release` for Linux once DataIO is ready
// https://github.com/apple/swift-foundation/issues/221
#if os(Windows)
guard let version = _rawOSVersion else {
return "Windows"
}
let release = switch (version.dwMajorVersion, version.dwMinorVersion, version.dwBuildNumber) {
case (5, 0, _): "2000"
case (5, 1, _): "XP"
case (5, 2, _) where version.wProductType == VER_NT_WORKSTATION:
"XP Professional x64"
case (5, 2, _) where version.wProductType == VER_SUITE_WH_SERVER:
"Home Server"
case (5, 2, _): "Server 2003"
case (6, 0, _): "Server 2008"
case (6, 1, _) where version.wProductType == VER_NT_WORKSTATION:
"7"
case (6, 2, _) where version.wProductType == VER_NT_WORKSTATION:
"8"
case (6, 2, _): "Server 2012"
case (6, 3, _) where version.wProductType == VER_NT_WORKSTATION:
"8.1"
case (6, 3, _): "Server 2012 R2"
case (10, 0, ..<22000) where version.wProductType == VER_NT_WORKSTATION:
"10"
case (10, 0, 22000...) where version.wProductType == VER_NT_WORKSTATION:
"11"
case (10, 0, _): "Server 2019"
default: "unknown"
}
return "Windows \(release) (build \(version.dwBuildNumber))"
#else
#if os(macOS)
var versionString = "macOS"
#elseif os(Linux)
Expand All @@ -204,9 +266,18 @@ extension _ProcessInfo {
}

return versionString
#endif
}

var operatingSystemVersion: (major: Int, minor: Int, patch: Int) {
#if os(Windows)
guard let version = _rawOSVersion else {
return (major: -1, minor: 0, patch: 0)
}
return (major: Int(version.dwMajorVersion),
minor: Int(version.dwMinorVersion),
patch: Int(version.dwBuildNumber))
#else
var uts: utsname = utsname()
guard uname(&uts) == 0 else {
return (major: -1, minor: 0, patch: 0)
Expand All @@ -223,6 +294,7 @@ extension _ProcessInfo {
let minor = version.count >= 2 ? version[1] : 0
let patch = version.count >= 3 ? version[2] : 0
return (major: major, minor: minor, patch: patch)
#endif
}

func isOperatingSystemAtLeast(_ version: (major: Int, minor: Int, patch: Int)) -> Bool {
Expand Down Expand Up @@ -261,6 +333,10 @@ extension _ProcessInfo {
return 0
}
return Int(count)
#elseif os(Windows)
var siInfo = SYSTEM_INFO()
GetSystemInfo(&siInfo)
return Int(siInfo.dwNumberOfProcessors)
#else
return Int(sysconf(Int32(_SC_NPROCESSORS_CONF)))
#endif
Expand All @@ -276,6 +352,10 @@ extension _ProcessInfo {
return 0
}
return Int(count)
#elseif os(Windows)
var siInfo = SYSTEM_INFO()
GetSystemInfo(&siInfo)
return siInfo.dwActiveProcessorMask.nonzeroBitCount
#else
return Int(sysconf(Int32(_SC_NPROCESSORS_ONLN)))
#endif
Expand All @@ -293,6 +373,12 @@ extension _ProcessInfo {
}
return 0
}
#elseif os(Windows)
var TotalMemoryKB: ULONGLONG = 0
guard GetPhysicallyInstalledSystemMemory(&TotalMemoryKB) else {
return 0
}
return TotalMemoryKB * 1024
#else
var memory = sysconf(Int32(_SC_PHYS_PAGES))
memory *= sysconf(Int32(_SC_PAGESIZE))
Expand All @@ -305,6 +391,8 @@ extension _ProcessInfo {
let (_, secondsPerTick) = _systemClockTickRate
let time = mach_absolute_time()
return TimeInterval(time) * secondsPerTick
#elseif os(Windows)
return TimeInterval(GetTickCount64()) / 1000.0
#else
var ts = timespec()
guard clock_gettime(CLOCK_MONOTONIC, &ts) == 0 else {
Expand Down