diff mbox series

[2/5] pager: do not close fd 2 unnecessarily

Message ID 80f15223-246e-4cfb-a139-e47af829c938@gmail.com (mailing list archive)
State Superseded
Headers show
Series use the pager in 'add -p' | expand

Commit Message

Rubén Justo May 19, 2024, 7:12 a.m. UTC
We send errors to the pager since 61b80509e3 (sending errors to stdout
under $PAGER, 2008-02-16).

In a8335024c2 (pager: do not dup2 stderr if it is already redirected,
2008-12-15) an exception was introduced to avoid redirecting stderr if
it is not connected to a terminal.

In such exceptional cases, the close(STDERR_FILENO) we're doing in
close_pager_fds, is unnecessary.

Furthermore, in a subsequent commit we're going to introduce changes
that might call close_pager_fds multiple times.  With this in mind,
unconditionally closing stderr will become undesirable.

Let's close(STDERR_FILENO) only when necessary, and pave the way for
the coming changes.

Signed-off-by: Rubén Justo <rjusto@gmail.com>
---
 pager.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

Comments

Junio C Hamano May 20, 2024, 7:14 p.m. UTC | #1
Rubén Justo <rjusto@gmail.com> writes:

> We send errors to the pager since 61b80509e3 (sending errors to stdout
> under $PAGER, 2008-02-16).
>
> In a8335024c2 (pager: do not dup2 stderr if it is already redirected,
> 2008-12-15) an exception was introduced to avoid redirecting stderr if
> it is not connected to a terminal.
>
> In such exceptional cases, the close(STDERR_FILENO) we're doing in
> close_pager_fds, is unnecessary.


> Furthermore, in a subsequent commit we're going to introduce changes
> that might call close_pager_fds multiple times.  With this in mind,
> unconditionally closing stderr will become undesirable.

In a new world with such a change, what does it mean to call
close_pager_fds()?  It used to mean "we are really done with the
pager and we no longer need them, ever".

And we still call the helper for that purpose after this change,
from wait_for_pager_atexit() and wait_for_pager_signal().

So no matter what "a subsequent commit" does, it feels conceptually
wrong to call it more than once in the first place.  In other words,
what is wrong is that this function closes stderr, but "a subsequent
commit" calls this function multiple times, no?

>  static struct child_process pager_process;
>  static const char *pager_program;
> +static int old_fd2 = -1;

What does the magic number "-1" mean?  We often use it to signal
"uninitialized", but then what are concrete "initialized" values
mean?  "We dup2()'ed something else to stderr/fd #2 but before doing
so we saved the original fd #2 away to this variable, so that we can
restore fd #2 by another dup2() of the value of this variable when
we declare that we are done with the standard error stream"?

But that does not look like what is happening here.

>  /* Is the value coming back from term_columns() just a guess? */
>  static int term_columns_guessed;
> @@ -23,7 +24,8 @@ static void close_pager_fds(void)
>  {
>  	/* signal EOF to pager */
>  	close(1);
> -	close(2);
> +	if (old_fd2 != -1)
> +		close(2);
>  }
>  
>  static void wait_for_pager_atexit(void)
> @@ -141,8 +143,10 @@ void setup_pager(void)
>  
>  	/* original process continues, but writes to the pipe */
>  	dup2(pager_process.in, 1);
> -	if (isatty(2))
> +	if (isatty(2)) {
> +		old_fd2 = 1;

Equally unclear magic number "1" is used here.

This value is different from pager_process.in, and my earlier "we
are saving away" does not apply, either.

>  		dup2(pager_process.in, 2);
> +	}
>  	close(pager_process.in);

Puzzled...
Rubén Justo May 20, 2024, 10:33 p.m. UTC | #2
On Mon, May 20, 2024 at 12:14:04PM -0700, Junio C Hamano wrote:
> Rubén Justo <rjusto@gmail.com> writes:
> 
> > We send errors to the pager since 61b80509e3 (sending errors to stdout
> > under $PAGER, 2008-02-16).
> >
> > In a8335024c2 (pager: do not dup2 stderr if it is already redirected,
> > 2008-12-15) an exception was introduced to avoid redirecting stderr if
> > it is not connected to a terminal.
> >
> > In such exceptional cases, the close(STDERR_FILENO) we're doing in
> > close_pager_fds, is unnecessary.
> 
> 
> > Furthermore, in a subsequent commit we're going to introduce changes
> > that might call close_pager_fds multiple times.  With this in mind,
> > unconditionally closing stderr will become undesirable.
> 
> In a new world with such a change, what does it mean to call
> close_pager_fds()?

There is no change in what calling close_pager_fds() means.

> It used to mean "we are really done with the
> pager and we no longer need them, ever".
> 
> And we still call the helper for that purpose after this change,
> from wait_for_pager_atexit() and wait_for_pager_signal().

Yes, no change here either.  In the next commit, a new client of the
helper is introduced, the new API: wait_for_pager().

> 
> So no matter what "a subsequent commit" does, it feels conceptually
> wrong to call it more than once in the first place.
> In other words,
> what is wrong is that this function closes stderr, but "a subsequent
> commit" calls this function multiple times, no?

This series is trying to allow triggering the pager multiple times.
Reaching to close_pager_fds() multiple times is a consequence of it.

> 
> >  static struct child_process pager_process;
> >  static const char *pager_program;
> > +static int old_fd2 = -1;
> 
> What does the magic number "-1" mean?

Invalid fd.

> We often use it to signal
> "uninitialized", but then what are concrete "initialized" values
> mean?  "We dup2()'ed something else to stderr/fd #2 but before doing
> so we saved the original fd #2 away to this variable, so that we can
> restore fd #2 by another dup2() of the value of this variable when
> we declare that we are done with the standard error stream"?
> 
> But that does not look like what is happening here.
> 
> >  /* Is the value coming back from term_columns() just a guess? */
> >  static int term_columns_guessed;
> > @@ -23,7 +24,8 @@ static void close_pager_fds(void)
> >  {
> >  	/* signal EOF to pager */
> >  	close(1);
> > -	close(2);
> > +	if (old_fd2 != -1)
> > +		close(2);
> >  }
> >  
> >  static void wait_for_pager_atexit(void)
> > @@ -141,8 +143,10 @@ void setup_pager(void)
> >  
> >  	/* original process continues, but writes to the pipe */
> >  	dup2(pager_process.in, 1);
> > -	if (isatty(2))
> > +	if (isatty(2)) {
> > +		old_fd2 = 1;
> 
> Equally unclear magic number "1" is used here.
> 
> This value is different from pager_process.in, and my earlier "we
> are saving away" does not apply, either.

It applies, in 3/5.

> 
> >  		dup2(pager_process.in, 2);
> > +	}
> >  	close(pager_process.in);
> 
> Puzzled...

Thanks for reading the series.
Junio C Hamano May 21, 2024, 8:57 p.m. UTC | #3
Rubén Justo <rjusto@gmail.com> writes:

>> >  static struct child_process pager_process;
>> >  static const char *pager_program;
>> > +static int old_fd2 = -1;
>> 
>> What does the magic number "-1" mean?
>
> Invalid fd.
>
>> We often use it to signal
>> "uninitialized", but then what are concrete "initialized" values
>> mean?  "We dup2()'ed something else to stderr/fd #2 but before doing
>> so we saved the original fd #2 away to this variable, so that we can
>> restore fd #2 by another dup2() of the value of this variable when
>> we declare that we are done with the standard error stream"?
>> 
>> But that does not look like what is happening here.
>>  ....
>> Equally unclear magic number "1" is used here.
>> 
>> This value is different from pager_process.in, and my earlier "we
>> are saving away" does not apply, either.
>
> It applies, in 3/5.

We need to be prepared to see a series chomped at an early stage and
it should still make sense.  If the series does not make sense when
you stop before applying patch 3, it is a strong sign that this step
and the next step can be separated and structured better.

Or perhaps if they are made into a single patch it makes more sense
and becomes easier to explain?
Rubén Justo May 21, 2024, 9:35 p.m. UTC | #4
On Tue, May 21, 2024 at 01:57:19PM -0700, Junio C Hamano wrote:
> Rubén Justo <rjusto@gmail.com> writes:
> 
> >> >  static struct child_process pager_process;
> >> >  static const char *pager_program;
> >> > +static int old_fd2 = -1;
> >> 
> >> What does the magic number "-1" mean?
> >
> > Invalid fd.
> >
> >> We often use it to signal
> >> "uninitialized", but then what are concrete "initialized" values
> >> mean?  "We dup2()'ed something else to stderr/fd #2 but before doing
> >> so we saved the original fd #2 away to this variable, so that we can
> >> restore fd #2 by another dup2() of the value of this variable when
> >> we declare that we are done with the standard error stream"?
> >> 
> >> But that does not look like what is happening here.
> >>  ....
> >> Equally unclear magic number "1" is used here.
> >> 
> >> This value is different from pager_process.in, and my earlier "we
> >> are saving away" does not apply, either.
> >
> > It applies, in 3/5.
> 
> We need to be prepared to see a series chomped at an early stage and
> it should still make sense.  If the series does not make sense when
> you stop before applying patch 3, it is a strong sign that this step
> and the next step can be separated and structured better.
> 
> Or perhaps if they are made into a single patch it makes more sense
> and becomes easier to explain?
> 

Adding logic to adjust when we close(stderr) in close_pager_fds() makes
sense on its own, I think.

And, the values for the flag "do-we-want-to-close-stderr-at-exit", too,
to me.

I am happy with the series;  the 'P' command introduced in v2 is a good
improvement.  Combining 2/5 and 3/5, I think it is not a good idea.

Therefore, I'm not sure how to alleviate the puzzling.
Junio C Hamano May 21, 2024, 10 p.m. UTC | #5
Rubén Justo <rjusto@gmail.com> writes:

> Adding logic to adjust when we close(stderr) in close_pager_fds() makes
> sense on its own, I think.

The feature may be.

> And, the values for the flag "do-we-want-to-close-stderr-at-exit", too,
> to me.

But the thing is, the flag is *NOT* named as such, and an
undocumented "value -1 means X, while value 1 means Y", do not make
any sense, either.
Rubén Justo May 22, 2024, 5:19 p.m. UTC | #6
On Tue, May 21, 2024 at 03:00:10PM -0700, Junio C Hamano wrote:
> Rubén Justo <rjusto@gmail.com> writes:
> 
> > Adding logic to adjust when we close(stderr) in close_pager_fds() makes
> > sense on its own, I think.
> 
> The feature may be.
> 
> > And, the values for the flag "do-we-want-to-close-stderr-at-exit", too,
> > to me.
> 
> But the thing is, the flag is *NOT* named as such, and an
> undocumented "value -1 means X, while value 1 means Y", do not make
> any sense, either

Perhaps this makes more sense?:

1:  8fe915a820 ! 1:  70cc34efc4 pager: do not close fd 2 unnecessarily
    @@ pager.c: int pager_use_color = 1;
      
      static struct child_process pager_process;
      static const char *pager_program;
    -+static int old_fd2 = -1;
    ++static int old_fd2;
      
      /* Is the value coming back from term_columns() just a guess? */
      static int term_columns_guessed;
    @@ pager.c: static void close_pager_fds(void)
      	/* signal EOF to pager */
      	close(1);
     -	close(2);
    -+	if (old_fd2 != -1)
    ++	if (old_fd2)
     +		close(2);
      }
Junio C Hamano May 22, 2024, 5:40 p.m. UTC | #7
Rubén Justo <rjusto@gmail.com> writes:

> Perhaps this makes more sense?:
>
> 1:  8fe915a820 ! 1:  70cc34efc4 pager: do not close fd 2 unnecessarily
>     @@ pager.c: int pager_use_color = 1;
>       
>       static struct child_process pager_process;
>       static const char *pager_program;
>     -+static int old_fd2 = -1;
>     ++static int old_fd2;
>       
>       /* Is the value coming back from term_columns() just a guess? */
>       static int term_columns_guessed;
>     @@ pager.c: static void close_pager_fds(void)
>       	/* signal EOF to pager */
>       	close(1);
>      -	close(2);
>     -+	if (old_fd2 != -1)
>     ++	if (old_fd2)
>      +		close(2);
>       }

Not really.  The name "old_fd2" strongly implies "where did fd#2
come from?" and it did not come from fd#0, did it?  Until [3/5]
this variable used to mean something different from "this was the
saved fd#2 we can use to restore it later", which is the name
"old_fd2" clearly wants to stand for.

If you really want to have them as two separate patches, I would
expect the proposed log message for the [3/5] step to say something
like

    ... we added variable X to signal if we should close fd#2 in
    function F in the previous step.  As store away the original
    fd#2 with dup(2) to be restored later after we close() it, the
    question the previous step asked, "should we be the one closing
    fd#2?" becomes equivalent to "have we stored away the original
    fd#2 (in which case we close() fd#2 when we are done with the
    pager and restore the original one)?"  Rename X to old_fd2 and
    have it serve both purposes.

or somesuch.
Rubén Justo May 26, 2024, 6:48 a.m. UTC | #8
On Wed, May 22, 2024 at 10:40:18AM -0700, Junio C Hamano wrote:

> Not really.  The name "old_fd2" strongly implies "where did fd#2
> come from?" and it did not come from fd#0, did it?

Perhaps "close_fd2" is a better name?:

@@ pager.c: int pager_use_color = 1;
  
  static struct child_process pager_process;
  static const char *pager_program;
-+static int old_fd2 = -1;
++static int close_fd2;
  
  /* Is the value coming back from term_columns() just a guess? */
  static int term_columns_guessed;

@@ pager.c: static void close_pager_fds(void)
  	/* signal EOF to pager */
  	close(1);
 -	close(2);
-+	if (old_fd2 != -1)
++	if (close_fd2)
 +		close(2);
  }
Junio C Hamano May 26, 2024, 9:26 p.m. UTC | #9
Rubén Justo <rjusto@gmail.com> writes:

> On Wed, May 22, 2024 at 10:40:18AM -0700, Junio C Hamano wrote:
>
>> Not really.  The name "old_fd2" strongly implies "where did fd#2
>> come from?" and it did not come from fd#0, did it?
>
> Perhaps "close_fd2" is a better name?:
> @@ pager.c: static void close_pager_fds(void)
>   	/* signal EOF to pager */
>   	close(1);
>  -	close(2);
> -+	if (old_fd2 != -1)
> ++	if (close_fd2)
>  +		close(2);

That's a very straight-forward name that says what effect anybody
who assigns to the variable wants to see.
diff mbox series

Patch

diff --git a/pager.c b/pager.c
index b8822a9381..3ef6798f7e 100644
--- a/pager.c
+++ b/pager.c
@@ -14,6 +14,7 @@  int pager_use_color = 1;
 
 static struct child_process pager_process;
 static const char *pager_program;
+static int old_fd2 = -1;
 
 /* Is the value coming back from term_columns() just a guess? */
 static int term_columns_guessed;
@@ -23,7 +24,8 @@  static void close_pager_fds(void)
 {
 	/* signal EOF to pager */
 	close(1);
-	close(2);
+	if (old_fd2 != -1)
+		close(2);
 }
 
 static void wait_for_pager_atexit(void)
@@ -141,8 +143,10 @@  void setup_pager(void)
 
 	/* original process continues, but writes to the pipe */
 	dup2(pager_process.in, 1);
-	if (isatty(2))
+	if (isatty(2)) {
+		old_fd2 = 1;
 		dup2(pager_process.in, 2);
+	}
 	close(pager_process.in);
 
 	/* this makes sure that the parent terminates after the pager */