Skip to content

Partial Fix Issue 16615 - convert os pid to std.process Pid#5086

Merged
dlang-bot merged 1 commit intodlang:masterfrom
edi33416:process_enhancement
Jun 8, 2017
Merged

Partial Fix Issue 16615 - convert os pid to std.process Pid#5086
dlang-bot merged 1 commit intodlang:masterfrom
edi33416:process_enhancement

Conversation

@edi33416
Copy link
Contributor

@edi33416 edi33416 commented Feb 3, 2017

The issue on bugzilla states that we are currently missing the functionality of converting a pid to a std.process Pid in order to be able to use the kill function, if someone were to implement a task manager for example.

@dlang-bot
Copy link
Contributor

dlang-bot commented Feb 3, 2017

Fix Bugzilla Description
16615 std.process is missing functionality for child processes

@JackStouffer
Copy link
Contributor

While this is a nice convenience function, this does not address the primary complaint of the bug report: an inability to easily get the pid of a child process.

Please change your commit message to Partial Fix For Issue 16615 - convert os pid to std.process Pid

{
version (Windows) TestScript prog = "exit %~1";
else version (Posix) TestScript prog = "exit $1";
auto pid = spawnProcess([prog.path, "10"]);
Copy link
Member

Choose a reason for hiding this comment

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

This is racy - by the time you issue kill the process may already exit normally. An infinite loop would be a better choice then "exit $1"

std/process.d Outdated
_processHandle = new Pid(pid, _handle);
}

return _processHandle;
Copy link
Member

Choose a reason for hiding this comment

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

Most functions of this module will throw on error, I suggest to do the same here.

@edi33416 edi33416 force-pushed the process_enhancement branch 6 times, most recently from 1486096 to 91ce00d Compare April 6, 2017 09:32
@edi33416 edi33416 force-pushed the process_enhancement branch from 91ce00d to 560e2c8 Compare April 10, 2017 11:02
@edi33416 edi33416 changed the title Fix Issue 16615 - convert os pid to std.process Pid Partial Fix Issue 16615 - convert os pid to std.process Pid Jun 6, 2017
@edi33416 edi33416 force-pushed the process_enhancement branch from 560e2c8 to 0ee3059 Compare June 6, 2017 18:27
Copy link
Member

@andralex andralex left a comment

Choose a reason for hiding this comment

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

Please fix the nits

version (Windows)
{
/**
A simple factory that returns a $(LREF Pid) handle when provided with a
Copy link
Member

Choose a reason for hiding this comment

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

LREF is deprecated, please use REF e.g. $(REF Pid, std, process).

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be new to me.
LREF = local reference (within the module), only XREF was deprecated.

Copy link
Member

Choose a reason for hiding this comment

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

rox

std/process.d Outdated
A simple factory that returns a $(LREF Pid) handle when provided with a
global `pid`. This handle can be used to kill processes that haven't been
spawned by this process.
`Note:` this does not check if the provided `pid` is valid and corresponds
Copy link
Member

Choose a reason for hiding this comment

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

Note should not be in code font.

Copy link
Member

Choose a reason for hiding this comment

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

s/if/whether/


(Windows specific) Throws:
$(LREF ProcessException) on failure.
*/
Copy link
Member

Choose a reason for hiding this comment

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

FYI we currently only generate documentation on Posix, so this doc won't appear anywhere. At some point we should get things going by running wine to build windows dox...

std/process.d Outdated
pid = a process id

Returns: an instance to a `Pid` that corresponds to the pid within the
operating system, or, on Windows, throws `ProcessException` if opening the
Copy link
Member

Choose a reason for hiding this comment

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

The info about throwing should be in a separate section:

Throws: this and that and the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the throws part all together since it is Windows specific.

@edi33416 edi33416 force-pushed the process_enhancement branch from 44fc785 to 71ca4c5 Compare June 7, 2017 22:27
@dlang-bot dlang-bot merged commit 15a5a48 into dlang:master Jun 8, 2017
Copy link
Member

@CyberShadow CyberShadow left a comment

Choose a reason for hiding this comment

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

Please amend.


@system unittest // Pid.fromSystemPid()
{
version (Windows) TestScript prog = "do { timeout 1 } while (1)";
Copy link
Member

Choose a reason for hiding this comment

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

What the heck is this syntax? It's definitely not .cmd syntax. Is this a broken test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's powershell syntax

Copy link
Member

Choose a reason for hiding this comment

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

Well, it's not being executed as a PowerShell script. Look at the implementation of TestScript. The string is saved to a .cmd file. In addition to that, I don't think you can just CreateProcess / spawnProcess a .cmd etc. file on Windows. Windows does not have shebangs, associations (incl. .cmd) are handled by userspace (the shell) in a layer above CreateProcess, so you must spawn the command interpreter explicitly.

Copy link
Member

Choose a reason for hiding this comment

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

In addition to fixing the script, you need to find why your test was broken (in that it allowed a broken script to work and pass the test).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it's not being executed as a PowerShell script. Look at the implementation of TestScript. The string is saved to a .cmd file. [..]

I have missed that. I just assumed that if the syntax was invalid, auto tester would complain about it.

In addition to fixing the script, you need to find why your test was broken (in that it allowed a broken script to work and pass the test).

Again, I just assumed everything was ok since the tester did not complain about it.
Any suggestions on how to approach the issue and find out why the tester is not complaining?

Copy link
Member

Choose a reason for hiding this comment

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

Any suggestions on how to approach the issue and find out why the tester is not complaining?

You need to find why your test is broken. You might be missing a check which ensures that everything has gone as expected. If you need more time for this, I suggest we revert the PR for now.

Copy link
Member

Choose a reason for hiding this comment

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

Try inserting Thread.sleep(1.seconds) before the kill. I'm guessing your test is killing the process before the OS figures out the test script is invalid, in which case it's at least a race condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Try inserting Thread.sleep(1.seconds) before the kill. I'm guessing your test is killing the process before the OS figures out the test script is invalid, in which case it's at least a race condition.

Great observation! I totally missed that this could happen.


Throws: $(LREF ProcessException) if opening the process failed; this is
Windows specific.
*/
Copy link
Member

Choose a reason for hiding this comment

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

Do not have different documentation blocks for different platforms. Most users will never see the Windows one. Instead, have a look at version(StdDDoc).

Copy link
Member

Choose a reason for hiding this comment

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

(or just move the version blocks from outside the function body to inside it, so there's only one declaration)


Throws: $(LREF ProcessException) if opening the process failed; this is
Windows specific.
*/
Copy link
Member

Choose a reason for hiding this comment

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

This, or the wait documentation, should mention that wait-ing on a Pid that does not belong to a child (i.e. not created by spawnProcess etc.) is invalid and is not going to work.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, it seems like there's more to it - currently wait uses asserts to verify the validity of the Pid. If non-child PIDs becomes part of its API, I think those checks need to be made explicit.

Copy link
Member

Choose a reason for hiding this comment

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

yah that would need to change if we allow non-child pids

@CyberShadow
Copy link
Member

@andralex Please ping me or Steven or Lars if you need something reviewed regarding std.file or std.process.

@CyberShadow
Copy link
Member

CyberShadow commented Jun 8, 2017

So, question: do we actually need this in Phobos? It is certainly a simple enough addition, but:

std.process.Pid was created not to encapsulate the PID of any process, but only child processes created by spawnProcess et al. For instance, on Windows it has a _handle field which you only get when creating the process (or "opening" it manually). So, for one thing, wait(Pid) is now slightly changed in meaning: whereas before it was implied that its parameter could only represent a child process's PID, this is now no longer true, and the underlying primitive won't work with other Pids.

Another question is where do we want to draw the line for general process management. Killing arbitrary processes is the immediate benefit of this change. However, do we also want to have things such as process enumeration, renicing, reading/writing process memory etc.?

@edi33416
Copy link
Contributor Author

edi33416 commented Jun 8, 2017

So, question: do we actually need this in Phobos? It is certainly a simple enough addition, but:

I think we should have this in Phobos, as, imho, this expands the functionality of std.process quite a lot: now you could write a task manager using D (right?)

std.process.Pid was created not to encapsulate the PID of any process, but only child processes created by spawnProcess et al. For instance, on Windows it has a _handle field which you only get when creating the process (or "opening" it manually). So, for one thing, wait(Pid) is now slightly changed in meaning: whereas before it was implied that its parameter could only represent a child process's PID, this is now no longer true, and the underlying primitive won't work with other Pids.

I think wait should only be allowed for processes that were created using spawnProcess, so this might need some tweaking.

Another question is where do we want to draw the line for general process management. Killing arbitrary processes is the immediate benefit of this change. However, do we also want to have things such as process enumeration, renicing, reading/writing process memory etc.?

On the top of my head, probably only wait and anonymous pipes shouldn't be allowed, since these depend on process relationship. Everything else, imo, should work; we are a systems programming language after all.

@edi33416
Copy link
Contributor Author

edi33416 commented Jun 9, 2017

@CyberShadow what do you think?

@CyberShadow
Copy link
Member

Everything else, imo, should work; we are a systems programming language after all.

I don't think that argument makes sense (you can say it about a lot of useless or under-designed proposals); otherwise I can review a follow-up PR, but personally I would like more opinions on whether this functionality is necessary in Phobos. As I see it allowing Pid to represent non-child processes is shoe-horning functionality into it it was never meant to have, with the _handle field being the smoking gun.

@andralex
Copy link
Member

andralex commented Jun 9, 2017

Few questions:

So now we have wait(pid) work the same for children pids, but not as one might expect for pids obtained with fromSystemPid, correct?

Is the call wait(pid) for "bad" pids unsafe/liable to cause immediate abort etc? What will be the error case - throw, error code, ...?

Do other useful functions of Pid apply to pids obtained with fromSystemPid, i.e. what are the good uses counterbalancing the bad use?

Thanks.

@CyberShadow
Copy link
Member

Is the call wait(pid) for "bad" pids unsafe/liable to cause immediate abort etc?

Right now there is an assert in the Windows implementation of wait, I've requested that it's changed to an explicit check above.

Do other useful functions of Pid apply to pids obtained with fromSystemPid, i.e. what are the good uses counterbalancing the bad use?

AFAIU, only kill (which is why I'm not sure about this addtition).

@andralex
Copy link
Member

andralex commented Jun 9, 2017

So that puts as at 50/50. Assuming we add more operations on pids, are the majority of those likely to work uniformly for child and non-child processes? If so, adding is good. Are the majority of those likely to NOT work the same for child and non-child processes? Then let's not add.

Is there a possibility to fill _handle appropriately on non-child processes?

(My cringe: Pid is actually a GC class instead of a refcounted struct)

@CyberShadow
Copy link
Member

Assuming we add more operations on pids

I think we need some concrete proposals of what more could be added, and I can't think of any that would belong in Phobos - as I wrote above: "However, do we also want to have things such as process enumeration, renicing, reading/writing process memory etc.?"

Is there a possibility to fill _handle appropriately on non-child processes?

Actually it looks like I misread the patch - the added function does fill it, as a handle is needed to kill a process, however for doing so it adds an optional parameter (desiredAccess). Which raises a few questions, like:

  1. We are already straying into platform-dependent APIs in that the user would need to import the Windows API modules to use the appropriate process access flags. It's already bad enough that kill's parameter means completely different things on Windows and POSIX (exit code and signal respectively).
  2. If the only supported operation on a non-child Pid is killing it, why even allow users to customize which access to open the process with?
  3. By now it's already clear that Pid is a misnomer, since (esp. on Windows) it does not represent a process ID, but a process handle through which a child can be killed and waited on, but this further pushes the issue by having the Windows implementation opening a handle to the process on construction.

@CyberShadow
Copy link
Member

Especially considering the problem with kill's API, and given that killing processes is the only thing you can do with a non-child PID, I'm now convinced that this is not something Phobos needs to bother with. You are going to have to go into platform-specific territory anyway with kill's parameter, so considering all the desiredAccess ugliness this adds very little value.

@schveiguy
Copy link
Member

schveiguy commented Jun 9, 2017

We have discussed this kind of change before. Killing a pid with the OS API is pretty simple, we could probably wrap that into a single function.

The std.process API generally is for creating processes and handling the output/results. When you tack on managing processes that you didn't create, it's almost a separate API. It would belong in std.process, but not in the existing Pid type.

Things that you can do with a process id should dictate where our OS-agnostic API should go. We can get pretty far into the weeds on Windows, but Unix capabilties are pretty straightforward (if you don't count /proc/<pid> on Linux).

A very simple addition would just be a kill function that does the right thing depending on OS. Maybe nice as well.

@andralex
Copy link
Member

andralex commented Jun 9, 2017

Actually it looks like I misread the patch

Was about to point out the same. Then why was the revert pulled before this concluded?

A larger point - how can we reduce the churn? This whole discussion ideally would have taken place in the issue.

@CyberShadow
Copy link
Member

Then why was the revert pulled before this concluded?

Because it's obvious there are several non-trivial issues here, the implementation needs improvement anyway, and more time is needed to settle them.

A larger point - how can we reduce the churn? This whole discussion ideally would have taken place in the issue.

Sorry about that - I haven't seen the original issue or the PR when it was created. I've subscribed to all PRs/issues after DConf but anything that happened before then wasn't on my radar.

@schveiguy
Copy link
Member

how can we reduce the churn?

Just speaking for myself -- I haven't had tons of time to read through all the bug reports, so I have lost track of which ones would be good for me to review. Also my list of unread emails from github is hovering over 4000. I just happened to click on this one when it was brought to the top in a few spare minutes I had.

The best advice is if there is a section of the runtime/library that is pertinent to a particular person, ping that person either via github in the PR or via bugzilla by subscribing them to that bug. Otherwise, sometimes life gets in the way, and that person may miss it until later. I personally don't have a lot of traffic from bugzilla, so I would see that more likely, and anything that comes from github goes into a bucket on my email except if it has @schveiguy in it, then it goes in my inbox.

@CyberShadow
Copy link
Member

Was about to point out the same. Then why was the revert pulled before this concluded?

(Also, I proposed the revert specifically after I've re-read the patch, as I think the actual solution was not better than what I thought it was, and it made me realize additional problems with the proposal.)

@CyberShadow
Copy link
Member

and anything that comes from github goes into a bucket on my email except if it has @schveiguy in it, then it goes in my inbox

Same here. OT:

Shortly after DConf, I realized that my morning and evening routine consisted of at least half an hour of Reddit/HN (mainly reading stuff for the sake of it), and I noticed an opportunity for optimization: I've subscribed to all GitHub and Bugzilla notifications (sending those to a separate label) and replaced part of my Reddit time with looking through what accumulated there. Gmail's interface makes it very easy to blaze through threads that are not relevant, so picking out threads relevant to you (or where you can contribute something like triaging a low-effort bug report or bisecting a regression) becomes easy as well. Hopefully I can keep up this habit going forwards, and of course there is the temporary problem that situations like this happen where something shows up on your radar only after it's been merged.

@andralex
Copy link
Member

andralex commented Jun 9, 2017

Because it's obvious there are several non-trivial issues here, the implementation needs improvement anyway, and more time is needed to settle them.

There is time until the next release.

Because it's obvious there are several non-trivial issues here, the implementation needs improvement anyway, and more time is needed to settle them.

Could you please get into those?

@CyberShadow
Copy link
Member

CyberShadow commented Jun 9, 2017

Could you please get into those?

I did, and barring additional arguments my conclusion so far is that this is not something that should be in Phobos. See also Steven's reply on the PR - we could have a kill(int pid, int signalOrExitCode) instead, but at this point this needs a rationale of what the submitter is trying to achieve.

@CyberShadow
Copy link
Member

There is time until the next release.

I think the stable branch can be forked earlier than the release for RCs and such. Either way, I think it's better to not rely on a calendar and revert early regardless (and lest the revert be forgotten or get stuck in analysis paralysis) - better safe than sorry.

@andralex
Copy link
Member

andralex commented Jun 9, 2017

@CyberShadow could you please suggest a step forward for https://issues.dlang.org/show_bug.cgi?id=16615? Thanks.

@CyberShadow
Copy link
Member

WONTFIX. Although it might not be obvious, killing a process is inherently a very platform specific operation. There is and cannot be an obviously correct way to do it on both platforms (Windows and POSIX), because the two platforms manage processes in different way. Windows does not have signals; POSIX does not allow specifying exit codes; and reparenting is done differently on both. Furthermore, the example use case presented in the issue (killing a process tree) is not possible to correctly implement with just kill - you must suspend the entire process group, and only then kill them, and I'm not sure that even that is sufficient. I would advise against attempting to do it in general, and instead run the process group in a container and kill it wholesale.

@andralex
Copy link
Member

andralex commented Jun 9, 2017

@CyberShadow OK, please put WONTFIX on it with explanation.

@CyberShadow
Copy link
Member

Done

@quickfur
Copy link
Member

quickfur commented Jun 9, 2017

IMO, it's OK to add a function to return the OS pid from a Pid object in Phobos. However, going the other way is a bad idea, for the reasons discussed above.

Writing a task manager in D implies knowledge of OS-specific implementation details, and due to differences in Windows vs. Posix, I don't think there is any platform-agnostic way to do this. I.e., I don't think this should be used as a reason for allowing arbitrary conversion of OS pids to std.process Pid objects.

@andralex
Copy link
Member

andralex commented Jun 9, 2017

Thanks to all!

@schveiguy
Copy link
Member

kill(int pid, int signalOrExitCode)

I was thinking more along the lines of killProcess(int pid), which just does the same thing kill pid does in Unix, and closing the process does in Windows. Trying to handle all the nuances of the OS options, as you say, isn't possible in an OS-agnostic fashion. In that case we can just defer to the exposed C functions instead.

Even that simple wrapper may be more fluff than substance, as the C functions are pretty straightforward.

@CyberShadow
Copy link
Member

which just does the same thing kill pid does in Unix

Yeah but kill sends SIGTERM by default, which might not be what the user intends. Windows does not have an equivalent of SIGTERM.

and closing the process does in Windows

You mean from Task Manager?

@quickfur
Copy link
Member

quickfur commented Jun 9, 2017

Haha, @CyberShadow has a good point, kill sends SIGTERM by default, but processes may ignore it and/or do something else if they've installed their own signal handler. I'm not sure there's any common process-terminating operation that has common semantics across Posix and Windows.

@schveiguy
Copy link
Member

I mean do the thing that people do on that os. Yes sigterm on unix, task manager closing (or force closing) on windows. Maybe a bool flag for force closing which switches to sigkill and force close.

@CyberShadow
Copy link
Member

How do you propose to implement non-force-closing on Windows?

@schveiguy
Copy link
Member

Not sure. I searched online for a mechanism but can't find it.

@edi33416 edi33416 deleted the process_enhancement branch October 9, 2017 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants