lib/gis: Return the child exit status from G_popen_close - #7847
Conversation
|
@wenzeslaus taking this out of draft, since the two questions I asked on #7735 are easier to answer against code than in the abstract, and leaving it as a draft mostly means nobody looks. The defaults it ships with are |
|
Thanks — I checked both, and it makes the guard more important than I had treated it.
That matters here because It also answers the question the other way round from how I framed it in the description. I justified On the range: One thing I noticed while checking, which is outside this PR: |
|
Not a maintainer, but you asked for reactions and this has been sitting a while. On the API change: there are 32 call sites of On the sentinel: the comment says
I have not built this, so the Windows bit is read from source, not reproduced. |
|
Thanks — the Windows point is a real error in the patch, and I checked all three rather than taking them on trust. The sentinel. You are right, and it is worse than you put it. So So I have kept The 32 call sites. Confirmed — 18 files outside No assignment, no condition, no The docblock. Also agreed. A second close returning Appreciate the review — the Windows cast is not something I would have found by reading the POSIX branch, which is all I had been looking at. |
dc3e277 to
a134015
Compare
|
Let us keep this as simple as possible. What we want here is Lines 974 to 975 in 55de52a A caller code could look like: int res = G_popen_close(&child);
if (res != EXIT_SUCCESS)
G_fatal_error(_("child process failed with code: %d"), res);(should have linked to posix wait for a more comprehensive description) |
|
See #7912 |
a134015 to
bb2ff5d
Compare
|
Agreed — simplified to exactly that. int G_popen_close(struct Popen *state)
{
int status = -1;
if (state->fp) {
fclose(state->fp);
state->fp = NULL;
}
if (state->pid != -1) {
status = G_wait(state->pid);
state->pid = -1;
}
return status;
}Your caller shape works unchanged, and it also disposes of the Windows problem @Valyrian-Code found — with no sentinel there is nothing for an exit code to collide with. Net -18 lines, +3. One thing I have kept, and I want to flag it rather than have it look like leftover defensiveness: the Thanks for #7912 — the missing |
There was a problem hiding this comment.
I suggest a more concrete description of the function, and clearing up state with G_popen_clear. One important difference is the default return value of 0. A state with state->pid != -1 is already a very defensive guard. G_popen_* functions both return state with that value if G_spawn_ex fails, and returns NULL, which must be checked long before closing the pipe with G_popen_close. Let us keep this as close as possible to the original void functionality.
If the review suggestion snippets are difficult to follow, here is my suggestion as a whole:
int G_popen_close(struct Popen *state)
{
int status = 0;
if (state->fp)
fclose(state->fp);
if (state->pid != -1)
status = G_wait(state->pid);
G_popen_clear(state);
return status;
}G_popen_close() waited for the child but dropped G_wait()'s value, so callers could not tell whether the child succeeded. That is one of the links that made OSGeo#7734 silent. The value is passed through unchanged, so a caller can treat anything other than EXIT_SUCCESS as a failure. A state with no process returns 0, which keeps the function as close as possible to its previous void behaviour: G_popen_read() and G_popen_write() already return NULL when G_spawn_ex() fails, and that must be checked long before the pipe is closed. G_popen_clear() resets the state afterwards, so a second close does not wait again. All 18 files outside popen.c that call G_popen_close discard the value, so widening the return type from void to int breaks nothing in tree. Signed-off-by: sai pranav <rajasaipranav0@gmail.com>
bb2ff5d to
7b6518d
Compare
|
Taken as written — your version is in, docblock included, and the branch is rebased onto current Your reasoning on the default settles the point I had been arguing from the wrong end. I was treating a state with
The net change is now 15 insertions and 19 deletions, so the file is shorter than before. |
|
@nilason thanks for the review and the rewrite — it is a better shape than what I had. This is approved with CI green, so I think it just needs someone with the button. Flagging it rather than because I am in a hurry with it: new contributors here are limited to five open non-draft PRs and I am at that limit, so #7853 is sitting as a draft waiting for a slot, as is the testing-guide section @wenzeslaus asked for in #7862. Merging this frees one. No rush if there is a reason to hold it. |
Fixes #7735. Draft — I asked two contract questions on the issue and haven't heard back, so this is a concrete proposal to react to rather than a settled design. Happy to change either decision.
G_popen_close()waited for the child but droppedG_wait()'s value, so callers could not tell whether the child succeeded. That is one of the links that made #7734 silent.The two decisions I made
1. What to return when there is no child. You asked for "something distinguishable", and a plain
-1would not be:G_wait()already returns-1whenwaitpidfails, and returns small positives forWTERMSIG. So this addsG_POPEN_NO_CHILD(-2), which is outside the setG_wait()can produce, next tostruct Popeningis.h.2. Raw vs decoded status. This passes
G_wait()'s value straight through, so it is consistent with the rest of the library — decodedWEXITSTATUS/WTERMSIGon POSIX, the process exit code on Windows. It is therefore not interchangeable with apclose()result, which is raw. I preferred consistency withG_wait(); say the word if you would rather it matchpclose().One thing I added beyond the issue
fpandpidare cleared once handled. Without that, a secondG_popen_close()on the same state waits on an already-reaped pid, gets-1fromwaitpid, and reports a failure for a pipe that closed fine. That only became observable once the value was returned, so it seemed to belong here — but I am happy to drop it if you would rather keep the diff to the return value alone.Compatibility
All 33 call sites of
G_popen_close()outsidepopen.cignore the return value today, sovoid→intdoes not require touching any of them. A full build shows no errors.Verification
Exercised through the real API against a built library:
clang-formatreports no changes.I used an AI assistant while preparing this. I understand the change and can explain it.
Source compatibility of the void -> int change
Verified:
G_popen_closehas call sites in 18 files outsidepopen.cand the header —lib/gis/pager.c,raster/r.mode,r.rescale,r.rescale.eq,r.coin,r.topmodeland the elevenraster/r.statistics/o_*.c— and every one discards the value. No assignment, no use in a condition, noreturn. So widening the return type breaks nothing in tree.Thanks to @Valyrian-Code for prompting both this and the Windows correction below.