-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
[API Proposal]: Process.TryGetProcessById() #101582
Comments
Tagging subscribers to this area: @dotnet/area-system-diagnostics-process |
The |
Can you elaborate on the scenario where this is needed? You're on some path where the overhead of the exception is prohibitive? |
I don't think that Exceptions are massively expensive, they can be error prone to handle, may not actually cover what the user wanted to cover over time, and it is generally considered best practice to not do control flow based on exceptions. Forcing users to handle them in scenarios that are known to be prone to arbitrary failure (looking up process IDs, opening or creating files, etc) just because the operation itself already has a larger bit of overhead doesn't make sense to me and seems to be encouraging practices that are widely touted as being bad or problematic. The general |
That's an extremely slippery slope. .NET uses exceptions for these cases. From my perspective, that argument effectively leads to saying that any method which could throw (which is many) could have a Try method just to represent failure differently, and that's not a path we're going to go down. |
I've seen this discussion a few times on API proposals in the past and generally they returned to the consensus that .NET for good or ill chose the exception model for error handling and the Try pattern is only for the highest performance cases where errors are inevitable e.g. parsing. If we use the Try pattern more broadly, what criteria do you believe we should use to decide whether it is worthwhile to add the duplicate API? |
For this theres a cost even in determining whether the process exists or not. Looking at the implementation its using |
I disagree. This isn't a statement that try can/should be provided for "any method" which could throw, it's specifically keeping the spirit of
The only difference is that it's saying that "to avoid performance problems related to exceptions" should be extended to basically include something along the lines of "or where the user performing their own pre-validation is impossible". So something like However, something like There is a very clear and distinguished line we can draw that keeps this from being a slippery slope. Which provides clear and concise benefit to users that are using these APIs while maintaining the spirit of the guideline and how users generally expect Try APIs to work. |
We are ourselves functionally using system level "try apis" for most of the functions in this category already and simply converting them to exceptions 100% of the time. So it's really just an ask to expose the functionality the system already provides because the system already understands that it needs to exist that way. |
And I believe that extension includes the vast majority of exceptions we end up throwing.
I disagree. That "or" is a very large leap from the original spirit. |
Do you have an example of what you think it would include? The things people have historically asked for these APIs around are explicitly APIs like file handling or things involving processes and that other languages and the system APIs themselves provide "try" like APIs for. It feels like a disservice to .NET users to keep pushing off exposing such functionality ourselves due to the potential for it to be a slipper slope or belief that the performance characteristics don't matter. |
As one example of many, Stream.Read; that can throw exceptions for any number of reasons where "the user performing their own pre-validation is impossible". |
Stream is an example of IO and would qualify as something a try API should really exist for IMO. It's functionally the same concept as doing other types of IO, a case where such try APIs have been explicitly requested in the past, and where other languages or the system level APIs themselves expose the ability to do the functionality in a non-faulting form.
|
To me, and I think to a pretty decent part of the .NET community, Things like Things like Things like |
If a method can fail in normal use, it qualifies for a |
And this is broadly arguing against .NET's general exception model and philosophy. It's been litigated in the past. I'm not going to relitigate it here. |
And this is precisely not the case for |
Yes, I pointed out the reason for diverging from that in the first post under "Alternative Designs". But maybe I missed a way that would work. |
Requiring to the original proposal. It's certainly true that it is not possible for the developer to avoid potential exceptions. But I'm not clear what the motivation is in this particular case to avoid an exception. Are you seeing a performance problem from so many exceptions? Or it's about writing more compact code? |
And for the general point, I don't have the FDG in front of me, but my recollection is this falls out of the scope they define for the Try pattern. Assuming that's the case (@bartonjs) then the first thing to do if you want this API is to get the FDG changed, so we don't debate this case by case. |
This API proposal was motivated by a discussion about merits of catching ArgumentExceptions in unrelated issue. |
@danmoseley I apologize, I neglected to answer @stephentoub's original question. This particular requests was not motivated by performance considerations; it came about when I was searching through my code for places where I catch I'm not arguing for making exception-free versions of all core apis, but was bothered by the fact that attempting to find a process by id when it has already exited is not exceptional (and is rather routine, actually) but still throws. I was also bothered by the specific exception that was thrown, as @jkotas also mentions. |
Gotcha. With respect to the exception type, it wouldn't be the only case where it arguably wasn't the best choice they made way back. The only option would be to throw a derived type, presumably a new one, and it isn't clear what that could reasonably be. |
As for not being exceptional - looking for input from @bartonjs on whether that conflicts with FDG. If it does, the discussion (if you want to pursue) should be advocating FDG not changing this specific case. |
GetProcessById throwing an ArgumentException when there's no process with that ID is not consistent with the guidelines. Or at least with best practice and a Krzysztof personal advice bubble:
So this should have been something like InvalidOperationException, while a negative PID is presumably actually an ArgumentException.
FDG is very clear on this one:
and also
There is, of course, always room for "maybe we never considered...", but given that this is on the wrong side of two "DOs" (and my gut says we're already had Try methods emitting IDisposable things), it feels unlikely to be the way we choose.
@stephentoub was already addressing this point. FDG doesn't directly define when not to make them, but what it has to say about when is
The prose also supports doing it for anything with an "inherent race condition" (citing ConcurrentDictionary.TryAdd). One thing that is potentially difficult for this specific proposal, and for the linked File.TryOpen proposal is
For TryGetProcessById, that one reason could be "that PID doesn't exist". If there are permissions associated with the routine then there's a question of should an access denied throw or return false (changing the reason to "I can't open it"). The sniff test is "does the same if-false block handle the failures the same way". Something like And, at the end of the day:
Which basically means that a Try method should only be added for performance reasons when there's a broad (enough) demonstrated need, not just a "but exceptions are expensive!" knee-jerk reaction. All that said, in my personal opinion If the existing API didn't misuse ArgumentException for "your argument doesn't match the runtime state" I'd probably fall back to asking what sort of workload would benefit from this (e.g. for a WaitForExit type thing, the slowness of the exception route is probably still faster than the expected wait time, so there's not (to me) valid a "but... perf!" argument). I'd also favor adding |
Background and motivation
The existing api for retrieving a process by its process id (
Process.GetProcessById()
) does not provide any exception-safe method of querying a process, as it throws anArgumentException
if the specified pid cannot be found and there is no means of atomically guaranteeing that a once-valid process id will remain valid by the time theProcess.GetProcessById()
call is being serviced.This is true regardless of where the pid was obtained from, even if the pid were obtained from a previous call to
Process.GetProcessById()
due to the inherently concurrent execution model associated with multiprocessing.If it's any sort of additional incentive, the exception thrown by
Process.GetProcessById()
when a valid-but-no-longer-extant pid is provided isArgumentException
, which is normally not an exception that a calling library/application should be catching. Moreover, the reuse ofArgumentException
to indicate both "process not found" and "invalid machine name" (for the two-parameter override) adds to the confusion of using this api, as one case is intended to be caught and the other, generally speaking, is not.API Proposal
API Usage
Alternative Designs
I know the preferred approach for
TryFoo()
methods is to return a boolean indicating success or failure and use anout
param to provide the output on success, but in this case I eschewed that approach in favor of returning a nullableProcess?
directly to make sure it remains easy to remember to always useusing
with the disposableProcess
object.Otherwise, the API might look as follows:
but that would be prone to misuse.
Risks
Some might be concerned about the proliferation of
TryFoo()
methods, though working around the unfortunate legacy use ofArgumentException
here and the inherently fallible nature of obtaining a process (that may have already exited) by its id really lend themselves to recommending the addition of an exception-free alternative to the currentProcess.GetProcessById()
method.The text was updated successfully, but these errors were encountered: