-
Notifications
You must be signed in to change notification settings - Fork 127
[DRAFT] ProcessID, Signal, SignalSet, TaskInfo, ResourceUsageInfo #20
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
ad80d0f
0ebaa29
544fc8a
881a8ad
0c32e4e
c96a3e6
a051d71
153e5d3
e8b5ab4
9edaf16
5bc82a4
9830717
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| /* | ||
| This source file is part of the Swift System open source project | ||
|
|
||
| Copyright (c) 2020 Apple Inc. and the Swift System project authors | ||
| Licensed under Apache License v2.0 with Runtime Library Exception | ||
|
|
||
| See https://swift.org/LICENSE.txt for license information | ||
| */ | ||
|
|
||
| #ifdef __MACH__ | ||
| #include "libproc.h" | ||
| #else | ||
| #error "whoops" | ||
| #endif | ||
|
|
||
| // |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,3 +16,7 @@ | |
| #if defined(_WIN32) | ||
| #include <CSystemWindows.h> | ||
| #endif | ||
|
|
||
| #ifdef __MACH__ | ||
| #include <CSystemDarwin.h> | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
|
|
||
| // FIXME(DO NOT MERGE): We need to find a way around this. We want to declare | ||
| // a typealias to a struct from a header, but don't want downstream to import | ||
| // Darwin or the whole header just for that. | ||
| // | ||
| import Darwin | ||
| extension CInterop { | ||
| public typealias PID = Int32 | ||
| public typealias ProcTaskInfo = proc_taskinfo // FIXME | ||
| public typealias RUsageInfo = rusage_info_current // FIXME | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have the option to just go with the original type names here, but I think it's still nice to have these typealiases. |
||
| } | ||
|
|
||
| public struct ProcessID: RawRepresentable, Hashable, Codable { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me that this is more of a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This struct is the handle to the process, it's very much like a file descriptor. It's not "safe" to assume it's still valid after a process is torn down, etc. I've been thinking that a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think |
||
| /// The raw C process id. | ||
| @_alwaysEmitIntoClient | ||
| public let rawValue: CInterop.PID | ||
|
|
||
| /// Creates a strongly-typed process id from a raw C pid | ||
| @_alwaysEmitIntoClient | ||
| public init(rawValue: CInterop.PID) { self.rawValue = rawValue } | ||
|
|
||
| fileprivate init(_ rawValue: CInterop.PID) { self.init(rawValue: rawValue) } | ||
|
|
||
| } | ||
|
|
||
| extension ProcessID { | ||
| public static func current() -> ProcessID { | ||
| ProcessID(getpid()) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
|
|
||
| extension ProcessID { | ||
| public struct ResourceUsageInfo: RawRepresentable/*, Hashable, Codable*/ { | ||
| /// The raw C process id. | ||
| @_alwaysEmitIntoClient | ||
| public let rawValue: CInterop.RUsageInfo | ||
|
|
||
| /// Creates a strongly-typed process id from a raw C pid | ||
| @_alwaysEmitIntoClient | ||
| public init(rawValue: CInterop.RUsageInfo) { self.rawValue = rawValue } | ||
|
|
||
| fileprivate init(_ rawValue: CInterop.RUsageInfo) { | ||
| self.init(rawValue: rawValue) | ||
| } | ||
|
|
||
| fileprivate static var blank: ResourceUsageInfo { | ||
| ResourceUsageInfo(rusage_info_current()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // FIXME(DO NOT MERGE): system_foo wrappers for these and mocking | ||
| import CSystem | ||
| extension ProcessID { | ||
| public func getResourceUsageInfo() throws -> ResourceUsageInfo { | ||
| var current = ResourceUsageInfo.blank | ||
| try withUnsafeMutablePointer(to: ¤t) { | ||
| try $0.withMemoryRebound(to: rusage_info_t?.self, capacity: 1) { | ||
| guard 0 == proc_pid_rusage(self.rawValue, RUSAGE_INFO_CURRENT, $0) else { | ||
| throw Errno(rawValue: errno) | ||
| } | ||
| } | ||
| } | ||
| return current | ||
| } | ||
| } | ||
|
|
||
| // FIXME: docs or comments, the headers have none... | ||
| // FIXME: names | ||
| extension ProcessID.ResourceUsageInfo { | ||
| // FIXME: UUID proper type | ||
| public typealias UUID = ( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Foundation already has a wrapper for |
||
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | ||
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | ||
|
|
||
| /// `ri_uuid`: TBD | ||
| public var uuid: UUID { rawValue.ri_uuid } | ||
|
|
||
| /// `ri_user_time`: TBD | ||
| public var userTime: UInt64 { rawValue.ri_user_time } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should specify units for all these dimensioned quantities, ideally at the type system level. Should these time intervals return whatever we end up using for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately the headers are not clear what even is here. It's like that |
||
|
|
||
| /// `ri_system_time`: TBD | ||
| public var systemTime: UInt64 { rawValue.ri_system_time } | ||
|
|
||
| /// `ri_pkg_idle_wkups`: TBD | ||
| public var pkgIdleWakeups: UInt64 { rawValue.ri_pkg_idle_wkups } | ||
|
|
||
| /// `ri_interrupt_wkups`: TBD | ||
| public var interruptWakeups: UInt64 { rawValue.ri_interrupt_wkups } | ||
|
|
||
| /// `ri_pageins`: TBD | ||
| public var pageIns: UInt64 { rawValue.ri_pageins } | ||
|
|
||
| /// `ri_wired_size`: TBD | ||
| public var wiredSize: UInt64 { rawValue.ri_wired_size } | ||
|
|
||
| /// `ri_resident_size`: TBD | ||
| public var residentSize: UInt64 { rawValue.ri_resident_size } | ||
|
|
||
| /// `ri_phys_footprint`: TBD | ||
| public var physicalFootprint: UInt64 { rawValue.ri_phys_footprint } | ||
|
|
||
| /// `ri_proc_start_abstime`: TBD | ||
| public var processStartAbsoluteTime: UInt64 { rawValue.ri_proc_start_abstime } | ||
|
|
||
| /// `ri_proc_exit_abstime`: TBD | ||
| public var processExitAbsoluteTime: UInt64 { rawValue.ri_proc_exit_abstime } | ||
|
|
||
| /// `ri_child_user_time`: TBD | ||
| public var childUserTime: UInt64 { rawValue.ri_child_user_time } | ||
|
|
||
| /// `ri_child_system_time`: TBD | ||
| public var childSystemTime: UInt64 { rawValue.ri_child_system_time } | ||
|
|
||
| /// `ri_child_pkg_idle_wkups`: TBD | ||
| public var childPkgIdleWakeups: UInt64 { rawValue.ri_child_pkg_idle_wkups } | ||
|
|
||
| /// `ri_child_interrupt_wkups`: TBD | ||
| public var childInterruptWakeups: UInt64 { rawValue.ri_child_interrupt_wkups } | ||
|
|
||
| /// `ri_child_pageins`: TBD | ||
| public var childPageIns: UInt64 { rawValue.ri_child_pageins } | ||
|
|
||
| /// `ri_child_elapsed_abstime`: TBD | ||
| public var childElapsedAbsoluteTime: UInt64 { rawValue.ri_child_elapsed_abstime } | ||
|
|
||
| /// `ri_diskio_bytesread`: TBD | ||
| public var diskIOBytesRead: UInt64 { rawValue.ri_diskio_bytesread } | ||
|
|
||
| /// `ri_diskio_byteswritten`: TBD | ||
| public var diskIOBytesWritten: UInt64 { rawValue.ri_diskio_byteswritten } | ||
|
|
||
| /// `ri_cpu_time_qos_default`: TBD | ||
| public var cpuTimeQOSDefault: UInt64 { rawValue.ri_cpu_time_qos_default } | ||
|
|
||
| /// `ri_cpu_time_qos_maintenance`: TBD | ||
| public var cpuTimeQOSMaintenance: UInt64 { rawValue.ri_cpu_time_qos_maintenance } | ||
|
|
||
| /// `ri_cpu_time_qos_background`: TBD | ||
| public var cpuTimeQOSBackground: UInt64 { rawValue.ri_cpu_time_qos_background } | ||
|
|
||
| /// `ri_cpu_time_qos_utility`: TBD | ||
| public var cpuTimeQOSUtility: UInt64 { rawValue.ri_cpu_time_qos_utility } | ||
|
|
||
| /// `ri_cpu_time_qos_legacy`: TBD | ||
| public var cpuTimeQOSLegacy: UInt64 { rawValue.ri_cpu_time_qos_legacy } | ||
|
|
||
| /// `ri_cpu_time_qos_user_initiated`: TBD | ||
| public var cpuTimeQOSUserInitiated: UInt64 { rawValue.ri_cpu_time_qos_user_initiated } | ||
|
|
||
| /// `ri_cpu_time_qos_user_interactive`: TBD | ||
| public var cpuTimeQOSUserInteractive: UInt64 { rawValue.ri_cpu_time_qos_user_interactive } | ||
|
|
||
| /// `ri_billed_system_time`: TBD | ||
| public var billedSystemTime: UInt64 { rawValue.ri_billed_system_time } | ||
|
|
||
| /// `ri_serviced_system_time`: TBD | ||
| public var servicedSystemTime: UInt64 { rawValue.ri_serviced_system_time } | ||
|
|
||
| /// `ri_logical_writes`: TBD | ||
| public var logicalWrites: UInt64 { rawValue.ri_logical_writes } | ||
|
|
||
| /// `ri_lifetime_max_phys_footprint`: TBD | ||
| public var lifetimeMaxPhysicalFootprint: UInt64 { rawValue.ri_lifetime_max_phys_footprint } | ||
|
|
||
| /// `ri_instructions`: TBD | ||
| public var instructions: UInt64 { rawValue.ri_instructions } | ||
|
|
||
| /// `ri_cycles`: TBD | ||
| public var cycles: UInt64 { rawValue.ri_cycles } | ||
|
|
||
| /// `ri_billed_energy`: TBD | ||
| public var billedEnergy: UInt64 { rawValue.ri_billed_energy } | ||
|
|
||
| /// `ri_serviced_energy`: TBD | ||
| public var servicedEnergy: UInt64 { rawValue.ri_serviced_energy } | ||
|
|
||
| /// `ri_interval_max_phys_footprint`: TBD | ||
| public var intervalMaxPhysicalFootprint: UInt64 { rawValue.ri_interval_max_phys_footprint } | ||
|
|
||
| /// `ri_runnable_time`: TBD | ||
| public var runnableTime: UInt64 { rawValue.ri_runnable_time } | ||
|
|
||
| /// `ri_flags`: TBD | ||
| public var flags: UInt64 { rawValue.ri_flags } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,114 @@ | ||||||||||
|
|
||||||||||
| public struct Signal: RawRepresentable, Hashable { | ||||||||||
| public var rawValue: CInt | ||||||||||
| public init(rawValue: CInt) { self.rawValue = rawValue } | ||||||||||
| fileprivate init(_ rawValue: CInt) { self.init(rawValue: rawValue) } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // FIXME(DO NOT MERGE): Migrate these to the constants.swift file | ||||||||||
| import Darwin | ||||||||||
|
|
||||||||||
| extension Signal { | ||||||||||
| #if os(Linux) | ||||||||||
| public static var unused: Singal { Signal(SIGUNUSED) } | ||||||||||
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| #endif | ||||||||||
|
|
||||||||||
| // TODO: better names | ||||||||||
|
|
||||||||||
| /// SIGHUP (1): terminal line hangup (default behavior: terminate process) | ||||||||||
| public static var hangup: Signal { Signal(SIGHUP) } | ||||||||||
lorentey marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| /// SIGINT (2): interrupt program (default behavior: terminate process) | ||||||||||
| public static var interrupt: Signal { Signal(SIGINT) } | ||||||||||
|
|
||||||||||
| /// SIGQUIT (3): quit program (default behavior: create core image) | ||||||||||
| public static var quit: Signal { Signal(SIGQUIT) } | ||||||||||
|
|
||||||||||
| /// SIGILL (4): illegal instruction (default behavior: create core image) | ||||||||||
| public static var illegalInsruction: Signal { Signal(SIGILL) } | ||||||||||
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| /// SIGTRAP (5): trace trap (default behavior: create core image) | ||||||||||
| public static var traceTrap: Signal { Signal(SIGTRAP) } | ||||||||||
|
|
||||||||||
| /// SIGABRT (6): abort program (formerly SIGIOT) (default behavior: create core image) | ||||||||||
| public static var abort: Signal { Signal(SIGABRT) } | ||||||||||
|
|
||||||||||
| /// SIGEMT (7): emulate instruction executed (default behavior: create core image) | ||||||||||
| public static var emulatorTrap: Signal { Signal(SIGEMT) } | ||||||||||
|
|
||||||||||
| /// SIGFPE (8): floating-point exception (default behavior: create core image) | ||||||||||
| public static var floatingPointException: Signal { Signal(SIGFPE) } | ||||||||||
|
|
||||||||||
| /// SIGKILL (9): kill program (default behavior: terminate process) | ||||||||||
| public static var kill: Signal { Signal(SIGKILL) } | ||||||||||
|
|
||||||||||
| /// SIGBUS (10): bus error (default behavior: create core image) | ||||||||||
| public static var busError: Signal { Signal(SIGBUS) } | ||||||||||
|
|
||||||||||
| /// SIGSEGV (11): segmentation violation (default behavior: create core image) | ||||||||||
| public static var segfault: Signal { Signal(SIGSEGV) } | ||||||||||
|
||||||||||
| public static var segfault: Signal { Signal(SIGSEGV) } | |
| public static var segmentationViolation: Signal { Signal(SIGSEGV) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not super how I feel about this. We're not using segmentation anymore but paging, so spelling "segfault" out may not make it easier to find?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having an unavailable-renamed entry for SIGSEGV would hopefully help point to whatever name we choose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true. But segmentationViolation is still not what this will be sent for so I'd be inclined to leave the as a "term of art".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is sort of like hangup in that the name has somehow outlived its original meaning, which is now a complete anachronism. SIGSEGV is defined as "segmentation violation" in the man pages, so at least this expansion isn't likely to confuse anyone.
@weissi, what would you propose as a replacement? Note that System is supposed to follow the Swift naming conventions, so the original arbitrary abbreviations need to be replaced, no matter how well known.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, POSIX describes SIGSEGV as "Invalid memory reference". Would we be willing to name it invalidMemoryReference?
SEGV is such an (in)famous signal, it may be worth spending a term-of-art card on segmentationViolation, no matter how far it is from the current meaning...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
invalidMemoryReference is IMHO much better than segmentationViolation. Both names however aren't as good as SIGSEGV or sigsegv IMHO. If you want to program your operating system directly, I think any translation of names is counterproductive and will lead to avoidable bugs, imprecise reviews, etc.
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| public static var pipe: Signal { Signal(SIGPIPE) } | |
| public static var brokenPipe: Signal { Signal(SIGPIPE) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, you can also get them on sockets...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's interesting. 🤔
Broken pipe is what psignal prints on SIGPIPE, which is why I suggested it. It has the advantage that it evokes the original name but also tries to clarify it a little.
Should we use that, use pipe, or should we try to find a more exact term?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, we imported EPIPE as Errno.brokenPipe. I think we should probably use the same name here -- especially since it looks like the error is returned in the exact same situations as when the signal is sent.
[EPIPE]
Broken pipe. A write was attempted on a socket, pipe, or FIFO for which there is no process to read the data.
lorentey marked this conversation as resolved.
Show resolved
Hide resolved
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the T is short for "terminal" in this case; this is a job control thing, generated on C-z. It's the polite way to request that a process suspend itself, so what if:
| public static var temporaryStop: Signal { Signal(SIGTSTP) } | |
| public static var stopRequest: Signal { Signal(SIGTSTP) } |
C.f. the terminationRequest suggestion above for SIGTERM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think SIGSTOP is uncatchable so it's not a request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is SIGTSTP.
interactiveStop could be another name, or terminalStop, hopefully avoiding confusion with SIGSTOP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh god, this is a great argument for not renaming them from what they are. I know SIGTSTP and SIGSTOP but stopRequest and interactiveStop really don't convey the meaning they need to convey IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would terminalStop be clearer than interactiveStop? "Interactive stop" was from the GNU docs, and seemed a little more general than "terminal".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't think so. People who operate at this level know exactly what SIGTSTP is but they don't mind what Apple/POSIX/GNU document it as. So any wording you could make up here would lose the most crucial information which is that it is SIGTSTP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling it SIGTSTP is a non-starter, I'm afraid. We need to follow the naming conventions we established for System.
Either terminalStop or interactiveStop looks fine to me. What term does POSIX use to explain this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, POSIX calls it "Terminal stop signal". That sort of settles it for me.
| public static var temporaryStop: Signal { Signal(SIGTSTP) } | |
| public static var terminalStop: Signal { Signal(SIGTSTP) } |
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
milseman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the io prefix weird? Not that I can think of a better suggestion. readyForIO and readyCondition (c.f. urgentCondition) both seem silly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out we should just import Darwin! Since System isn't an overlay, it doesn't need to reexport the module, and merely importing it won't pollute the client namespace with C junk. 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use
Darwin's types though? Also, can we do this with ourCSystemmodule as well (which is needed for Linux, sincelibCisn'tlibSystem).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so -- if people only import System, they will still be able to refer to these types by one of their System typealiases (such as the ones in CInterop or RawValue). (They can't spell
Darwin.foounless they import it.)CSystemis trickier, because it shouldn't be a public module, so we should continue to import it@_implementationOnly.