keypresses and signal handling

E

Eric Sosman

My interrupt handler sets a flag variable to 1 and the variable is
tested at key points in the program where the program can flush the
buffers and close all of the open files. As I understand signal
handling (which I admit is limited) if I am in a function that is
doing a malloc (I don't have realloc in my program) and I press Ctrl-C
during the malloc then my program jumps to my signal handler. My
signal handler sets my flag to 1 (flag is a global variable
initialized to 0, and the programs continues where it left off. So in
my case I should be safe. Do I understand this properly?

Yes, that's fine: The signal handler can safely set a flag
(for ultimate squeaky-cleanliness the flag should be of type
`volatile sig_atomic_t') that's tested and acted upon in the
non-signal portion of the program. My warning is about calling
printf() or whatever while the signal is active: Don't Do That.

The fact that you yourself don't call realloc() -- or qsort()
or fputc() or whatever else you care to name -- is no guarantee
that the program as a whole doesn't call it. The functions that
you *do* call may perfectly well call the others that you don't
(with only a few exceptions), so you can never be sure that thus-
and-such library function is not used at all. Some combinations
are surpassingly unlikely (strlen() probably doesn't call rewind()),
but the language definition actually forbids only a few such
dependencies.
19.3 is a good start. I was hoping to avoid having something
(progress bar or baton) constantly showing while the program was
running as that would consume processor cycles making the program
slower. Slower by how much I don't know until I do some testing.

It's all a function of how often you update the doohickey. And
consider: If you've got a display that runs from "0%" to "100%" you'll
update it no more than 101 times (assuming your program's smart enough
not to update when it doesn't change). If the computation takes a
l-o-n-g time, those 101 updates won't amount to a hill of beans -- and
if the computation is fast, why bother with an indicator at all?
I am trying to keep my program OS independent so I really don't want
to use ncurses or some other OS specific library. I realize my
program is almost always used on Linux so POSIX should be available.
SIGQUIT looks promising but I don't know if it works the same the way
on windows 7 or XP.

Neither do I. Signals (and AST's and so on) tend to be quirky and
quite idiosyncratic to the systems that fire them, so maximizing your
program's portability usually means minimizing its reliance on signals.
The straightforward twirl-the-baton-on-every-thousandth-iteration scheme
is likely to be far more portable. Which is not to say that it will
meet every need, and that's why I described it as "an alternative you
might consider."
 
N

Nick

Eric Sosman said:
Yes, that's fine: The signal handler can safely set a flag
(for ultimate squeaky-cleanliness the flag should be of type
`volatile sig_atomic_t') that's tested and acted upon in the
non-signal portion of the program. My warning is about calling
printf() or whatever while the signal is active: Don't Do That.

I'm ashamed to confess that I have a chunk of code that does all sorts
of things - generating complicated text and then system-ing out to an
external program - when it hits a signal (specifically - horror of horrors
- a segfault).

It actually works very well. I go on the principle that all bets are off
once you've segfaulted, so you might as well just go for it!

I do trap ctrl-C in the official way, volatile sig_atomic_t and all.
 
E

Eric Sosman

I'm ashamed to confess that I have a chunk of code that does all sorts
of things - generating complicated text and then system-ing out to an
external program - when it hits a signal (specifically - horror of horrors
- a segfault).

Might as well be hanged for a sheep as for a lamb, eh? Can't
quarrel with the overall philosophy, although I usually will steer
clear of anything as (potentially) complex as system() or printf() --
even though my program is going down in flames, I'd like to maximize
my chance of reading its final farewell. So in a POSIX environment
I'll stick to things like write() instead of printf(), but I admit
to freely using strlen() and so on -- functions that are unlikely to
carry a lot of complicated internal state, and are thus likely to be
reentrant.
It actually works very well. I go on the principle that all bets are off
once you've segfaulted, so you might as well just go for it!

I do trap ctrl-C in the official way, volatile sig_atomic_t and all.

Your devotion to cleanliness exceeds my own, then.
 
N

Nobody

Neither of these signal handlers is "safe". I don't think POSIX alters
what you can do in a signal handler

C99 only specifies abort and signal as being safe to call from signal
handlers. POSIX also specifies raise, rename, and time (as well as
many POSIX-specific functions).

None of the stdio functions are considered safe, nor are malloc etc or
anything which might reasonably need to use them.

Most of the core POSIX API is safe, including open, read, write, close,
fork, exec* (but not execlp/execvp).

Also, sem_post() is safe, so you can have a thread blocked on a semaphore
woken from a signal handler.
 
E

Ersek, Laszlo

(Adding comp.unix.programmer.)

In my program I want it so when the users presses the space bar (or any
other key if it is easier) they get a printf("hello, I am still
processing your request please wait") while the program is doing its
work. I added a signal handler to catch Ctrl-C and that works great
(i.e the program gracefully exits instead of crashes). Is there some
other signal I can use to do what I want?

I was intrigued by this problem and decided to spend a few hours on it.

Below is a program for SUSv3 platforms (populer unices should qualify).
According to my intent, the program should do the following:

- It executes a busy loop to simulate CPU hungry computation. (If one
chooses to test it, and on a multi-user system, please be sure to run it
with "nice".)

- The user can press a key any time on the terminal. No later than one
tenth of a second, a message should be written back to the terminal,
reporting progress, and asking for more time.

- Pending terminal input is not checked in each loop execution, because
that would waste very much CPU time in kernel mode. Instead, keypress
checking is decoupled and only attempted at every 1/10 of a second
(reconfigurable, USECS_PER_KEYCHECK). This should make responsiveness
independent from the processing power of the CPU running the program. If
one runs "top", the program should spend an overwhelming proportion of its
CPU time in user mode, and should never wait for IO.

- Each time pending input is detected, all pending input should be
flushed.

- One way to test the above might be: running the program and keeping the
space key depressed, with a keyboard repetition rate set to 30/sec. One
message should appear at each tenth of a second, and there should be huge
jumps in the progress info. Another way is to put the word "hello" in the
PRIMARY selection in some way, then paste it into the xterm running the
program -- dependent on how fast the pasting works (over the network, for
example), only intersecting alarms should trigger "please wait" messages,
not each single character.

- The program handles SIGINT/SIGTERM, if the parent process didn't set any
corresponding signal disposition to SIG_IGN (eg. a shell with "trap ''
SIGINT"). The exit status should reflect any such received signal.

- If no such signal was received, the exit status should reflect the
outcome of the computation. The length of the computation is configurable
(CTR_LIM).

- The program should correctly cooperate with an interactive shell
supporting job control. If any stop signal is delivered to the process,
the session leader shell should take back control and restore terminal
characteristics to useful values. (This is the job of any sane shell,
because any program might die unexpectedly, due to programming error or
eg. an async SIGKILL, before restoring terminal attributes.) On SIGCONT,
the program sets its own terminal attributes again. ^Z + fg should work as
expected. ^Z + bg works differently on Linux and Solaris (*), but the loop
in term_setup() should take care of that.


/*
compile & link with eg.

gcc -std=c99 -pedantic -Wall -Wextra -o keypress keypress.c

Be nice to other users, run it with

nice ./keypress
*/

#define _XOPEN_SOURCE 600 /* SUSv3 */

#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* EXIT_FAILURE */
#include <unistd.h> /* close() */
#include <signal.h> /* sigaction() */
#include <sys/time.h> /* setitimer() */
#include <fcntl.h> /* open() */
#include <string.h> /* strrchr() */
#include <errno.h> /* errno */
#include <termios.h> /* tcgetattr() */
#include <limits.h> /* ULLONG_MAX */

/* Counter limit. Make this a long long unsigned. */
#define CTR_LIM ULLONG_MAX

/* Generate SIGARLM this often. */
#define USECS_PER_KEYCHECK ((suseconds_t)1000 * 1000 / 10)

/* Indicators set by SIGALRM, SIGINT, SIGTERM. */
static volatile sig_atomic_t ring,
abandon_int, abandon_term;

/* Basename of executable. */
static const char *pname;

/* File descriptor to the controlling terminal. */
static int term_fd = -1;

/* Original attributes of the controlling terminal. */
static struct termios orig_attrs;


static void
satomic_handler(int sig)
{
switch (sig) {
case SIGALRM: ring = 1; break;
case SIGINT: abandon_int = 1; break;
case SIGTERM: abandon_term = 1;
}
}


static int
term_setup(void)
{
/*
Turn off canonical mode and local echo. Make read() return 0 if no byte is
available immediately. Since requests can succeed partially, make sure the
entire request succeeded. This function can be called from within an async
signal handler. This function is reentrant and idempotent.
*/
struct termios attrs;
int tmp;

attrs = orig_attrs;
attrs.c_lflag &= ~(ECHO | ICANON);
attrs.c_cc[VMIN] = 0;
attrs.c_cc[VTIME] = 0;

while (-1 == (tmp = tcsetattr(term_fd, TCSAFLUSH, &attrs))
&& EINTR == errno) {
}

return (0 == tmp
&& 0 == tcgetattr(term_fd, &attrs)
&& 0 == (attrs.c_lflag & (ECHO | ICANON))
&& 0 == attrs.c_cc[VMIN]
&& 0 == attrs.c_cc[VTIME]) ? 0 : -1;
}


static void
scont_handler(int sig)
{
/* Re-setup the terminal characteristics we need. */
if (-1 == term_setup()) {
abort();
}
}


static void
xsigemptyset(sigset_t *set)
{
if (-1 == sigemptyset(set)) {
(void)fprintf(stderr, "%s: sigemptyset() failed\n", pname);
abort();
}
}

static void
xsigaddset(sigset_t *set, int signo)
{
if (-1 == sigaddset(set, signo)) {
(void)fprintf(stderr, "%s: sigaddset() failed\n", pname);
abort();
}
}


static void
xsigaction(int sig, const struct sigaction *restrict act,
struct sigaction *restrict oact)
{
if (-1 == sigaction(sig, act, oact)) {
(void)fprintf(stderr, "%s: sigaction(): %s\n", pname, strerror(errno));
abort();
}
}


static void
xsigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)
{
if (-1 == sigprocmask(how, set, oset)) {
(void)fprintf(stderr, "%s: sigprocmask(): %s\n", pname, strerror(errno));
abort();
}
}


static void
xsetitimer(int which, const struct itimerval *restrict value,
struct itimerval *restrict ovalue)
{
if (-1 == setitimer(which, value, ovalue)) {
(void)fprintf(stderr, "%s: setitimer(): %s\n", pname, strerror(errno));
abort();
}
}

static void
sig_setup(struct sigaction *old_int, struct sigaction *old_term)
{
struct sigaction sa;

sa.sa_handler = &satomic_handler;
xsigemptyset(&sa.sa_mask);
sa.sa_flags = 0;

/*
Allow the parent process to turn off SIGINT/SIGTERM handling, by
making us inherit SIG_IGN.
*/
xsigaction(SIGINT, 0, old_int);
if (SIG_DFL == old_int->sa_handler) {
xsigaction(SIGINT, &sa, 0);
}

xsigaction(SIGTERM, 0, old_term);
if (SIG_DFL == old_term->sa_handler) {
xsigaction(SIGTERM, &sa, 0);
}

xsigaction(SIGALRM, &sa, 0);

sa.sa_handler = &scont_handler;
sa.sa_flags = SA_RESTART;
xsigaction(SIGCONT, &sa, 0);
}


static void
sig_restore(const struct sigaction *old_int, const struct sigaction *old_term)
{
{
struct sigaction sa;

sa.sa_handler = SIG_DFL;
xsigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
xsigaction(SIGCONT, &sa, 0);
}

xsigaction(SIGINT, old_int, 0);
xsigaction(SIGTERM, old_term, 0);
}


static void
timer_start(void)
{
struct itimerval it;

it.it_value.tv_sec = 0;
it.it_value.tv_usec = USECS_PER_KEYCHECK;
it.it_interval = it.it_value;
xsetitimer(ITIMER_REAL, &it, 0);
}


static void
timer_stop(void)
{
struct itimerval it;

it.it_value.tv_sec = 0;
it.it_value.tv_usec = 0;
it.it_interval = it.it_value;
xsetitimer(ITIMER_REAL, &it, 0);
}


static int
process(FILE *term_str)
{
sigset_t alrm_mask;
int err;
long long unsigned ctr;

xsigemptyset(&alrm_mask);
xsigaddset(&alrm_mask, SIGALRM);

err = 0;
for (ctr = 0LLU; ctr < CTR_LIM && !abandon_int && !abandon_term && !err;
++ctr) {
/* computation comes here */

if (ring) {
char unsigned dummy;

/*
Block SIGALRM. We've set up SIGCONT to restart read() and write(). So
the only signals that can interrupt the primitives below are SIGINT
and SIGTERM.
*/
xsigprocmask(SIG_BLOCK, &alrm_mask, 0);

switch (read(term_fd, &dummy, sizeof dummy)) {
case -1:
if (EINTR != errno) {
(void)fprintf(stderr, "%s: read(ctty): %s\n", pname,
strerror(errno));
err = 1;
}
break;

case 0:
/* no key was pressed */
break;

default:
/* Flush any other pending keypresses. */
if (-1 == tcflush(term_fd, TCIFLUSH)) {
/* tcflush() is uninterruptible */
(void)fprintf(stderr,
"%s: failed to flush pending ctty input: %s\n", pname,
strerror(errno));
err = 1;
}
else {
/*
Notify the user on the controlling terminal. The current active
handle here is a file descriptor, no action is needed when
leaving it for the sake of a standard I/O stream. After the
fprintf(), the active handle will again become the file
descriptor, thus some action may be necessary, dependent on the
buffering of term_str (which we didn't modify with setvbuf()
after fdopen() returned). Since term_str is open for writing
only, fflush() is sufficient.
*/
if ((0 > fprintf(term_str, "please wait... %llu / %llu\n", ctr,
CTR_LIM) || EOF == fflush(term_str)) && EINTR != errno) {
(void)fprintf(stderr, "%s: failed to write to ctty: %s\n", pname,
strerror(errno));
err = 1;
}
}
}

ring = 0;
xsigprocmask(SIG_UNBLOCK, &alrm_mask, 0);
}
}

return err ? -1 : 0;
}


int
main(int argc, char **argv)
{
int ret;

ret = EXIT_FAILURE;

pname = strrchr(argv[0], '/');
pname = pname ? pname + 1 : argv[0];

term_fd = open("/dev/tty", O_RDWR);
if (-1 == term_fd) {
(void)fprintf(stderr, "%s: failed to open controlling terminal: %s\n",
pname, strerror(errno));
}
else {
/*
Transfer ownership of file descriptor to an stdio stream. We'll use the
file descriptor for input (because input from a stream can't be portably
done in an unbuffered fashion), and the stream for output. We'll obey
the handle activation rules when switching between them.
*/
FILE *term_str;

term_str = fdopen(term_fd, "w");
if (0 == term_str) {
(void)fprintf(stderr, "%s: fdopen(ctty): %s\n", pname, strerror(errno));
(void)close(term_fd);
}
else {
if (-1 == tcgetattr(term_fd, &orig_attrs)) {
(void)fprintf(stderr, "%s: failed to get ctty attrs: %s\n", pname,
strerror(errno));
}
else {
struct sigaction old_int,
old_term;

sig_setup(&old_int, &old_term);

/*
From this point on, SIGINT and SIGTERM can interrupt. SIGALRM is not
yet periodically generated. A SIGCONT can be delivered. If it does,
it will set up the terminal for us. The probable case is that no
SIGCONT arrives in this tiny window, so we set up the terminal
ourselves, and are bit more lenient if it fails. If a SIGINT or
SIGTERM interrupts an underlying write() which has written no bytes
yet, then that's no problem, because we're bailing out anyway. A
SIGCONT will restart such a write().
*/

if (-1 == term_setup()) {
(void)fprintf(stderr, "%s: failed to set ctty attrs\n", pname);
}
else {
timer_start();
/* SIGALRM is generated periodically. */

if (0 == process(term_str)) {
ret = EXIT_SUCCESS;
}

timer_stop();
/* SIGALRM isn't generated by the timer anymore. */

if ((abandon_int || abandon_term) && (0 > fprintf(term_str,
"SIGINT/SIGTERM received\n") || EOF == fflush(term_str))) {
(void)fprintf(stderr, "%s: failed to write to ctty: %s\n", pname,
strerror(errno));
ret = EXIT_FAILURE;
}
}

sig_restore(&old_int, &old_term);

/*
SIGCONT is reset to SIG_DFL here ("nothing special"). The
disposition of SIGINT and SIGTERM are reset to the inherited
actions.
*/

/* Make an effort to restore original terminal modes. */
if (-1 == tcsetattr(term_fd, TCSAFLUSH, &orig_attrs)
&& EXIT_SUCCESS == ret) {
(void)fprintf(stderr, "%s: failed to restore ctty attrs\n", pname);
ret = EXIT_FAILURE;
}
}

/* fclose() takes care of term_fd as well */
if (EOF == fclose(term_str) && EXIT_SUCCESS == ret) {
(void)fprintf(stderr, "%s: fclose(ctty): %s\n", pname,
strerror(errno));
ret = EXIT_FAILURE;
}
}

/* Here, term_fd was already dealt with. */
}

if (abandon_int) {
(void)raise(SIGINT);
}
if (abandon_term) {
(void)raise(SIGTERM);
}
return ret;
}


--------
Footnote:

(*) linux-2.6.26 seems less standards-conformant than SunOS 5.9
Generic_122300-13 in this aspect.

When the process is put in the background and sent a SIGCONT signal with
"bg", the term_setup() function is invoked from the signal handler.
SIGCONT is simultaneously added to the signal mask. tcsetattr() elicits a
SIGTTOU, which stops the process again (in the signal handler context).
Now if another "bg" is issued,

- on Solaris, the stopped process wakes up inside the signal handler,
SIGCONT becomes pending (beause SIGCONT is handled but blocked),
tcsetattr() is retried, and another SIGTTOU is sent immedately. Thus the
Solaris process never moves. When an "fg" is issued consecutively, another
SIGCONT is sent (which was already pending), tcsetattr() is retried and it
succeeds. The signal handler returns, and is invoked again immediately,
because SIGCONT was pending. (term_setup() is idempotent, so this is
okay.) I find this behavior standards conformant.

- OTOH, on Linux, the stopped process not only wakes up in tcsetattr()
that was invoked on the signal stack, but tcsetattr() even returns with
-1/EINTR. I find this less standards conformant, because SIGCONT is
*blocked* when and where tcsetattr() is resumed. (No handler ran within
tcsetattr().) And sure enough, the handler is not reentered (SIGCONT
remains pending), therefore this is a spurious wakeup which necessiates
the loop. Due to the loop, tcsetattr() is retried and that evokes another
SIGTTOU, and the process is stopped. When an "fg" is issued finally, the
loop is exercised one final time, and then the tcsetattr() succeeds, and
then the signal handler runs again due to the pending SIGCONT.

The tcsetattr() invocation could be interrupted by other signals (or by
the SIGCONT handler when the same term_setup() function is running outside
of signal context); the loop should cover those too.
--------

.... I didn't try to find the best / simplest solution, just one that was
working. Any constructive criticism is highly appreciated. (I'm going to
attribute any bugs to beers downed.)

.... Aaah. Now that I reread the OP's question, perhaps Rich wasn't
interested in "all keys", but "one specific key", or perhaps "one suitable
signal". :)

Unix utilities should probably choose one of SIGUSR1, SIGUSR2, and the
user could send such a signal from a different terminal with the "kill"
utility. Some systems have a STATUS special character, which sends the
SIGINFO signal to the foreground process group. SIGINFO should be
dedicated to this exact purpose. (I'm remindend of ^T on OpenVMS.)

http://www.gnu.org/s/libc/manual/html_node/Miscellaneous-Signals.html

lacos
 
R

Rich

     Yes, that's fine: The signal handler can safely set a flag
(for ultimate squeaky-cleanliness the flag should be of type
`volatile sig_atomic_t') that's tested and acted upon in the
non-signal portion of the program.  My warning is about calling
printf() or whatever while the signal is active: Don't Do That.

     The fact that you yourself don't call realloc() -- or qsort()
or fputc() or whatever else you care to name -- is no guarantee
that the program as a whole doesn't call it.  The functions that
you *do* call may perfectly well call the others that you don't
(with only a few exceptions), so you can never be sure that thus-
and-such library function is not used at all.  Some combinations
are surpassingly unlikely (strlen() probably doesn't call rewind()),
but the language definition actually forbids only a few such
dependencies.


     It's all a function of how often you update the doohickey.  And
consider: If you've got a display that runs from "0%" to "100%" you'll
update it no more than 101 times (assuming your program's smart enough
not to update when it doesn't change).  If the computation takes a
l-o-n-g time, those 101 updates won't amount to a hill of beans -- and
if the computation is fast, why bother with an indicator at all?


     Neither do I.  Signals (and AST's and so on) tend to be quirky and
quite idiosyncratic to the systems that fire them, so maximizing your
program's portability usually means minimizing its reliance on signals.
The straightforward twirl-the-baton-on-every-thousandth-iteration scheme
is likely to be far more portable.  Which is not to say that it will
meet every need, and that's why I described it as "an alternative you
might consider."

I changed my flag from int to volatile sig_atomic_t,

I am going with the print a percentage done approach. It took a few
minutes to find the right spot to print the percentage. I did have to
add a counter to the program to get the correct percentage and the
counter does have a small impact but it isn't too bad. However after
a short break and a code review I changed the counter logic and impact
is now almost nonexistent.

I tried the SIGQUIT approach and it does work if I use write in the
handler. I.e. write(0, "HERE\n", 5); The only problem is I haven't
figured out away to make the message meaningful. I.e. Please be
patient. I am on XX of ZZ records. I would have to print or strcat
to build the sentence and there aren't async safe. I am also
concerned with the portability of this approach. It can't be
guaranteed that this will work exactly the same way on DOS, Windows,
Linux, FreeBSD, etc.

I found
https://www.securecoding.cert.org/c...hronous-safe+functions+within+signal+handlers
has a list of asynchronous safe functions.

Thank you for your time. You have been most helpful and so have a few
others.
 
L

luserXtrog

Some systems have a STATUS special character, which sends the
SIGINFO signal to the foreground process group. SIGINFO should be
dedicated to this exact purpose. (I'm remindend of ^T on OpenVMS.)

This is what I was looking for when I stumbled upon SIGQUIT and
termios.
I first learned about ctrl-T from "Inside the Apple Laserwriter".
It truly appears to be the ideal mechanism for asking a silent program
what it's up to. [grammatical aside: 'up to what it is'?]

Why doesn't Linux support this?
 
U

Uno

Seebs said:
The comp.lang.c FAQ has repeatedly answered my specific questions. Often
in great detail.

And while I have often had support people refer me to a useless "FAQ" site,
I have also seen a few company-provided FAQs that really did cover my
problem.

#define 'see the c faq' 'mitch mcconnell wants to attach ammendments'

I think they're purposefully outdated. Then there's no way to escape
the fundamentalists.
 
B

Ben Bacarisse

I changed my flag from int to volatile sig_atomic_t,

I am going with the print a percentage done approach. It took a few
minutes to find the right spot to print the percentage. I did have to
add a counter to the program to get the correct percentage and the
counter does have a small impact but it isn't too bad. However after
a short break and a code review I changed the counter logic and impact
is now almost nonexistent.

I tried the SIGQUIT approach and it does work if I use write in the
handler. I.e. write(0, "HERE\n", 5); The only problem is I haven't
figured out away to make the message meaningful. I.e. Please be
patient. I am on XX of ZZ records. I would have to print or strcat
to build the sentence and there aren't async safe.

write is safe if you can assume POSIX but you are right that the str*
functions aren't and there is a reason for this. If the string
functions use some complex hardware assist, these operations may not
"nest". I.e. if the signal handler interrupts a string operation,
either the strcat done in the handler or the one that was interrupted
might go wrong. There may be other reasons, of course. I offer that
only to show that the restriction is not arbitrary.

strcat is unlikely to be the stumbling-block since you can use multiple
writes to build up the message. The trouble is you need a conversion
from a number to a string and sprintf if not "signal safe". However,
since the number can be forced to be an integer between 0 and 99 you
would not need much code to convert it to text for printing with write.

I'd still go for the "set flag and report elsewhere" approach.
I am also
concerned with the portability of this approach. It can't be
guaranteed that this will work exactly the same way on DOS, Windows,
Linux, FreeBSD, etc.

I found
https://www.securecoding.cert.org/c...hronous-safe+functions+within+signal+handlers
has a list of asynchronous safe functions.

POSIX (technically "The Single UNIX Specification") expands on what can
be called but it does not add any useful standard C functions to those
that C permits you to call from a signal handler. (It does add "time"
but that's not a lot of help.)

http://www.UNIX.org/2008edition/ and look for "Signal Concepts". BTW,
if this gets any more involved, comp.unix.programmer is probably a
better place to discuss it.

<snip>
 
B

Ben Bacarisse

Nobody said:
C99 only specifies abort and signal as being safe to call from signal
handlers. POSIX also specifies raise, rename, and time (as well as
many POSIX-specific functions).

C99 also permits _Exit but that does not alter your point.
None of the stdio functions are considered safe, nor are malloc etc or
anything which might reasonably need to use them.

Most of the core POSIX API is safe, including open, read, write, close,
fork, exec* (but not execlp/execvp).

Also, sem_post() is safe, so you can have a thread blocked on a semaphore
woken from a signal handler.

Yup. I intended to say something along the lines of "POSIX does not
alter what standard C functions you can use" but it came out as the
entirely wrong statement you've corrected. As it turns out, even that
would have been wrong since POSIX states that "time()" is safe to call
from a signal handler and that /is/ a standard C function.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top