[comp.unix.questions] how to suspend a process

coleman@cam.nist.gov (Sean Sheridan Coleman X5672) (02/06/91)

In a program I am writing, I want to be able to catch SIGTSTP
but if I do that, the process will not get suspended. How can
I do what the shell would do under normal situation, suspend the
process? Is sigsuspend the way to go? 

How to I get my program to restart or do I need to concern
myself with this problem?



Thanks

Sean Coleman
coleman@bldrdoc.gov

rbj@uunet.UU.NET (Root Boy Jim) (02/06/91)

In article <7016@alpha.cam.nist.gov> coleman@cam.nist.gov (Sean Sheridan Coleman X5672) writes:
>In a program I am writing, I want to be able to catch SIGTSTP
>but if I do that, the process will not get suspended. How can
>I do what the shell would do under normal situation, suspend the
>process? Is sigsuspend the way to go? 

In your handler, you send yourself a SIGSTOP, which you cannot catch.
You could also unregister your handler, then send a SIGTSTP.

>How to I get my program to restart ...

Another process sends it a SIGCONT, most likely your shell as the
result of typing "fg" or "bg".

> ...or do I need to concern myself with this problem?

Well, yess and no. The tricky thing is figuring out what to do
before and after you're suspended. Typical things are:
save and restore tty modes, flush output files, repaint screen
after resuming. You will resume in your TSTP signal handler.

You may want to look at something like less to see what to do.

>Thanks
>
>Sean Coleman
>coleman@bldrdoc.gov

Are you in Boulder or Gaithersburg?
-- 

	Root Boy Jim Cottrell <rbj@uunet.uu.net>
	I got a head full of ideas
	They're driving me insane

dce@smsc.sony.com (David Elliott) (02/07/91)

In article <121503@uunet.UU.NET>, rbj@uunet.UU.NET (Root Boy Jim) writes:
|> In article <7016@alpha.cam.nist.gov> coleman@cam.nist.gov (Sean Sheridan Coleman X5672) writes:
|> >In a program I am writing, I want to be able to catch SIGTSTP
|> >but if I do that, the process will not get suspended. How can
|> >I do what the shell would do under normal situation, suspend the
|> >process? Is sigsuspend the way to go? 
|> 
|> In your handler, you send yourself a SIGSTOP, which you cannot catch.
|> You could also unregister your handler, then send a SIGTSTP.

It may be a trivial point, but it's nice to have the signal that
stops the program be the one that was intended to stop the
program.

If you send a SIGSTOP to a program, csh (and probably others) tell
you it was stopped by a signal.  If SIGTSTP is used, the output
from csh is different.

So, if you are stopping your program in response to some user input,
it looks better if SIGTSTP is the signal that does the stopping.

-- 
...David Elliott
...dce@smsc.sony.com | ...!{uunet,mips}!sonyusa!dce
...(408)944-4073
..."His lower lip waved poutily with defiance..."

torek@elf.ee.lbl.gov (Chris Torek) (02/18/91)

In article <121503@uunet.UU.NET> rbj@uunet.UU.NET (Root Boy Jim) writes:
>In your handler, you send yourself a SIGSTOP, which you cannot catch.
>You could also unregister your handler, then send a SIGTSTP.

The second is `better' aesthetically.

Handling stops correctly is actually quite tricky.  When you receive
a TSTP signal you may assume that all other processes in your process
group have also received one.  You must therefore not send a second
TSTP to everyone (kill(0, SIGTSTP)) but rather only to yourself
(kill(getpid(), SIGTSTP)).

To make the stop `atomic' (i.e., to prevent users with itchy ^Z fingers
from stopping you while you are setting up to be stopped) you should
(a) be using the `Berkeley signal' mechanism (likely to be available,
if not the default, given that you have SIGTSTP in the first place);
(b) use code of the form:

	stop_handler()
	{
		sigmask_t omask;

		... do cleanup operations ...
		/*
		 * SIGTSTP is currently blocked.
		 * To stop exactly once, send another TSTP in case
		 * none are pending.  (If one is pending, the kill()
		 * will still leave exactly one pending.)  Then
		 * atomically lower the mask and wait for the signal.
		 *
		 * Alternatively, we could use the sequence
		 *	sigsetmask(omask & ~sigmask(SIGTSTP));
		 *	sigsetmask(omask);
		 * which would let the signal in (thus stopping)
		 * then block the signal once we are resumed, but
		 * one call to sigpause is more efficient.
		 */
		omask = sigblock(0);
		(void) kill(getpid(), SIGTSTP);
		(void) sigpause(omask & ~sigmask(SIGTSTP));
		... do setup again ...
	}

The equivalent sequence of POSIX signal calls is:

	sigset_t none, tmp;

	(void) sigemptyset(&none);
	(void) sigprocmask(SIG_BLOCK, &none, &tmp);
	(void) sigdelset(&tmp, SIGTSTP);
	(void) sigsuspend(&tmp);

If the reason you are catching signals is that you modify a tty state
(e.g., use CBREAK/not-ICANON mode), you should be careful to pick up
the new state after the stop.  A number of programs, including `vi',
assume that the state they got when the first started is still in
effect, and the sequence:

	% stty erase ^h
	% vi
	:stop
	% stty erase ^_
	% fg
	:q
	%

leaves erase set to ^H rather than ^_.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (02/19/91)

In article <9999@dog.ee.lbl.gov> torek@elf.ee.lbl.gov (Chris Torek) writes:
  [ summary of one way to handle TSTP ]

Yeah. It's actually sort of funny watching programs which don't know how
to deal with this: give rn some ^Zs and bgs and it goes into never-never
land. Start it in the background and you're in really deep trouble.

But this begs the question of why the default signal handler operations
aren't available as atomic syscalls in the first place. stop(), tstp(),
ttin(), ttou(), and abrt() (preferably with a filename) are trivial to
add to the kernel, would eliminate the need for several signal-handling
kludges, and would make any character-mode program a lot cleaner.

> If the reason you are catching signals is that you modify a tty state
> (e.g., use CBREAK/not-ICANON mode), you should be careful to pick up
> the new state after the stop.

This is a matter of opinion. Some people believe that the shell should
handle all tty mode changes, including restoring modes after a stop or
termination. Then the shell alone can decide whether to pick up the new
state or not. This too would make the system a lot cleaner: programmers
would not have to repeat the same tty mode munging and signal handling
code in every application. Writing a no-echo, character-mode program
wouldn't require ioctl() or signal() or anything else.

(Of course, the programmer could just run pty -pEc foo to run foo under
a no-echo, character-mode pseudo-tty. Since pty groks job control, foo
doesn't need to handle signals either. But I won't pretend that this is
the right solution. It should not require an extra program and tty just
to do something that the shell should do automatically.)

---Dan

gwyn@smoke.brl.mil (Doug Gwyn) (02/19/91)

In article <10269:Feb1821:14:1591@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>This is a matter of opinion. Some people believe that the shell should
>handle all tty mode changes, including restoring modes after a stop or
>termination.

I would go further than this, and assert that an optimal design would
not require the switched task to be aware of task switching at all.