I am looking for help interpreting the standard for a shell job
control scenario.

SIGTTIN and SIGTTOU signals are generated by the controlling terminal
when a process in a background process group attempts IO. Achieving
this requires two actions:

1. Determine that the process belongs to a background process group
2. Sending a signal to the process

Does the standard imply, or intend, that the pair of actions is
applied atomically?


For example consider the following timeline for background process P,
and shell process S:

P0. Process P reads from the controlling terminal.
P1. The terminal determines that process A to belongs to a
    background process group.

S1. Process S uses tcsetpgrp() to place Process P into a
    foreground process group.
S2. Process S uses killpg() to send SIGCONT to the foreground
    process group of Process P.

P2. The terminal sends SIGTTIN to the process group of Process P.



This suggests the following outcomes:

* If P1+P2 is atomic, then SIGTTIN becomes pending before SIGCONT,
  and SIGTTIN is discarded with SIGCONT delivered to process P.

* If P1+P2 is not atomic, then SIGCONT becomes pending before SIGTTIN,
  and SIGCONT is discarded with SIGTTIN delivered to process P.



A consequence of non-atomic operation is that there is no
straightforward way for a job control shell to bring a running
background process to the foreground reliably. The prospective SIGCONT
issued by the shell, required to compensate for tcsetpgrp() racing the
background group check, races delivery of the SIGTTIN.


In the absence of atomic operation, a candidate algorithm might be for
the shell to first issue SIGSTOP, wait for all background group members
to either terminate or stop, use tcsetpgrp() to set the foreground
process group, and then issue SIGCONT. This algorithm has the
side-effect of disrupting the execution of the running processes, and
might be generally unacceptable for this reason.



Sending SIGTTIN and SIGTTOU is described by the following:

https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap11.html

> Any attempts by a process in a background process group to read from
> its controlling terminal cause its process group to be sent a SIGTTIN
> signal unless one of the following special cases applies:
> ...
> Attempts by a process in a background process group to write to its
> controlling terminal shall cause the process group to be sent SIGTTOU
> signal unless one of the following special cases applies:
> ...



Sending a signal is described by the following:

https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_01

> A signal is said to be "generated" for (or sent to) a process or
> thread when the event that causes the signal first occurs.
> ...
> During the time between the generation of a signal and its delivery
> or acceptance, the signal is said to be "pending".
> ...
> When any stop signal (SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU) is generated
> for a process or thread, all pending SIGCONT signals for that process
> or any of the threads within that process shall be discarded.
> Conversely, when SIGCONT is generated for a process or thread, all
> pending stop signals for that process or any of the threads within
> that process shall be discarded.


A brief census of present implementations shows the following:

* FreeBSD - Atomic - SIGTTIN vs TIOSPGRP

  https://github.com/freebsd/freebsd-src/blob/main/sys/kern/tty.c#L431-L493

https://github.com/freebsd/freebsd-src/blob/main/sys/kern/tty.c#L1878-L1893

* OpenBSD - Not atomic - SIGTTIN vs TIOSPGRP

  https://github.com/openbsd/src/blob/master/sys/kern/tty.c#L1762-L1774
  https://github.com/openbsd/src/blob/master/sys/kern/tty.c#L1012-L1023

* Linux - Not atomic - SIGTTIN vs tiocspgrp()


https://github.com/torvalds/linux/blob/master/drivers/tty/tty_jobctrl.c#L42-L61

https://github.com/torvalds/linux/blob/master/drivers/tty/tty_jobctrl.c#L504-L525


Earl

Reply via email to