[comp.windows.x] Problem with Signals

jcarson@WILKINS.BCM.TMC.EDU (Janet L. Carson) (03/01/90)

I've been watching the "problem with signals" thread, and I agree 
with Bob Scheifler that no one has really explained clearly what the 
problems are.  I am in the process of writing a tool which is going 
to be an interface to FTP, and I am including a SIGPIPE error handler 
to handle the "connection lost" case.  Where do I need to look out 
for problems?

Thanks for any info,
--Janet

Janet L. Carson                               internet: jcarson@bcm.tmc.edu 
Baylor College of Medicine              uucp: {rutgers,mailrus}!bcm!jcarson

argv%turnpike@Sun.COM (Dan Heller) (03/02/90)

From: jcarson@WILKINS.BCM.TMC.EDU (Janet L. Carson)
Subject: Problem with Signals

    I've been watching the "problem with signals" thread, and I agree 
    with Bob Scheifler that no one has really explained clearly what the 
    problems are.  I am in the process of writing a tool which is going 
    to be an interface to FTP, and I am including a SIGPIPE error handler 
    to handle the "connection lost" case.  Where do I need to look out 
    for problems?

    Janet L. Carson                               internet: jcarson@bcm.tmc.edu 
    Baylor College of Medicine              uucp: {rutgers,mailrus}!bcm!jcarson

The problem with signals and X is rather simple.  X is asynchronous
and relies on a communication between the client (application) and
the server (the X server).  This "protocol" is defined and implemented
using Xlib.  If you are putting together a packet to send over the wire
to the server and a signal "interrupts" your client side application,
then you have interrupted the protocol and *may* result in a protocol
error with the server.  You may even get a core dump.

This is not guaranteed to happen, but the case becomes much more
likely if your event handler attempts to make any Xlib calls (which
includes many, but not all, Xt calls).  Perhaps the most common
case of this is the SIGALRM signal, which is delivered as a result
of such things as "setitimer".  Note Brian's problem below:

From: envbvs@epb2.lbl.gov (Brian V. Smith)

I have put patch3 to xfig2.0 on expo.lcs.mit.edu.
...
Changes from patchlevel 2:

o now uses XtAddTimer instead of setitimer() for blinking text cursor

The reason he did this was because xfig suffered from random
core dumps as a result of the timer interfering with the X
protocol and attempting to "blink" the cursor by turning it
off and on.  As you can see, he fixed the problem by using
XtAddTimeOut() to replace the use of the timer.  Xt doesn't
use signals to do this -- it simulates signals by simply checking
the time in the main looping mechanism.

Because this particular problem is solved, and because it is
the most commonly utilized signal so far, many people don't
perceive the fundamental problem as being that bad.  However,
the "fundamental problem" still exists: if you interrupt the
protocol betwween the client and server, then you must be sure
to return to the place in the application that was interrupted
before making further attempts to communicate with the X server.
Therefore, you signal handler should not display dialog boxes,
make Xlib calls, or do much any anything until it is guaranteed
that you are not going to interfere with a protocol exachange.

So, what do you do?  Well, your signal handler could be written
to set a flag to note the delivery of the signal and then return
so that the code being execute may continue with whatever it's
doing till you know it's safe.  *that* is the problem -- you 
never know when it's safe.  And when it *is* safe, you are not
in any application-specific code.  There may not be any more
events in the queue so that your next event handling function
won't be called for quite some time.

My proposed solution is to implement a routine:
    XtSigRet
    XtAddSignalHandler(signal, handler)
	int signal;
	XtSigRet (*handler)();

XtSigRet is either void or int depending on what your UNIX OS
uses for signal().  When you register your signal handler for
the specified signal, when the signal is delivered, *Xt* catches
the signal and notes the signal context, resources, etc..
Then, when all is well with the events and protocol, etc,
your handler is called.  Your handler is written _exactly the same_
as it would be had you called "signal(sig, handler)" because
the same parameters are passed back into the signal handler.
(XtAddSignalHandler returns the previously set handler just
like signal() does.)

Now, you could implement this yourself via "work procs"
(see XtAddWorkProc()), but the granularity isn't fine enough.
That is, work procs are only called when there are no events
pending.  Implemented properly, the internals to Xt would
check in between event delivery to event handlers.

Like rws, I don't know VMS's ASTs well enough to know how this
mechasnism fits in with the sceme programmatically.  However,
I do know that this implementation can fit in conceptually.
"signal()" can be #ifdef'ed out if necessary and those systems
that don't have signal() are simply unaffected since the check
for nonexistent signals does nothing.  That is, I envision the
implementation as being:
    signal is delvered, Xt sets a bit in a flag indicating
    that the signal has been delivered.  Signal number, contenxt
    resource usage, etc.. are saved.  At appropriate time, Xt
    checks signal flags and see swhich signals have been delivered
    and calls the signal handler with appropriate parameters saved
    from the real signal delivery.
If VMS's ASTs has similar parameters, use those, too.

dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.

karlton@sgi.com (Phil Karlton) (03/03/90)

This is what happened to VMS as the result of X (if memory serves me
right). The LockDisplay and UnlockDisplay macro invocations that
surround every Xlib routine were defined to set or clear a particular
bit in the client's address space. (This is a relatively cheap
operation.) Then the OS was changed so that if the bit was set the AST
was not delivered to the process until the bit was cleared. This
prevents the "interrupt" routines from running while a partial X request
was in an I/O buffer. I am not going to hold my breath until all of the
various UNIX vendors add a similar feature.

One might consider merely using a different X connection for
communicating with the server while in any interrupt routine. Adequate
support within the intrinsics for dealing with mulitple connections and
reentrancy would be necessary to make this viable for Xt based clients.

PK

JONESD@kcgl1.eng.ohio-state.edu (David Jones) (03/03/90)

Instead of hacking up tookits to allow your application to add signal handlers,
I'd rather see them hacked up to convert signals into client events.  The
application would deal with with the signal as another event to deal and
not as something to be dealt with by a signal handler.


David L. Jones               |      Phone:    (614) 292-6929
Ohio State Unviversity       |      Internet:
1971 Neil Ave. Rm. 406       |               jonesd@kcgl1.eng.ohio-state.edu
Columbus, OH 43210           |               jones-d@eng.ohio-state.edu

Disclaimer: A repudiation of a claim.

argv%turnpike@Sun.COM (Dan Heller) (03/04/90)

In article <4429@quanta.eng.ohio-state.edu> JONESD@kcgl1.eng.ohio-state.edu (David Jones) writes:
> Instead of hacking up tookits to allow your application to add signal handlers,
> I'd rather see them hacked up to convert signals into client events.  The
> application would deal with with the signal as another event to deal and
> not as something to be dealt with by a signal handler.

That's fundamentally the same thing I'm talking about.  That is,
you register signals and handlers (functions) associated with those
signals in case a signal is delivered.  The toolkit then calls the
function registered.  However, the idea asbout converting signals
into events odesn't make sense -- signals don't happen in windows [%]
and they don't have x,y values or serial IDs etc.. associated with
them so the generic event type can't fill in anything.  Keep the
signals as signals...

[%] SIGWINCH is a possible exception and an interesting case..
altho not that interesting...

    From: PARCEL@INTELLICORP.COM (Scott Parcel)
    Subject: X & Signals
    Message-ID: <9002281442.aa11275@mintaka.lcs.mit.edu>
    Date: 28 Feb 90 19:37:02 GMT

    I have some questions and comments about the recent discussion of
    handling events inside of Unix signal handlers.  My particular
    interest is in using signals to do all X event processing.

Can you please explain this?  This makes no sense to me.
What kind of interface do you provide for an application
that does nothing but trap signals?  What kind of functionality
does such an application serve?

The following is my understanding of signal handling problems and
methodology:
dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.

argv%turnpike@Sun.COM (Dan Heller) (03/04/90)

Geez, not only did I forget to proofread my last message, I forgot
to *finish* it before I posted it.. It's been a very long week....

My previous message was supposed to look like this:

In article <4429@quanta.eng.ohio-state.edu> JONESD@kcgl1.eng.ohio-state.edu (David Jones) writes:
> Instead of hacking up tookits to allow your application to add signal handlers,
> I'd rather see them hacked up to convert signals into client events.  The
> application would deal with with the signal as another event to deal and
> not as something to be dealt with by a signal handler.

That's fundamentally the same thing I'm talking about.  That is,
you should register signals and handlers (functions) for those
signals with a toolkit or its intrinsics (Xt, in this case).  The
toolkit calls the function registered for the signal in case one
is delivered.  However, the idea about converting signals into
events doesn't make sense -- signals don't happen in windows [%]
and they don't have any of the associated values that accompany
existing X events such as a serial #, window ID, x/y values, etc...
Signals are associated with processes, not X clients.  The problem
may become more complicated as subprocesses are created (fork(),
for example).

[%] SIGWINCH is a possible exception and an interesting case..
altho not that interesting...

    From: PARCEL@INTELLICORP.COM (Scott Parcel)
    Subject: X & Signals
    Message-ID: <9002281442.aa11275@mintaka.lcs.mit.edu>
    Date: 28 Feb 90 19:37:02 GMT

    I have some questions and comments about the recent discussion of
    handling events inside of Unix signal handlers.  My particular
    interest is in using signals to do all X event processing.

Can you please explain this?  This makes no sense to me.
What kind of interface do you provide for an application
that does nothing but trap signals?  What kind of functionality
does such an application serve?

dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.

evgabb@sdrc.UUCP (Rob Gabbard) (03/05/90)

In article <4429@quanta.eng.ohio-state.edu>, JONESD@kcgl1.eng.ohio-state.edu (David Jones) writes:
> Instead of hacking up tookits to allow your application to add signal handlers,
> I'd rather see them hacked up to convert signals into client events.  The
> application would deal with with the signal as another event to deal and
> not as something to be dealt with by a signal handler.

This wouldn't solve the original problem I had that started all this. I wanted a
way for my process to wake up periodically and flush out the event queue to 
keep it from overflowing. For instance, what if I had a model solver that was 
going to take 7 hours to run a solve on a model. The user clicks a SOLVE widget
and its out of the event loop for 7 hours. During that time all kinds of events
could be received into the queue and not serviced causing it to eventually
overflow. I wanted to use setitimer and signal to periodically check the queue,
but the problems I had are what started this discussion.


-- 
                                   ________   _________    _______    ________
                                  / _______|  |  ____  \  |  ___  \  / _______|
Rob Gabbard (uunet!sdrc!evgabb)   | |______   | |    \  \ | |   \ | | /
Technical Development Engineer    \_______ \  | |     | | | |___/ / | |
Structural Dynamics Research Corp. ______| |  | |____/  / |  ____ \ | \_______
#include <std/disclaimer.h>       |________/  |________/  |_|    |_| \________|

mitch@mouse..westford.ccur.com (Mitchel Kuninsky) (03/06/90)

argv wrote the following:

>...long description of the problem with X and signals...
>
>Now, you could implement this yourself via "work procs"
>(see XtAddWorkProc()), but the granularity isn't fine enough.
>That is, work procs are only called when there are no events
>pending.  Implemented properly, the internals to Xt would
>check in between event delivery to event handlers.
>
>
>dan

Are you suggesting that you do an XtAddWorkProc from the signal
handler?  You really can't even do that safely because the client code
being interrupted by the signal might also be in the middle of a call
to XtAddWorkProc and the workproc queue (or whatever database is used
to keep track of them) might be in some inconsistent state.  Just about
the ONLY thing you can do safely from a signal handler is set a flag.

One solution to this problem, as you mention in your message, is to set
a flag and use a modified XtMainLoop that checks the flag upon return
from XtNextEvent before calling XtDispatchEvent.  I've implemented
something like this and the one problem I have is that the arrival of
the signal will not kick the client out of XtNextEvent so your signal
handler will not get executed until some other event comes along.
Usually, this is no problem, but, if the signal you're catching is
SIGQUIT, there probably will not be any more events coming from the
user.

The real root of this problem, at least on Unix systems, is that the
select in the Xlib tries to recover from being interrupted by a signal
by checking errno for EINTR (which is the value returned when a system
call is interrupted by a signal).  If it finds EINTR, it just dives
back into the select call.  On the surface, it looks as though you
could hack an Xlib to respond to signals by changing this behaviour so
that XtNextEvent would return with a null event (or something) if it
was interrupted and your special XtMainLoop could examine your signal
flag.

I envision a signal handler like:

	SignalHandler
	{
	    sigFlag = TRUE;
	}

and an XtMainLoop like:

	MyMainLoop()
	{
	    XEvent event;

	    for (;;)
	    {
		XtNextEvent( &event );
		if (event.type != NULL_EVENT)	/* or something like this. */
		    XtDispatchEvent( &event );
		if (sigFlag)
		{
		    process signal.
		    Inhibit signals.
		    sigFlag = FALSE;
		    Enable signals.
		}
	    }
	}

PARCEL@INTELLICORP.COM (Scott Parcel) (03/07/90)

>This wouldn't solve the original problem I had that started all this. I wanted a
>way for my process to wake up periodically and flush out the event queue to
>keep it from overflowing. For instance, what if I had a model solver that was
>going to take 7 hours to run a solve on a model. The user clicks a SOLVE widget
>and its out of the event loop for 7 hours. During that time all kinds of events
>could be received into the queue and not serviced causing it to eventually
>overflow. I wanted to use setitimer and signal to periodically check the queue,
>but the problems I had are what started this discussion.

Perhaps I'm repeat prior info, but, to process events during the 7 hour loop I'd:
   1. Setup to get SIGIO signals when X event data arrives on the event queue.
   2. Mask signals or use some other protection technique to only allow processing
      the signals and the event queue when you know its safe.  (Safe to me means
      your not in the middle of any nonreentrant functions which might be called
      during the signal handler event processing.)  If your only concerned with
      using the signal handler to process events during a 7 hour mathmatical
      computation this might be easy.
I'm doing this kind of thing and it works fairly well.  There are some more
refinements which can be needed in some situations.
-- scott parcel@intellicorp.com
-------

argv%turnpike@Sun.COM (Dan Heller) (03/07/90)

BTW, the recent discussion of XtCleanup() or XtExit() makes this
discussion even more important.  Trapping signals reliably will
aid in the programmer being able to call XtExit() as needed.

In article <23695@masscomp.ccur.com> mitch@westford.ccur.com (Mitchel Kuninsky) writes:
> argv wrote the following:

> >Now, you could implement this yourself via "work procs"
> >(see XtAddWorkProc()), but the granularity isn't fine enough.
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (Note #1)
> >That is, work procs are only called when there are no events
> >pending.  Implemented properly, the internals to Xt would
> >check in between event delivery to event handlers.
         ^^^^^^^^^^^^^^^^^^^^^^^^^ (note #2)

> Are you suggesting that you do an XtAddWorkProc from the signal
> handler?
Not exactly.  There are two ways to address this: one where Xt is
modified and the other for when Xt is not modified (e.g. you have
write the necessary functions).

I claim that the best solution is a modification to Xt.  However, I
suggested a "workaround" for the timebeing.  This is the use of "work
procs".  From the top: the first call to AddSignalHandler(sig, proc)
does:

AddSignalHandler(sig, handler)
int sig;
SIGRET (*handler)();
{
    SIGRET old_hanlder = signal(sig, _private_func); /* see below */
    /* add to a linked list (data struct, whatever) the fact that
       "handler" should be called when "sig" is delivered.
     */
    return old_handler;
}

The _private_func is static to the .c file that is implementing this
stuff.  It would have code which does:

static long signal_received = 0L; /* bitmask indicating delivered sigs */

static
_private_func(sig, ...)
int sig;
...
{
    /* save signal context, resources, etc... in a *queue* because the same
       signal may have been delivered more than once before processing.
     */
    if (!signal_received) /* no signals registered so far, add work proc */
	XtAddWorkProc(_check_sigs, NULL); /* see below */
    signal_received |= (1<<sig); /* flag this signal as received */
}

static
_check_sigs(unused)
XtPointer unused;
{
    int i;
    if (!signal_received)
	return; /* save lots of time */
    for (i == 1; i < NSIG; i++)
	if (signal_received & (1<<i)) {
	    /* this signal has been received ... look up in linked list
	       which function to call -- also retrieve saved contex.
	     */
	    while (saved_context)
		handler(i, save_context, saved_resources, etc...)
	    signal_received &= ~(1<<i);
	}
}

>   Just about
> the ONLY thing you can do safely from a signal handler is set a flag.
which is exactly what I'm doing.

> One solution to this problem, as you mention in your message, is to set
> a flag and use a modified XtMainLoop that checks the flag upon return
> from XtNextEvent before calling XtDispatchEvent.  I've implemented
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> something like this and the one problem I have is that the arrival of
> the signal will not kick the client out of XtNextEvent so your signal
> handler will not get executed until some other event comes along.

That's not the way to do it.  You *must* do this from _within_
XtNextEvent().  You should not wait till afterwards.  As my "notes"
(#1 and #2 in the beginning of my message) point out, one way of
getting out of XtNextEvent() quickly is by a work proc, but the
graunliary isn't fine enough -- you have to wait for all queued
events to be delivered before getting notified of the signal.

Thus, to really modify XtNextEvent correctly,
you have to add a test for the "signal_received" bitmask (!= 0)
at the this location within the Xt code:

void XtAppNextEvent(app, event)
        XtAppContext app;
        XEvent *event;
{
    int i, d;

    for (;;) {
add ->  if (app->signal_received != 0)
add ->      /* call the equivalent to the work proc above */
        if (app->count == 0)
            DoOtherSources(app);
        else {
            for (i = 1; i <= app->count; i++) {
	    .
	    .
	    .

Note: "signal_received" does not necessarily have to be atteched
to an application context, but let's not get into that right now.

> Usually, this is no problem, but, if the signal you're catching is
> SIGQUIT, there probably will not be any more events coming from the
> user.
Oh yeah?  If you've trapped it, you will certainly get a bunch of
events from the user.  Because he sees that the application is
not quitting, he's going to do it again, click on the "quit" button
and ask "why is this application not quitting?" while he moves
his mouse around, pushes keys and presses buttons.

> The real root of this problem, at least on Unix systems, is that the
> select in the Xlib tries to recover from being interrupted by a signal
> by checking errno for EINTR (which is the value returned when a system
> call is interrupted by a signal).  If it finds EINTR, it just dives
This is only a problem on sys-v for one.  But, Xt already does this
so it's not terribly important to continue to address this problem.

dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.

argv%turnpike@Sun.COM (Dan Heller) (03/07/90)

In article <1174@sdrc.UUCPevgabb@sdrc.UUCP (Rob Gabbard) writes:

    This wouldn't solve the original problem I had that started all this. I
    wanted a way for my process to wake up periodically and flush out the
    event queue to keep it from overflowing. For instance, what if I had a
    model solver that was going to take 7 hours to run a solve on a model.
    The user clicks a SOLVE widget and its out of the event loop for 7
    hours. During that time all kinds of events could be received into the
    queue and not serviced causing it to eventually overflow. I wanted to
    use setitimer and signal to periodically check the queue, but the
    problems I had are what started this discussion.

There are two ways to address this problem.  First and foremost,
you need to redesign this user interface.  You should never have
a function that takes that much time an run it in a callback
routine.  That type of processing should either be done in a
separate process, or you need to read and dispatch events within
that cpu-intensive routine.

Secondly, given that you *have* to run this 7-hour routine, you
should not decide to use the timer signal to test for user events.
If you do, you're perpetuating the same problem that started this.
That is, the signal handler may still be interfering with any Xlib
calls you may be executing since you would be using Xlib to test
for and deal with events.

Let me be more explicit.  If you want to write a long time-consuming
or CPU intensive routine, then the problem of reading/waiting for
events should be handled manually.  Edit the cpu-intensive routine
and insert locations in which you can call XPending(), XReadEvent()
or XDispatchEvent() as needed.  Do not use a itimer to do this for you.

If you can't edit that routine, then you should spawn a new process
and execute it.  I doubt this is the case, however.  If you are using
Xview, the Notifier is really handy for stuff like this and there is
a chapter that addresses this type of activity in the XView Programmer's
Manual (Volume 7 of the O'Reilly series on X programming).
dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.

hvr@kimba.Sun.COM (Heather Rose) (03/08/90)

In article <1174@sdrc.UUCP> evgabb@sdrc.UUCP (Rob Gabbard) writes:
>In article <4429@quanta.eng.ohio-state.edu>, JONESD@kcgl1.eng.ohio-state.edu (David Jones) writes:
>> Instead of hacking up tookits to allow your application to add signal handlers,
>> I'd rather see them hacked up to convert signals into client events.  The
>> application would deal with with the signal as another event to deal and
>> not as something to be dealt with by a signal handler.
>
>This wouldn't solve the original problem I had that started all this. I wanted a
>way for my process to wake up periodically and flush out the event queue to 
>keep it from overflowing. For instance, what if I had a model solver that was 
>going to take 7 hours to run a solve on a model. The user clicks a SOLVE widget
>and its out of the event loop for 7 hours. During that time all kinds of events
>could be received into the queue and not serviced causing it to eventually
>overflow. I wanted to use setitimer and signal to periodically check the queue,
>but the problems I had are what started this discussion.

In this example, you should use two processes.  One does the 7 hour calculation,
the other handles the window interface.

If you were to use XView for this, you would not even need signal handling
since you can set up an input function to read the output of the program
when it's available.

A two process model is much easier to implement and more reliable than 
what you've proposed.

As an added benefit, you could set the "nice" value of your calculating
process so it does not kill your user interaction time while processing.

Regards,

Heather

jeff@samna.UUCP (jeff) (03/08/90)

In article <23695@masscomp.ccur.com> mitch@westford.ccur.com (Mitchel Kuninsky) writes:
:I envision a signal handler like:
:
:	SignalHandler
:	{
:	    sigFlag = TRUE;
:	}
:
:and an XtMainLoop like:
:
:	MyMainLoop()
:	{
:	    XEvent event;
:
:	    for (;;)
:	    {
:(1)		XtNextEvent( &event );
:		if (event.type != NULL_EVENT)	/* or something like this. */
:		    XtDispatchEvent( &event );
:		if (sigFlag)
:		{
:		    process signal.
:		    Inhibit signals.
:		    sigFlag = FALSE;
:(2)		    Enable signals.
:		}
:	    }
:	}

This may be kind of nit-picking, but ... You have a nasty race here.
What if a signal arrives between the re-enabling of signals at (2) and
the next call to XtNextEvent at (1).

I believe this points out the need to have the signal-handling more
tightly integrated with the Intrinsics code.

---
Jeff

evgabb@sdrc.UUCP (Rob Gabbard) (03/08/90)

In article <132645@sun.Eng.Sun.COM>, hvr@kimba.Sun.COM (Heather Rose) writes:
> In this example, you should use two processes.  One does the 7 hour calculation,
> the other handles the window interface.

We are using a similar architecture to this model you are descibing.
What if your window interface was doing a ray trace into a pixmap that took 
a longer time to do than your timer interval ? That is where the real danger is.

> If you were to use XView for this, you would not even need signal handling

Sorry, we're DECWindows/Motif based. 

-- 
                                   ________   _________    _______    ________
                                  / _______|  |  ____  \  |  ___  \  / _______|
Rob Gabbard (uunet!sdrc!evgabb)   | |______   | |    \  \ | |   \ | | /
Technical Development Engineer    \_______ \  | |     | | | |___/ / | |
Structural Dynamics Research Corp. ______| |  | |____/  / |  ____ \ | \_______
#include <std/disclaimer.h>       |________/  |________/  |_|    |_| \________|

nick@bischeops.UUCP (Nick Bender) (03/10/90)

In article <203@samna.UUCP>, jeff@samna.UUCP (jeff) writes:
> In article <23695@masscomp.ccur.com> mitch@westford.ccur.com (Mitchel Kuninsky) writes:
> :I envision a signal handler like:
> :
> :	SignalHandler
> :	{
> :	    sigFlag = TRUE;
> :	}
> :
> :and an XtMainLoop like:
> :
> :	MyMainLoop()
> :	{
> :	    XEvent event;
> :
> :	    for (;;)
> :	    {
> :(1)		XtNextEvent( &event );
> :		if (event.type != NULL_EVENT)	/* or something like this. */
> :		    XtDispatchEvent( &event );
> :		if (sigFlag)
> :		{
> :		    process signal.
> :		    Inhibit signals.
> :		    sigFlag = FALSE;
> :(2)		    Enable signals.
> :		}
> :	    }
> :	}
> 
> This may be kind of nit-picking, but ... You have a nasty race here.
> What if a signal arrives between the re-enabling of signals at (2) and
> the next call to XtNextEvent at (1).
> 
> I believe this points out the need to have the signal-handling more
> tightly integrated with the Intrinsics code.
> 
> ---
> Jeff


I've been thinking a little about this problem and had the following idea:

  Boolean sigSet[MAX_SIGNALS];
  static struct timeval pollTimeout = { 0, 0 };
  static struct timeval *timeoutPtr;
  static fd_set *inputSetPtr;

  void SetSignalFlag (sig)
  {
    sigSet[sig] = TRUE;
    timeoutPtr = &pollTimeout;
  }

  SuperLoop()
  {
    while (whatever)
    {
      timeoutPtr = NextTimeoutTime();
(1)   UnblockSignals();
      nready = select (NOFILE, inputSetPtr, 0, 0, timeoutPtr);
(2)   BlockSignals();
      ExecuteRunnableThreads();
    }
  }

So the question is, will the select always return with EINTR or after
a polling call? Of course it doesn't deliver true async execution be
cause it assumes that all you do is set the flag in the interrupt handler,
but does it eliminate the race condition (does it *always* prevent the
select call from hanging indefinitely given no scheduled timeouts)?
Do I *always* get what I want no matter what point the signal or signals
hit between (1) and (2) ?

Any thoughts ?

PS. I'm assuming the BSD style sigvector signal mechanisms (you know,
reliable delivery and all that...).

path nick%bischeops@uunet.uu.net

mouse@LARRY.MCRCIM.MCGILL.EDU (der Mouse) (03/11/90)

> I've been thinking a little about this problem [races in signal
> delivery] and had the following idea:

>	  Boolean sigSet[MAX_SIGNALS];
>	  static struct timeval pollTimeout = { 0, 0 };
>	  static struct timeval *timeoutPtr;
>	  static fd_set *inputSetPtr;
>	
>	  void SetSignalFlag (sig)
>	  {
>	    sigSet[sig] = TRUE;
>	    timeoutPtr = &pollTimeout;
>	  }
>	
>	  SuperLoop()
>	  {
>	    while (whatever)
>	    {
>	      timeoutPtr = NextTimeoutTime();
>	(1)   UnblockSignals();
>	      nready = select (NOFILE, inputSetPtr, 0, 0, timeoutPtr);
>	(2)   BlockSignals();
>	      ExecuteRunnableThreads();
>	    }
>	  }

> So the question is, will the select always return with EINTR or after
> a polling call?  Of course it doesn't deliver true async execution
> because it assumes that all you do is set the flag in the interrupt
> handler, but does it eliminate the race condition (does it *always*
> prevent the select call from hanging indefinitely given no scheduled
> timeouts)?  Do I *always* get what I want no matter what point the
> signal or signals hit between (1) and (2) ?

Alas, no.  If a signal arrives after the last argument to select has
been pushed on the stack (I'm assuming a stack-based argument passing
scheme, which is common enough to be reasonable) but before select has
been called, you'll lose.  (Or, if possible, after select has been
called but before it's entered the kernel.)

Suggested fix:

  Boolean sigSet[MAX_SIGNALS];
  static struct timeval pollTimeout = { 0, 0 };
  static struct timeval timeoutValue;
  static fd_set *inputSetPtr;

  void SetSignalFlag (sig)
  {
    sigSet[sig] = TRUE;
    timeoutValue = pollTimeout;
  }

  SuperLoop()
  {
    while (whatever)
    {
      timeoutValue = NextTimeoutTime();
      UnblockSignals();
      nready = select (NOFILE, inputSetPtr, 0, 0, &timeoutValue);
      BlockSignals();
      ExecuteRunnableThreads();
    }
  }

That is, instead of changing the pointer, change the value pointed to.
(I have assumed your C supports structure assignment and I've also
changed the return value of NextTimeoutTime from struct timeval * to
plain struct timeval.)  This works because the pointer is not followed
until select() is inside the kernel and signals have therefore been
(implicitly) blocked.

This doesn't work if you have a (hypothetical, as far as I know) select
that returns the remaining time in the timeout time structure, and you
actually use that information, because a signal can arrive after the
select returns for some other reason and bash timeoutValue.  But since
as far as I know nobody's select actually does that yet, and therefore
no code depends on it, you should be OK.

Select really should take a sixth argument indicating a signal mask and
also set the signal mask to that value atomically with the waiting for
some file descriptor to come ready or the timeout to expire, and reset
it to its previous value upon return, something like sigpause() and the
current select() rolled into one.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

thinman@cup.portal.com (Lance C Norskog) (03/18/90)

You're all going about this the wrong way.  The original poster with
the 7-hour operation can easily do what I did:

I wanted to get into X programming, and I wanted to look at some PostScript
stuff, so I brought up the Crispin Goswell postscripter X driver.
It doesn't handle X input well, but Berkeley sockets and AT&T Streams
both have a facility to deliver a signal on receipt of data on a network
connection.  Under SYSV it's SIGPOLL, under BSD I think it's SIGASYNC.

I don't use XtMainLoop at all.  The init routine does this:
	
	for (fd=3; fd<100; fd++)
		if fd is a network socket
			make fd send the input-available signal

	This is, admittedly, grody, but Xt won't find the fd's for me.

The signal handler for this sig sets a flag and exits.

Periodically you check that flag and run this routine:
	
	XtInputMask mask;

	while(mask = XtAppPending(applicationcontext))
		XtAppProcessEvent(applicationcontext, mask)

Under SYSV, this pair stops sigs and then handles them:
	sighold(SIGPOLL);	/* single-thread this routine */
	sigrelse(SIGPOLL);	/* take more signals now */

Using polling when interrupts are available is extremely goony.
If you follow the other suggestions in this thread, your 7-hour calculation
will become 8 or 9 or 10 hours, because of all the polling involved.

The only problem with this scheme under SYSV is that signals always make
system calls return with EINTR, so I had to guard all file I/O with 
sighold/sigrelse pairs and play games with the command line I/O handler.
Also, there are sighold/sigrelse pairs around all X driver routines.

Lance