Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

keypresses and signal handling

25 views
Skip to first unread message

Rich

unread,
Sep 25, 2010, 4:06:22 PM9/25/10
to
Hi,

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?

Thanks,

Eric Sosman

unread,
Sep 25, 2010, 4:39:59 PM9/25/10
to

C itself has no such capability, although the systems C runs on
may offer something. Check your system's documentation.

By the way, you almost certainly do *not* want to call printf()
in a signal handler, nor any other library function that your system's
documentation does not specifically describe as "async signal safe" or
something along those lines. Trouble can occur (usually, does occur)
if the signal arrives while the program is executing printf(), or some
other function (malloc(), maybe?) that printf() calls internally. If
you're half-way through a realloc() call, go into the signal handler
while realloc() has flour scattered all over the kitchen and the fridge
door hanging open, and then try to call malloc() before anybody's
had a chance to tidy up -- well, the outcome is seldom a happy one.

An alternative you might consider is to display some kind of
animated progress indicator: A number that starts at 417 and counts
down to zero, or a little twirling baton, or something like that. See
Question 19.3 on the comp.lang.c Frequently Asked Questions (FAQ) page
at <http://www.c-faq.com/>.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Seebs

unread,
Sep 25, 2010, 4:49:21 PM9/25/10
to

Not in general, no. This is a FAQ. You may find it useful to read
the newsgroup's FAQ:

http://c-faq.com/

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.

Uno

unread,
Sep 25, 2010, 6:19:21 PM9/25/10
to
Eric Sosman wrote:

> By the way, you almost certainly do *not* want to call printf()
> in a signal handler, nor any other library function that your system's
> documentation does not specifically describe as "async signal safe" or
> something along those lines. Trouble can occur (usually, does occur)
> if the signal arrives while the program is executing printf(), or some
> other function (malloc(), maybe?) that printf() calls internally. If
> you're half-way through a realloc() call, go into the signal handler
> while realloc() has flour scattered all over the kitchen and the fridge
> door hanging open, and then try to call malloc() before anybody's
> had a chance to tidy up -- well, the outcome is seldom a happy one.

I would claim the exception when you're testing it to see if it's ever
being used. I get stung on related issues regularly.


$ gcc -Wall -Wextra u4.c -o out
$ ./out
42
[file u4.c, line 14]: hey, x=-1207183232
$ cat u4.c
// debugging macros so we can pin down message provenance at a glance
#define WHERESTR "[file %s, line %d]: "
#define WHEREARG __FILE__, __LINE__
#define DEBUGPRINT2(...) fprintf(stderr, __VA_ARGS__)
#define DEBUGPRINT(_fmt, ...) DEBUGPRINT2(WHERESTR _fmt, WHEREARG,
__VA_ARGS__)

#include <stdio.h>

int main (void)
{
int x; //never gave it a value

printf("42\n");
DEBUGPRINT("hey, x=%d\n", x);
return 0;
}


// gcc -Wall -Wextra -dD -E u4.c >text1.txt
// gcc -Wall -Wextra u4.c -o out
$

Are __VA_ARGS__ and _fmt defined in a standard way?
--
Uno

Richard

unread,
Sep 25, 2010, 11:14:23 PM9/25/10
to
Seebs <usenet...@seebs.net> writes:

> On 2010-09-25, Rich <rtil...@gmail.com> wrote:
>> Hi,
>>
>> 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?
>
> Not in general, no. This is a FAQ. You may find it useful to read
> the newsgroup's FAQ:
>
> http://c-faq.com/
>
> -s

Why not answer the bloody question instead of passing him off to a
massive FAQ?


--
"Avoid hyperbole at all costs, its the most destructive argument on
the planet" - Mark McIntyre in comp.lang.c

Uno

unread,
Sep 26, 2010, 12:19:24 AM9/26/10
to
Richard wrote:

> Seebs <usenet...@seebs.net> writes:
the newsgroup's FAQ:

>> http://c-faq.com/
>>
>> -s
>
> Why not answer the bloody question instead of passing him off to a
> massive FAQ?
>
>

seebs 101: in the last case, help.

He takes douchebag to a new. unemployable, English level.
--
Uno

Ian Collins

unread,
Sep 26, 2010, 12:48:55 AM9/26/10
to
On 09/26/10 04:14 PM, Richard wrote:
> Seebs<usenet...@seebs.net> writes:
>
>> On 2010-09-25, Rich<rtil...@gmail.com> wrote:
>>> Hi,
>>>
>>> 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?
>>
>> Not in general, no. This is a FAQ. You may find it useful to read
>> the newsgroup's FAQ:
>>
>> http://c-faq.com/
>>
>> -s
>
> Why not answer the bloody question instead of passing him off to a
> massive FAQ?

Why don't you? Oh, I forgot you're only here to snipe.

--
Ian Collins

Geoff

unread,
Sep 26, 2010, 2:09:08 AM9/26/10
to
On Sun, 26 Sep 2010 05:14:23 +0200, Richard <rgr...@gmail.com> wrote:

>Seebs <usenet...@seebs.net> writes:
>
>> On 2010-09-25, Rich <rtil...@gmail.com> wrote:
>>> Hi,
>>>
>>> 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?
>>
>> Not in general, no. This is a FAQ. You may find it useful to read
>> the newsgroup's FAQ:
>>
>> http://c-faq.com/
>>
>> -s
>
>Why not answer the bloody question instead of passing him off to a
>massive FAQ?

Probably because he doesn't know the specific FAQ that pertains to the
question and he refuses to do the OP's searching for him.

Perhaps also because a search of the linear FAQ index:
http://c-faq.com/questions.html for "keypress" or "signal" or
"handling" yields no results.

Uno

unread,
Sep 26, 2010, 2:19:31 AM9/26/10
to


So Ian, are you the second english information non-provider here on this
thread, because unlike seebs, I like you.

Crap Almighty, I think OP is fishing for something that begins with SEG
in caps.

You didn't forget, and you are here to snipe, which has a regional
interpretation.
--
Uno

luserXtrog

unread,
Sep 26, 2010, 2:51:57 AM9/26/10
to

If you've got POSIX, this might work.
You send a SIGINT with ^c (as you know)
and a SIGQUIT with ^\.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#include <termios.h>
#include <unistd.h>

struct termios ts;

void on_int (int sig) {
puts("bye");
tcsetattr(fileno(stdin), TCSANOW, &ts);
exit(0);
}

void on_quit (int sig) {
puts("hello, I am still processing your request please wait");
}

int main() {

{ /* Enable other signal keys */
struct termios tsb;
tcgetattr(fileno(stdin), &ts);
tsb = ts;
tsb.c_lflag = ISIG;
tcsetattr(fileno(stdin), TCSANOW, &tsb);
}

{ /* Install Signal Handlers */
signal(SIGINT, on_int);
signal(SIGQUIT, on_quit);
}

while(1) {
usleep(500);
}
return 0;
}

/* eof */

Malcolm McLean

unread,
Sep 26, 2010, 3:26:55 AM9/26/10
to
kbhit() does what you want (I think, it's ages since I used it). It is
available on DOS.

just write if(khit()) printf("hello, I am still processing your
request please wait") somewhere in u=your inner loop. You might also
need to read the character.

If you don't have kbhit() this page gives an implementation for linux.

http://www.linux-sxs.org/programming/kbhit.html


Richard

unread,
Sep 26, 2010, 4:48:31 AM9/26/10
to
Ian Collins <ian-...@hotmail.com> writes:

Because someone else provided more info.

I answer when I think its valid OR to suggest something like above. If
you have nothing to offer, as is nearly all the time with Seebs, best to
STFU. Pointing someone to a C FAQ is stupid. EVERY question about C can
be handled there IF the Qs are, as is so often enforced, related to ISO
C.

THATS why.

Richard

unread,
Sep 26, 2010, 4:49:20 AM9/26/10
to
Geoff <ge...@invalid.invalid> writes:

> On Sun, 26 Sep 2010 05:14:23 +0200, Richard <rgr...@gmail.com> wrote:
>
>>Seebs <usenet...@seebs.net> writes:
>>
>>> On 2010-09-25, Rich <rtil...@gmail.com> wrote:
>>>> Hi,
>>>>
>>>> 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?
>>>
>>> Not in general, no. This is a FAQ. You may find it useful to read
>>> the newsgroup's FAQ:
>>>
>>> http://c-faq.com/
>>>
>>> -s
>>
>>Why not answer the bloody question instead of passing him off to a
>>massive FAQ?
>
> Probably because he doesn't know the specific FAQ that pertains to the
> question and he refuses to do the OP's searching for him.

I like forward to every question being answered by you with an RTFM
then.

Seebs

unread,
Sep 26, 2010, 5:21:30 AM9/26/10
to
On 2010-09-26, Malcolm McLean <malcolm...@btinternet.com> wrote:

> On Sep 25, 10:06?pm, Rich <rtillm...@gmail.com> wrote:
>> 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?

> kbhit() does what you want (I think, it's ages since I used it). It is
> available on DOS.

But on DOS, does ^C generate SIGINT?

The systems I've used where ^C can trigger a "signal handler" have not
been DOS... And the Linux "implementation" is sorta questionable.

This advice actually does a great job of showing why the FAQ is a
better answer than trying to write a specific answer. This is a pretty
tricky thing to get right for most platforms, and even on DOS, it's
not at all obvious that the kbhit() solution is a particularly good
choice. There are many ways of approaching this problem. The most
reasonable, for most targets, do not involve polling at all.

Ben Bacarisse

unread,
Sep 26, 2010, 5:47:26 AM9/26/10
to
luserXtrog <mij...@yahoo.com> writes:

> On Sep 25, 3:06 pm, Rich <rtillm...@gmail.com> wrote:
>> 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?
>

> If you've got POSIX, this might work.
> You send a SIGINT with ^c (as you know)
> and a SIGQUIT with ^\.
>
>
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <signal.h>
>
> #include <termios.h>
> #include <unistd.h>
>
> struct termios ts;
>
> void on_int (int sig) {
> puts("bye");
> tcsetattr(fileno(stdin), TCSANOW, &ts);
> exit(0);
> }
>
> void on_quit (int sig) {
> puts("hello, I am still processing your request please wait");
> }

Neither of these signal handlers is "safe". I don't think POSIX alters
what you can do in a signal handler -- puts is definitely out both in
standard C and in C+POSIX.

The general way to do this is to have the signal handler set a flag (of
type sig_atomic_t) and have this checked in the processing loop that is
taking so much time. That loop can print anything it likes. That's
both a C answer and a POSIX one.

<snip>
--
Ben.

Sarin

unread,
Sep 26, 2010, 5:47:35 AM9/26/10
to
On Sep 26, 2:21 pm, Seebs <usenet-nos...@seebs.net> wrote:

> On 2010-09-26, Malcolm McLean <malcolm.mcle...@btinternet.com> wrote:
>
> > On Sep 25, 10:06?pm, Rich <rtillm...@gmail.com> wrote:
> >> 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")

I was just wondering why no one suggested using threads. May be its an
overkill. But, I think a thread should do it(?) Please tell me if I am
wrong.

Seebs

unread,
Sep 26, 2010, 5:51:20 AM9/26/10
to
On 2010-09-26, Sarin <suse...@gmail.com> wrote:
> I was just wondering why no one suggested using threads. May be its an
> overkill. But, I think a thread should do it(?) Please tell me if I am
> wrong.

Because threads aren't (as of yet) a standard feature of C, and there
exist a fair number of implementations which don't have threading. Perhaps
more importantly, the threading implementations out there are largely
different from each other. As a result, any specific advice on this
is pretty much doomed to be system-specific.

So the best advice would be for the OP to ask about good ways to achieve
this result in a group specific to the target platform. The people here
are, unfortunately, sometimes a bit prone to confusion about the various
platforms, especially the ones they don't use...

Nick

unread,
Sep 26, 2010, 5:55:19 AM9/26/10
to
Uno <merril...@q.com> writes:

Did you help him.

The whole bloody point of a FAQ is that it holds good, detailed, answers
to FREQUENTLY ASKED QUESTIONS. It's almost always /far/ better to refer
someone to a FAQ than to try to answer the question yourself.
--
Online waterways route planner | http://canalplan.eu
Plan trips, see photos, check facilities | http://canalplan.org.uk

BartC

unread,
Sep 26, 2010, 6:03:27 AM9/26/10
to
"Malcolm McLean" <malcolm...@btinternet.com> wrote in message
news:02619143-e56b-4e9d...@i13g2000yqd.googlegroups.com...

> On Sep 25, 10:06 pm, Rich <rtillm...@gmail.com> wrote:
>>
>> 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?
>>
> kbhit() does what you want (I think, it's ages since I used it). It is
> available on DOS.

Where the processing is complex you really don't want to sprinkle kbhit()s
all over the place (eg. in a library).

And I'm not sure it handles Ctrl-C properly.

--
Bartc

BartC

unread,
Sep 26, 2010, 6:32:55 AM9/26/10
to
"Nick" <3-no...@temporary-address.org.uk> wrote in message
news:87sk0wz...@temporary-address.org.uk...

> The whole bloody point of a FAQ is that it holds good, detailed, answers
> to FREQUENTLY ASKED QUESTIONS. It's almost always /far/ better to refer
> someone to a FAQ than to try to answer the question yourself.

IME, no FAQ I've ever seen on a website has ever answered my specific
question.

This forces you to call a premium rate number to talk to an operator, and it
is incredibly annoying when the first minute is spent telling you to visit
the website! No-one ever calls these numbers unless they have to.

I remember this classic example of a completely useless (and lazy) response
to a pretty straightforward question:

"CBFalconer" <cbfalco...@yahoo.com> wrote in message

> ssylee wrote:
>> How would I printf a really small number (e.g. 1.24e-23) without
>> having 0.000000...000e+000 shown on the output? i.e. what flag
>> would that be?

> Why don't you look it up in the C standard. The things marked C99
> below are the standard.

> Some useful references about C:
> <http://www.ungerhu.com/jxh/clc.welcome.txt>
> <http://c-faq.com/> (C-faq)
> <http://benpfaff.org/writings/clc/off-topic.html>
> <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf> (C99)
> <http://cbfalconer.home.att.net/download/n869_txt.bz2> (pre-C99)
> <http://www.dinkumware.com/c99.aspx> (C-library}
> <http://gcc.gnu.org/onlinedocs/> (GNU docs)
> <http://clc-wiki.net/wiki/C_community:comp.lang.c:Introduction>
> <http://clc-wiki.net/wiki/Introduction_to_comp.lang.c>

--
Bartc

Kenny McCormack

unread,
Sep 26, 2010, 8:40:50 AM9/26/10
to
In article <i7mdqg$8mv$4...@news.eternal-september.org>,
Richard <rgr...@gmail.com> wrote:
...

>Why not answer the bloody question instead of passing him off to a
>massive FAQ?

The various BS answers have been given, but the real reason why it is
better (heh heh) to just point at a FAQ is that if you actually answer
someone's question, you are entering into a support relationship with
the person. That is, you are expected to continue to handle the
followup questions until such time as the poster is happy. And that can
be a long slog.

--
But the Bush apologists hope that you won't remember all that. And they
also have a theory, which I've been hearing more and more - namely,
that President Obama, though not yet in office or even elected, caused the
2008 slump. You see, people were worried in advance about his future
policies, and that's what caused the economy to tank. Seriously.

(Paul Krugman - Addicted to Bush)

Seebs

unread,
Sep 26, 2010, 12:12:55 PM9/26/10
to
On 2010-09-26, BartC <b...@freeuk.com> wrote:
> IME, no FAQ I've ever seen on a website has ever answered my specific
> question.

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.

Rich

unread,
Sep 26, 2010, 2:30:41 PM9/26/10
to

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?

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.

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.

I gave the FAQ a look over but I didn't read section 19. Displaying a
progress bar or baton doesn't seem like a system dependency to me. It
seems for like a stdio or Miscellaneous to me.

Thank you everyone for your input. I have quite a bit of research and
coding to do to see what will work best for me.

Thanks,

Eric Sosman

unread,
Sep 26, 2010, 3:00:15 PM9/26/10
to

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."

--
Eric Sosman
eso...@ieee-dot-org.invalid

Nick

unread,
Sep 26, 2010, 3:04:45 PM9/26/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> 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.

Eric Sosman

unread,
Sep 26, 2010, 3:10:59 PM9/26/10
to
On 9/26/2010 3:04 PM, Nick wrote:
> Eric Sosman<eso...@ieee-dot-org.invalid> writes:
>
>> 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).

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.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Nobody

unread,
Sep 26, 2010, 3:53:54 PM9/26/10
to
On Sun, 26 Sep 2010 10:47:26 +0100, Ben Bacarisse wrote:

> 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.

Ersek, Laszlo

unread,
Sep 26, 2010, 4:03:06 PM9/26/10
to
(Adding comp.unix.programmer.)

On Sat, 25 Sep 2010, Rich wrote:

> 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

Rich

unread,
Sep 26, 2010, 9:44:22 PM9/26/10
to

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/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-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.

luserXtrog

unread,
Sep 27, 2010, 12:59:22 AM9/27/10
to
On Sep 26, 3:03 pm, "Ersek, Laszlo" <la...@caesar.elte.hu> wrote:
<snip>

> 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?

--
could not decide whether to snip the link.

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

Uno

unread,
Sep 27, 2010, 5:13:39 AM9/27/10
to
Seebs wrote:
> On 2010-09-26, BartC <b...@freeuk.com> wrote:
>> IME, no FAQ I've ever seen on a website has ever answered my specific
>> question.
>
> 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.
--
Uno

Ben Bacarisse

unread,
Sep 27, 2010, 6:26:06 AM9/27/10
to
Rich <rtil...@gmail.com> writes:
<snip>

>> >> On 9/25/2010 4:06 PM, Rich wrote:
<snip>

>> >>> 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?
<snip>

> 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/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-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>
--
Ben.

Ben Bacarisse

unread,
Sep 27, 2010, 6:31:56 AM9/27/10
to
Nobody <nob...@nowhere.com> writes:

> On Sun, 26 Sep 2010 10:47:26 +0100, Ben Bacarisse wrote:
>
>> 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).

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.

--
Ben.

0 new messages