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

signals, threads, SIGPIPE, sigpending (fun!)

253 views
Skip to first unread message

Frank Cusack

unread,
May 28, 2003, 6:12:56 PM5/28/03
to
Let's say I have some library code that might be used by a threaded app.
The library is thread-safe. It talks to a server via the network. Now,
sometimes, write() might generate SIGPIPE (server closes connection).

I don't want the application to receive SIGPIPE from my library. I can't
just install a handler (even SIG_IGN) because that would affect all threads.
I don't think this is possible, but I'm hunting for ideas.

I can *block* SIGPIPE (blocking is per-thread) before calling write(),
and then test for it's pendingness after returning. If it's not
pending, just unblock. If it is pending, you know that it was this
thread's write() call that generated the signal, since SIGPIPE is a
"synchronous" signal. (Also, write() will return EPIPE.)

That would be useful info if you could simply "unpending" the signal.

ret = 0;
sigprocmask(SIG_BLOCK, [SIGPIPE ...], oset);
if (write() < 0) {
if ((ret = errno) == EPIPE) {
sigreceived(SIGPIPE); // see below
}
}
sigprocmask(SIG_SETMASK, oset, NULL);
return ret;

But you can't do that. The closest thing seems to be something like:

...
// instead if sigreceived(), from above
struct sigaction sa, osa;
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, sa, osa); // screws other threads
}
}
// sigprocmask() is guaranteed to deliver at least 1 of any pending signals
sigprocmask(SIG_SETMASK, oset, NULL);
// If SIGPIPE not delivered before sigprocmask() returns,
// it must be (is guaranteed to be) delivered before sigaction() (?)
sigaction(SIGPIPE, osa, NULL); // race if SIGPIPE not delivered

Is it possible to write a complicated signal handler which can test the
running thread? That way, if something like this happens:

thread 1 thread 2
int pipe_tid;
struct sigaction osa;
sem_t sem;

sem_wait(&sem);
sig_tid = tid;
sigprocmask(SIG_BLOCK, [SIGPIPE ...], oset);
errno = 0;
write(); => SIGPIPE
if (errno == EPIPE)
sigaction(SIGPIPE, sa, osa);
write(); => SIGPIPE
sigprocmask(SIG_SETMASK, oset);
sem_post(&sem);

and then

handler()
{
// are we in the handler because of the library thread?
// how to get current tid?
if (tid == pipe_tid && errno == EPIPE)
return;
handle SIGPIPE per osa;
return;
}

Also, what happens in thread 2, since SIGPIPE is already pending?
Is SIGPIPE delivered to that thread? Don't POSIX thread semantics
say that, yes, it must be? But don't POSIX thread semantics also say
that a SIGPIPE must be delivered to thread 1? This seems at odds
with the Unix signal implementation as a bitmask of pending signals.

Note that the SIGPIPE from thread 1 is *not* delivered to thread 2,
even though thread 2 is some other thread that can receive SIGPIPE,
because SIGPIPE is synchronous.

I think the above would work, if there's a way to get the current tid
in the signal handler, which I don't think is possible (portably).

So I propose that sigreceived() could be a useful new system call.

/fc

Marc Rochkind

unread,
May 28, 2003, 9:41:57 PM5/28/03
to

"Frank Cusack" <fcu...@fcusack.com> wrote in message
news:x5y4r3e...@vger.corp.google.com...

[snip]

>
> So I propose that sigreceived() could be a useful new system call.
>

Oh, yes, there are not nearly enough signal functions! More! More!

Wait... I'm kidding!

Well, only partly.

Anyway, I don't like the idea of trying to filter out the library-caused
SIGPIPEs to hide that unpleasantness from the app. I'm afraid it won't be
100% solid, and someday somebody will be trying to debug something having to
do with signals in the app, or trying to get another library to work with
yours, and they will curse you when they find out what's going on beneath
the covers.

I would just document the fact that your library occasionally generates
these signals, and advise the application writers to set them to be ignored.
If you think the app writers don't know how to do that properly, give them a
function they can call that does the whole job. SIGPIPEs are totally
unnecessary if the app (or you) are checking carefully for error returns
from write and the various send system calls. (They were put into the system
ages ago to do something that UNIX otherwise doesn't do: Make an error
return fatal by default, so that if a process being piped to aborted, the
whole pipeline would be rapidly cleaned up, instead of wasting time writing
tons of output to nowheresville. This was many years before sockets came
along. In fact, before UNIX application programming even came along.)

--Marc


Kurtis D. Rader

unread,
May 28, 2003, 10:40:43 PM5/28/03
to
On Wed, 28 May 2003 15:12:56 +0000, Frank Cusack wrote:

> Let's say I have some library code that might be used by a threaded app.
> The library is thread-safe. It talks to a server via the network. Now,
> sometimes, write() might generate SIGPIPE (server closes connection).
>
> I don't want the application to receive SIGPIPE from my library. I can't
> just install a handler (even SIG_IGN) because that would affect all
> threads. I don't think this is possible, but I'm hunting for ideas.

I second Mr. Rochkind's recommendation. I see no way for you to hide
the fact your code might generate a SIGPIPE that does not introduce
race conditions and or debugging problems. Simply document that any
application using your library must ignore SIGPIPE (which is a good
idea anyway for any well behaved app that checks the return codes of
its system calls).

Also, note that in the case of POSIX threads the signal mask is an
attribute of the LWP, not the process (In this context a LWP stands for
"light-weight process" and is only equivalent to a thread in a 1:1 thread
to LWP model). So your solution will simply guarantee the SIGPIPE is sent
to a different LWP. Lastly, a SIGPIPE is not a synchronous signal. So
there is no requirement it be delivered to the thread that generated
the event even if you don't block the signal.

Shaun Clowes

unread,
May 28, 2003, 11:44:18 PM5/28/03
to

"Frank Cusack" <fcu...@fcusack.com> wrote in message
news:x5y4r3e...@vger.corp.google.com...
> Let's say I have some library code that might be used by a threaded app.
> The library is thread-safe. It talks to a server via the network. Now,
> sometimes, write() might generate SIGPIPE (server closes connection).
>
> I don't want the application to receive SIGPIPE from my library. I can't
> just install a handler (even SIG_IGN) because that would affect all
threads.
> I don't think this is possible, but I'm hunting for ideas.

Believe it or not I had _exactly_ the same problem, you wouldn't happen to
be implementing an arm library would you? :)

Anyways, the way we eventually solved our issues was to open the pipe for
O_RDWR in the library, thus you'll never receive a SIGPIPE on writes to it.
Some unixes define O_RDWR as being invalid on a pipe but I haven't had any
problem on the 6 unixes we work with here (Solaris, AIX, HPUX, Irix,
UnixWare and Linux).

> I can *block* SIGPIPE (blocking is per-thread) before calling write(),
> and then test for it's pendingness after returning. If it's not
> pending, just unblock. If it is pending, you know that it was this
> thread's write() call that generated the signal, since SIGPIPE is a
> "synchronous" signal. (Also, write() will return EPIPE.)

rt_sigtimedwait should be what you're looking for on Linux, I'm not sure if
there is anything suitable on other platforms.

Cheers,
Shaun


Frank Cusack

unread,
May 29, 2003, 12:03:32 AM5/29/03
to
On Wed, 28 May 2003 19:40:43 -0700 "Kurtis D. Rader" <kra...@skepticism.us> wrote:
> Also, note that in the case of POSIX threads the signal mask is an
> attribute of the LWP, not the process (In this context a LWP stands for
> "light-weight process" and is only equivalent to a thread in a 1:1 thread
> to LWP model).

Yes. I explicitly said that in my post.

> So your solution will simply guarantee the SIGPIPE is sent
> to a different LWP. Lastly, a SIGPIPE is not a synchronous signal. So
> there is no requirement it be delivered to the thread that generated
> the event even if you don't block the signal.

SUSv3 (System Interfaces->Sockets->Signals):

The SIGPIPE signal shall be sent to a thread that attempts to send
data on a socket that is no longer able to send. In addition, the
send operation fails with the error [EPIPE].

Unfortunately, I cannot find such text for writing to a non-socket, but
SUSv2/v3 (System Interfaces->write()) says:

[EPIPE]
An attempt is made to write to a pipe or FIFO that is not open
for reading by any process, or that only has one end open. A
SIGPIPE signal shall also be sent to the thread.

SUSv3 also says (System Interfaces->General Concepts->
Signal Concepts->Signal Generation and Delivery):

At the time of generation, a determination shall be made whether
the signal has been generated for the process or for a specific
thread within the process. Signals which are generated by some
action attributable to a particular thread, such as a hardware
fault, shall be generated for the thread that caused the signal to
be generated.

write() is clearly an action attributable to a particular thread. Of
course, SIGPIPE may be delivered async (kill()), but I think I treat this
properly (I test for EPIPE, which doesn't happen for an async SIGPIPE).

/fc

those who know me have no need of my name

unread,
May 29, 2003, 12:26:57 AM5/29/03
to
in comp.unix.programmer i read:

>Let's say I have some library code that might be used by a threaded app.
>The library is thread-safe. It talks to a server via the network. Now,
>sometimes, write() might generate SIGPIPE (server closes connection).
>
>I don't want the application to receive SIGPIPE from my library.

do the work in a separate process.

--
bringing you boring signatures for 17 years

Kurtis D. Rader

unread,
May 29, 2003, 12:29:43 AM5/29/03
to
On Wed, 28 May 2003 21:03:32 +0000, Frank Cusack wrote:

> SUSv3 (System Interfaces->Sockets->Signals):
>
> The SIGPIPE signal shall be sent to a thread that attempts to send
> data on a socket that is no longer able to send. In addition, the
> send operation fails with the error [EPIPE].
>
> Unfortunately, I cannot find such text for writing to a non-socket, but
> SUSv2/v3 (System Interfaces->write()) says:
>
> [EPIPE] An attempt is made to write to a pipe or FIFO that is not
> open for reading by any process, or that only has one end open. A
> SIGPIPE signal shall also be sent to the thread.
>
> SUSv3 also says (System Interfaces->General Concepts-> Signal
> Concepts->Signal Generation and Delivery):
>
> At the time of generation, a determination shall be made whether the
> signal has been generated for the process or for a specific thread
> within the process. Signals which are generated by some action
> attributable to a particular thread, such as a hardware fault, shall
> be generated for the thread that caused the signal to be generated.
>
> write() is clearly an action attributable to a particular thread. Of
> course, SIGPIPE may be delivered async (kill()), but I think I treat this
> properly (I test for EPIPE, which doesn't happen for an async SIGPIPE).

Interesting. I consulted an actual implementation (a proprietary
UNIX, not Linux or FreeBSD) whose software engineers I respect. That
implementation does not guarantee (unless I've misread the code) that the
SIGPIPE will be delivered to the process that issued the write(2) call.

Also, note that your last quote from SUSv3 does not explicitly encompass
SIGPIPE but does encompass SIGFPE, SIGSEGV and SIGBUS. It's possible the
implementation I referenced is broken with respect to not guaranteeing
delivery to the thread that generated the SIGPIPE in the case where
SIGPIPE is not blocked by the thread. Nonetheless, I see nothing in the
standards that forbid delivering the signal to another thread which is
not blocking the signal.

All in all I would be very reluctant to rely on the behavior your design
requires. It seems more prudent to me to simply document that your
library might generate a SIGPIPE and therefore require the cooperation
of the application that uses it (e.g., by ignoring it). Alternatively,
do as Mr. Cowles suggested (if your communication protocol allows it)
and open the pipe O_RDWR so that a SIGPIPE will never be generated.

Frank Cusack

unread,
May 29, 2003, 12:59:09 AM5/29/03
to
On Thu, 29 May 2003 03:44:18 GMT "Shaun Clowes" <del...@no.spam.for.me.progsoc.org> wrote:
> "Frank Cusack" <fcu...@fcusack.com> wrote in message
> news:x5y4r3e...@vger.corp.google.com...
>> Let's say I have some library code that might be used by a threaded app.
>> The library is thread-safe. It talks to a server via the network. Now,
>> sometimes, write() might generate SIGPIPE (server closes connection).
>>
>> I don't want the application to receive SIGPIPE from my library. I can't
>> just install a handler (even SIG_IGN) because that would affect all
> threads.
>> I don't think this is possible, but I'm hunting for ideas.
>
> Believe it or not I had _exactly_ the same problem, you wouldn't happen to
> be implementing an arm library would you? :)

nss/pam libraries. Otherwise I'd find Marc Rochkind's suggestion of
just documenting it to be fine. As we're talking about libraries loaded
by other libraries :-) and the fact that they are used pervasively, that
[otherwise good and correct] solution just won't work. You can't expect
every little tool that might use (say) getpwbynam() to expect SIGPIPE.
And surely you can see where you might want the app continue rather than
terminate.

> rt_sigtimedwait should be what you're looking for on Linux, I'm not sure if
> there is anything suitable on other platforms.

Thanks! sigtimedwait() is POSIX and is pretty much what I wanted
sigreceived() should do. (dunno about rt_sigtimedwait()). So to repeat,
here is the proposed code, I appreciate comments on races or other
incorrectness (including further args that "it just won't work"):

int ret = 0;
struct sigset_t set, oset;
struct timespec timeout = { .tv_nsec = 1 }; // minimal value

sigemptyset(&set);
setaddset(&set, SIGPIPE);
sigprocmask(SIG_BLOCK, &set, &oset);

if (write() < 0)
if ((ret = errno) == EPIPE)

sigtimedwait(&set, NULL, &timeout); // deliver SIGPIPE

sigprocmask(SIG_SETMASK, oset, NULL);
return ret;

The timeout is only so that multiple write()'s which might return multiple
EPIPE's, but only deliver one signal because an already pending SIGPIPE
hasn't been delivered yet, don't hang on a just plain sigwait().

Here's some possible race conditions:

case 1: second SIGPIPE is generated in some other code, nothing is
done since it's already pending. In this case, the second SIGPIPE is
lost. Damn. The second write() does return EPIPE though.

case 2: second SIGPIPE is generated in another thread in this code,
nothing is done since it's already pending. Fine. One sigtimedwait()
returns with the signal, the other just returns.

case 3: second SIGPIPE is generated in some other code, and delivered
to that thread instead of this one. Fine.

case 4: second SIGPIPE is generated in another thread in this code,
and delivered to that thread instead of this one. Fine.

Out of these 4 cases, the first is probably the most likely implementation,
as well as being the only case that isn't quite right. :-( But, unix
apps already know that multiple signals may be combined, so it seems ok.
And apps don't have to worry about errant SIGPIPEs, just the normal case
where they might only get one delivery for multiple signal generations.

/fc

Frank Cusack

unread,
May 29, 2003, 1:00:22 AM5/29/03
to
On 29 May 2003 04:26:57 GMT those who know me have no need of my name <not-a-rea...@usa.net> wrote:
> in comp.unix.programmer i read:
>
>>Let's say I have some library code that might be used by a threaded app.
>>The library is thread-safe. It talks to a server via the network. Now,
>>sometimes, write() might generate SIGPIPE (server closes connection).
>>
>>I don't want the application to receive SIGPIPE from my library.
>
> do the work in a separate process.

Thank you, great idea. Too expensive for this case. Too much fork/exec.

/fc

Frank Cusack

unread,
May 29, 2003, 1:10:31 AM5/29/03
to
On Wed, 28 May 2003 21:29:43 -0700 "Kurtis D. Rader" <kra...@skepticism.us> wrote:
> On Wed, 28 May 2003 21:03:32 +0000, Frank Cusack wrote:
>> SUSv3 also says (System Interfaces->General Concepts-> Signal
>> Concepts->Signal Generation and Delivery):
>>
>> At the time of generation, a determination shall be made whether the
>> signal has been generated for the process or for a specific thread
>> within the process. Signals which are generated by some action
>> attributable to a particular thread, such as a hardware fault, shall
>> be generated for the thread that caused the signal to be generated.
>>
>> write() is clearly an action attributable to a particular thread. Of
>> course, SIGPIPE may be delivered async (kill()), but I think I treat this
>> properly (I test for EPIPE, which doesn't happen for an async SIGPIPE).

...


> Also, note that your last quote from SUSv3 does not explicitly encompass
> SIGPIPE but does encompass SIGFPE, SIGSEGV and SIGBUS. It's possible the
> implementation I referenced is broken with respect to not guaranteeing
> delivery to the thread that generated the SIGPIPE in the case where
> SIGPIPE is not blocked by the thread. Nonetheless, I see nothing in the
> standards that forbid delivering the signal to another thread which is
> not blocking the signal.

The way I read it is, "*such as* a hardware fault". But eg SIGSEGV is not
a hardware fault (is it?). I agree, it does not specifically mention
SIGPIPE, but I don't see how there can be an argument that SIGPIPE isn't
attributable to a particular thread--the one that called write(), the same
way SIGSEGV is attributable to a particular thread--the one that tried
to access out of range memory.

> All in all I would be very reluctant to rely on the behavior your design
> requires. It seems more prudent to me to simply document that your
> library might generate a SIGPIPE and therefore require the cooperation
> of the application that uses it (e.g., by ignoring it). Alternatively,
> do as Mr. Cowles suggested (if your communication protocol allows it)
> and open the pipe O_RDWR so that a SIGPIPE will never be generated.

Thanks. I would find the documentation route acceptable except that
no one ever reads documentation. :-) Seriously though, the library is
of the nature that users of it will be surprised by SIGPIPE, even if
documented.

/fc

Frank Cusack

unread,
May 29, 2003, 1:17:28 AM5/29/03
to

hmm.. although, I could fork/exec on the first call only. Subsequent
calls through the library would be serialized (because of a single
child reader) but even that could be handled somewhat ok by having a
simple process manager ala the apache model.

Personally, I think sigtimedwait() seems better.

/fc

Shaun Clowes

unread,
May 29, 2003, 1:44:47 AM5/29/03
to

"Frank Cusack" <fcu...@fcusack.com> wrote in message
news:x5yznl6...@vger.corp.google.com...

> On Thu, 29 May 2003 03:44:18 GMT "Shaun Clowes"
<del...@no.spam.for.me.progsoc.org> wrote:
> > "Frank Cusack" <fcu...@fcusack.com> wrote in message
> > news:x5y4r3e...@vger.corp.google.com...
> >> Let's say I have some library code that might be used by a threaded
app.
> >> The library is thread-safe. It talks to a server via the network.
Now,
> >> sometimes, write() might generate SIGPIPE (server closes connection).
> >>
> >> I don't want the application to receive SIGPIPE from my library. I
can't
> >> just install a handler (even SIG_IGN) because that would affect all
> > threads.
> >> I don't think this is possible, but I'm hunting for ideas.
> >
> > Believe it or not I had _exactly_ the same problem, you wouldn't happen
to
> > be implementing an arm library would you? :)
>
> nss/pam libraries. Otherwise I'd find Marc Rochkind's suggestion of
> just documenting it to be fine. As we're talking about libraries loaded
> by other libraries :-) and the fact that they are used pervasively, that
> [otherwise good and correct] solution just won't work. You can't expect
> every little tool that might use (say) getpwbynam() to expect SIGPIPE.
> And surely you can see where you might want the app continue rather than
> terminate.

Maybe it's just me but I can't fathom why you wouldn't just open the pipe
for read and write so that you don't get SIGPIPE at all?

Cheers,
Shaun


Shaun Clowes

unread,
May 29, 2003, 2:14:58 AM5/29/03
to

"Shaun Clowes" <del...@no.spam.for.me.progsoc.org> wrote in message
news:j7hBa.334$Ve4....@news.optus.net.au...

Sorry, I just noticed it was a socket and not a pipe, fair enough. This is a
hairy problem when I think about it, I can't see an obvious way to avoid all
of the race conditions, particularly given that SIGPIPE can result from
connect() too on Linux (at least it looks that way from the kernel source.

Cheers,
Shaun


Frank Cusack

unread,
May 29, 2003, 7:55:08 AM5/29/03
to
On Thu, 29 May 2003 06:14:58 GMT "Shaun Clowes" <del...@no.spam.for.me.progsoc.org> wrote:
> hairy problem when I think about it, I can't see an obvious way to avoid all
> of the race conditions, particularly given that SIGPIPE can result from
> connect() too on Linux (at least it looks that way from the kernel source.

I wrap calls to write() with sigprocmask(). I receive any pending
SIGPIPE. If I eat the SIGPIPE, it's guaranteed to have been generated
by my write() call. There may be additional SIGPIPE's that I catch,
but that's unix signal semantics.

What race is there?

thanks
/fc

Barry Margolin

unread,
May 29, 2003, 10:38:40 AM5/29/03
to
In article <x5yu1be...@vger.corp.google.com>,

Frank Cusack <fcu...@fcusack.com> wrote:
>The way I read it is, "*such as* a hardware fault". But eg SIGSEGV is not
>a hardware fault (is it?).

It's a fault that's normally detected by the memory management unit, just
as SIGFPE is a fault that's normally detected by the floating point unit.
I think both of them are considered hardware faults. Contrast them with
SIGINT and SIGCHLD, which are purely software issues.

--
Barry Margolin, barry.m...@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Shaun Clowes

unread,
May 29, 2003, 6:14:28 PM5/29/03
to

"Frank Cusack" <fcu...@fcusack.com> wrote in message
news:x5yhe7e...@vger.corp.google.com...

Perhaps I'm completely confused but in a multi threaded environment how do
you know that some other thread didn't cause the SIGPIPE to be received?
Signals are per process, not per thread (unless I've really missed
something)

Cheers,
Shaun


Frank Cusack

unread,
May 29, 2003, 9:09:06 PM5/29/03
to
On Thu, 29 May 2003 22:14:28 GMT "Shaun Clowes" <del...@no.spam.for.me.progsoc.org> wrote:
>> What race is there?
>
> Perhaps I'm completely confused but in a multi threaded environment how do
> you know that some other thread didn't cause the SIGPIPE to be received?
> Signals are per process, not per thread (unless I've really missed
> something)

Because SIGPIPE and others (eg SIGSEGV) are delivered to the thread
which caused it. In another part of this thread, Kurtis Rader says
that might not be guaranteed, and I'll agree that if that assumption
isn't correct then my solution is broken.

For the sake of discussion, let's say that SIGPIPE is delivered to the
same thread. I'll test that in a few days and report back.

/fc

Kurtis D. Rader

unread,
May 30, 2003, 12:25:08 AM5/30/03
to
On Wed, 28 May 2003 22:10:31 +0000, Frank Cusack wrote:

> The way I read it is, "*such as* a hardware fault". But eg SIGSEGV is
> not a hardware fault (is it?).

A SIGSEGV is a "hardware" fault. It's generated as the result of the
memory management unit (MMU) of the hardware being unable to create a
valid physical address for a given virtual address.

> I agree, it does not specifically mention SIGPIPE, but I don't see how
> there can be an argument that SIGPIPE isn't attributable to a particular
> thread--the one that called write(), the same way SIGSEGV is attributable
> to a particular thread--the one that tried to access out of range memory.

I agree. However, the implementation whose code I referenced does
not appear to. It's not clear to me at this time whether your and my
interpretation are correct, the code I'm looking at is wrong, or I'm
simply misinterpreting the code.

> Thanks. I would find the documentation route acceptable except that no
> one ever reads documentation. :-) Seriously though, the library is of
> the nature that users of it will be surprised by SIGPIPE, even if
> documented.

Consider the case where there are two threads in the process. Your code
blocks SIGPIPE in thread "A". Thread "B" also has SIGPIPE blocked. Thread
"A" generates a SIGPIPE followed by thread "B" during the interval where
the signal is blocked. Only one instance will be queued for the process
(assuming the typical non-queued behavior). If the user of your library is
unaware of your codes behavior they might be surprised when their SIGPIPE
is never delivered. I don't see how you can avoid documenting the fact your
code can generate a SIGPIPE.

Frank Cusack

unread,
Aug 4, 2003, 10:43:08 PM8/4/03
to
On Thu, 29 May 2003 18:09:06 -0700 Frank Cusack <fcu...@fcusack.com> wrote:
> For the sake of discussion, let's say that SIGPIPE is delivered to the
> same thread. I'll test that in a few days and report back.

So a few days has become a few weeks. But I have results. And they
are not good.

glibc-2.1.3: SIGPIPE always goes to thread generating it
glibc-2.3.2: depends on which threads are blocking SIGPIPE
Solaris 9_u2: SIGPIPE does strange things.

Regardless of what is or is not supposed to work, the tested behavior
isn't consistent and so the answer is that you cannot count on SIGPIPE
to go to the "generating thread".

So how is a threaded library that might generate SIGPIPE supposed to
prevent the caller from receiving it? Is documentation the only way?

In the code below, -DPIPESRV doesn't change the behavior; the server
thread NEVER receives a SIGPIPE. Strange. Any ideas here?

-DPIPEMAIN changes the SIGPIPE receiver like this:

-DPIPEMAIN (!-DPIPEMAIN)
glibc-2.1.3 client client
glibc-2.3.2 client server
Solaris 9_u2 client client/both (!)

So, to restate what I mentioned in the leading paragraph, with
glibc-2.1.3, SIGPIPE does always act synchronously. With glibc-2.3.2
and Solaris, SIGPIPE acts synchronously if there is no other thread
handling SIGPIPE.

With glibc-2.3.2, if another thread can handle a SIGPIPE, it goes
there. I can understand this.

With Solaris it's really strange. The client thread ALWAYS gets SIGPIPE.
But sometimes, another thread does as well, as best as I can tell:

[root@brak]# gcc -o block -lpthread -lrt -lsocket -lnsl block.c
[root@brak]# for f in 0 1 2 3 4 5 6 7 8 9 ; do ./block ; done
SIGPIPE delivered to client
SIGPIPE delivered to client
SIGPIPE delivered to client
Broken Pipe
SIGPIPE delivered to client
SIGPIPE delivered to client
SIGPIPE delivered to client
SIGPIPE delivered to client
SIGPIPE delivered to client
SIGPIPE delivered to client
SIGPIPE delivered to client

This seems really really wrong.

Another note, without any #defines (like above), if I uncomment the
call to nanosleep(), SIGPIPE is always delivered twice! ie, I always
get both messages

SIGPIPE delivered to client
Broken Pipe

With -DPIPEMAIN, I get only the "delivered" message and a 1s pause, as
I would expect.

Doesn't this seem like a Solaris bug? Here's the code in its entirety:

/*
* block.c
* gcc -o block -lpthread -lrt -lsocket -lnsl block.c
* test for thread handling of sigpipe
*/

#define _POSIX_PTHREAD_SEMANTICS

#include <pthread.h>
#include <sched.h>

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <time.h>

void client_thread(void *);
void server_thread(void *);

#define GO_SERVER 0
#define GO_CLIENT 1

int go = GO_SERVER;
pthread_mutex_t go_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t go_cv = PTHREAD_COND_INITIALIZER;

int
main(int argc, char *argv[])
{
pthread_t client, server;
pthread_attr_t attr;
struct sched_param param;
sigset_t set, oset, pset;
int i;

#ifdef PIPEMAIN
/* block SIGPIPE */
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &set, &oset);
#endif

pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

pthread_create(&server, &attr, (void *) server_thread, NULL);
pthread_create(&client, &attr, (void *) client_thread, NULL);

pthread_join(client, NULL);
pthread_join(server, NULL);

#ifdef PIPEMAIN
sigemptyset(&pset);
sigpending(&pset);
if (sigismember(&pset, SIGPIPE)) {
fprintf(stderr, "SIGPIPE delivered to main\n");
sigwait(&set, &i);
}
pthread_sigmask(SIG_SETMASK, &oset, NULL);
#endif

exit(0);
}

void
server_thread(void *v)
{
int s, fd, i;
socklen_t clen;
struct sockaddr_un caddr, saddr;
sigset_t set, oset, pset;

/*
* we run first
*/
s = socket(PF_UNIX, SOCK_STREAM, 0);
unlink("/tmp/sock");
memset(&saddr, 0, sizeof(saddr));
saddr.sun_family = AF_UNIX;
strcpy(saddr.sun_path, "/tmp/sock");
bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
listen(s, 1);

/* tell client to go */
pthread_mutex_lock(&go_mutex);
go = GO_CLIENT;
pthread_cond_signal(&go_cv);
pthread_mutex_unlock(&go_mutex);

/* blocks until client thread connects */
fd = accept(s, (struct sockaddr *) &caddr, &clen);

/* wait for go signal, we need this to make sure we run first */
pthread_mutex_lock(&go_mutex);
while (go != GO_SERVER)
pthread_cond_wait(&go_cv, &go_mutex);
pthread_mutex_unlock(&go_mutex);

#ifdef PIPESRV
/* block SIGPIPE */
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &set, &oset);
#endif

/* close recv end */
close(fd);

/* client is waiting on our go signal, start him up */
pthread_mutex_lock(&go_mutex);
go = GO_CLIENT;
pthread_cond_signal(&go_cv);
pthread_mutex_unlock(&go_mutex);

/* and wait here */
pthread_mutex_lock(&go_mutex);
while (go != GO_SERVER)
pthread_cond_wait(&go_cv, &go_mutex);
pthread_mutex_unlock(&go_mutex);

/* client should have received sigpipe */
close(s);
unlink("/tmp/sock");

#ifdef PIPESRV
sigemptyset(&pset);
sigpending(&pset);
if (sigismember(&pset, SIGPIPE)) {
fprintf(stderr, "SIGPIPE delivered to server\n");
sigwait(&set, &i);
}
pthread_sigmask(SIG_SETMASK, &oset, NULL);
#endif

pthread_exit(NULL);
}

void
client_thread(void *v)
{
int s, i;
struct sockaddr_un addr;
sigset_t set, oset, pset;
struct timespec sleep;

sleep.tv_sec = 1;
sleep.tv_nsec = 0;

/* wait for go signal */
pthread_mutex_lock(&go_mutex);
while (go != GO_CLIENT)
pthread_cond_wait(&go_cv, &go_mutex);
pthread_mutex_unlock(&go_mutex);

/* connect to server thread */
s = socket(PF_UNIX, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "/tmp/sock");
connect(s, (struct sockaddr *) &addr, sizeof(addr));

/* server is waiting on our go signal, start him up */
pthread_mutex_lock(&go_mutex);
go = GO_SERVER;
pthread_cond_signal(&go_cv);
pthread_mutex_unlock(&go_mutex);

/* and wait for our turn */
pthread_mutex_lock(&go_mutex);
while (go != GO_CLIENT)
pthread_cond_wait(&go_cv, &go_mutex);
pthread_mutex_unlock(&go_mutex);

/* did that seem useless? we needed to make sure the server ran first */

/* block SIGPIPE */
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &set, &oset);

/* this should deliver SIGPIPE to *this* thread */
write(s, (const void *) &i, 1);
/* receive it and clear mask */
sigemptyset(&pset);
sigpending(&pset);
if (sigismember(&pset, SIGPIPE)) {
fprintf(stderr, "SIGPIPE delivered to client\n");
/*nanosleep(&sleep, NULL);*/
sigwait(&set, &i);
}
pthread_sigmask(SIG_SETMASK, &oset, NULL);

/* clean up */
close(s);

/* tell server to finish */
pthread_mutex_lock(&go_mutex);
go = GO_SERVER;
pthread_cond_signal(&go_cv);
pthread_mutex_unlock(&go_mutex);

pthread_exit(NULL);
}

Loic Domaigne

unread,
Aug 5, 2003, 7:39:41 AM8/5/03
to
Hi Frank,

Pardon me for butting in a thread that I didn't follow from the
beginning... But

> > For the sake of discussion, let's say that SIGPIPE is delivered to the
> > same thread. I'll test that in a few days and report back.

AFAIK, SUSv3 states (in the rationale volume in believe) that SIGPIPE
_MAY_ be delivered to the same thread...

OTOH from SUSv3:

| The write() and [XSI] pwrite() functions shall fail if:


|
| [EPIPE]
| An attempt is made to write to a pipe or FIFO that is not open
| for reading by any process, or that only has one end open.
| A SIGPIPE signal shall also be sent to the thread.

So like you, I understand that in this case SIGPIPE should be
delivered to the thread that calls send().


> So a few days has become a few weeks. But I have results. And they
> are not good.
>
> glibc-2.1.3: SIGPIPE always goes to thread generating it
> glibc-2.3.2: depends on which threads are blocking SIGPIPE

Is it on Linux?

I have unfortunately not the time to watch at your code in details.
But two things that I noticed:

- When SIGPIPE is delivered as a process-oriented signal (i.e. the
signal is sent to the process, not to the offending thread), then I
believe this code is broken. Indeed, even with PIPESRV enabled the
signal could be delivered to the initial thread or the client as well
(I believe, you are seing this result).
[ Ok, I agree that this shouldn't be the case, if the implementation
conforms to SUS. ]

- There is a time windows between the sigpending() and sigwait()
(might not be relevant here).


Regards,
Loic.

David Butenhof

unread,
Aug 5, 2003, 9:38:40 AM8/5/03
to
Loic Domaigne wrote:

>> > For the sake of discussion, let's say that SIGPIPE is delivered to the
>> > same thread. I'll test that in a few days and report back.
>
> AFAIK, SUSv3 states (in the rationale volume in believe) that SIGPIPE
> _MAY_ be delivered to the same thread...

I'm not going to search the rationale without a specific reference; but in
any case remember that POSIX/SUS rationale is not part of the standard. In
particular, an incorrect statement in rationale places no obligation or
restrictions on implementations or applications.

> OTOH from SUSv3:
>
> | The write() and [XSI] pwrite() functions shall fail if:
> |
> | [EPIPE]
> | An attempt is made to write to a pipe or FIFO that is not open
> | for reading by any process, or that only has one end open.
> | A SIGPIPE signal shall also be sent to the thread.
>
> So like you, I understand that in this case SIGPIPE should be
> delivered to the thread that calls send().

SIGPIPE is a "thread directed" signal, and therefore SHALL (must) be
delivered to the signal that initiated the action -- e.g., calling send()
on a broken pipe.

This was always the intent, but the wording was incorrect until relatively
recently. POSIX 1003.1-1996 says "sent to the process" (incorrect) while
-2001 says "sent to the thread" (correct). (We're still finding occurrences
of the word "process" that ought to have been changed long ago to
"thread".) I'm not sure right now what SUSv2 said -- but it explicitly
defers to 1996 in case of contradiction so, technically speaking, it can't
really have fixed this.

There are currently no systems carrying the UNIX 03 brand, or claiming
conformance to the (fixed) 2001 or 2003 editions of POSIX 1003.1. So while
some existing implementations did it correctly anyway, you can't (portably)
depend on correct behavior. But of course you should complain to vendors
who did it wrong, and warn them that they'll need to fix it. ;-)

--
/--------------------[ David.B...@hp.com ]--------------------\
| Hewlett-Packard Company Tru64 UNIX & VMS Thread Architect |
| My book: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\----[ http://homepage.mac.com/dbutenhof/Threads/Threads.html ]---/

Frank Cusack

unread,
Aug 6, 2003, 3:57:48 AM8/6/03
to
On 5 Aug 2003 04:39:41 -0700 loic...@gmx.net (Loic Domaigne) wrote:
> Hi Frank,
>
> Pardon me for butting in a thread that I didn't follow from the
> beginning... But

No, please! I'm eager to hear all input, you have added to my
knowledge.

> So like you, I understand that in this case SIGPIPE should be
> delivered to the thread that calls send().

ahh ... great.

>> So a few days has become a few weeks. But I have results. And they
>> are not good.
>>
>> glibc-2.1.3: SIGPIPE always goes to thread generating it
>> glibc-2.3.2: depends on which threads are blocking SIGPIPE
>
> Is it on Linux?

Yes. 2.2.19 for glibc-2.1.3 and 2.4.20 for glibc-2.3.2. I suppose
the kernel thread support might make a difference (whether a userland
thread lib decides where to send the signal v. the kernel deciding).
Any ideas on if 2.2 v. 2.4 might be the real distinguisher here?

> - There is a time windows between the sigpending() and sigwait()
> (might not be relevant here).

hmm I will have to consider this.

/fc

Frank Cusack

unread,
Aug 6, 2003, 4:04:53 AM8/6/03
to
Lest readers think that the existing replies to this thread have ended
it (ie, satisfied my questions), I want to stress that I'm still very
interested in some opinion on the behavior below.

thanks

Loic Domaigne

unread,
Aug 6, 2003, 11:18:01 AM8/6/03
to
Hi Frank!

> Lest readers think that the existing replies to this thread have ended
> it (ie, satisfied my questions), I want to stress that I'm still very
> interested in some opinion on the behavior below.

> Yes. 2.2.19 for glibc-2.1.3 and 2.4.20 for glibc-2.3.2. I suppose


> the kernel thread support might make a difference (whether a userland
> thread lib decides where to send the signal v. the kernel deciding).
> Any ideas on if 2.2 v. 2.4 might be the real distinguisher here?

I think I have some ideas regarding that behaviour. But one question
first: are you by chance using RedHat v9.0 for the kernel 2.4.20 with
glibc-2.3.2?


> > With Solaris it's really strange. The client thread ALWAYS gets SIGPIPE.
> > But sometimes, another thread does as well, as best as I can tell:
> >
> > [root@brak]# gcc -o block -lpthread -lrt -lsocket -lnsl block.c
> > [root@brak]# for f in 0 1 2 3 4 5 6 7 8 9 ; do ./block ; done
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > Broken Pipe
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> > SIGPIPE delivered to client
> >
> > This seems really really wrong.

No, I don't think this is sooo wrong. I guess, the SIGPIPE gets
delivered to the process. As a result, any eligible thread might
receive the signal, unless you blocked it explicitely.

I believe, that's what you are seeing (e.g. if SIGPIPE is delivered to
the initial thread, then it results a "Broken Pipe").

> > Another note, without any #defines (like above), if I uncomment the
> > call to nanosleep(), SIGPIPE is always delivered twice! ie, I always
> > get both messages
> >
> > SIGPIPE delivered to client
> > Broken Pipe

This one looks strange. But, I would first fix the signal handling and
make it POSIX compliant. And then re-run your experiments. Some of
your oddities might disappear.


> > With -DPIPEMAIN, I get only the "delivered" message and a 1s pause, as
> > I would expect.

Which is IMHO logic, since with -DPIPEMAIN you are more "Posixly
correct" concerning the handling of SIGPIPE. Well, strictly speaking,
you don't need to block SIGPIPE again in the client, since the sigmask
is inherited from the initial thread. But that shouldn't hurt.


> > Doesn't this seem like a Solaris bug? Here's the code in its entirety:

Well, we shall see! But *please* fix first your code and check again!


Regards,
Loic.

David Butenhof

unread,
Aug 6, 2003, 12:47:58 PM8/6/03
to
Loic Domaigne wrote:

>> > This seems really really wrong.
>
> No, I don't think this is sooo wrong. I guess, the SIGPIPE gets
> delivered to the process. As a result, any eligible thread might
> receive the signal, unless you blocked it explicitely.

Well, it's certainly wrong for SIGPIPE to be delivered to the process. I do
vaguely recall someone once saying that Solaris would sometimes treat
SIGPIPE as a "process directed" signal. I thought that was long before
Solaris 9 -- but then it's hard to keep track of releases. (Even of my own
OS, much less others!)

>> > Another note, without any #defines (like above), if I uncomment the
>> > call to nanosleep(), SIGPIPE is always delivered twice! ie, I always
>> > get both messages
>> >
>> > SIGPIPE delivered to client
>> > Broken Pipe
>
> This one looks strange. But, I would first fix the signal handling and
> make it POSIX compliant. And then re-run your experiments. Some of
> your oddities might disappear.

The code isn't strictly POSIX conforming -- but it is UNIX 98, and Solaris 9
is UNIX 98 branded. That is, specifically, POSIX lacks the sigpending()
function; but it's in the Single UNIX Specification.

>> > With -DPIPEMAIN, I get only the "delivered" message and a 1s pause, as
>> > I would expect.
>
> Which is IMHO logic, since with -DPIPEMAIN you are more "Posixly
> correct" concerning the handling of SIGPIPE. Well, strictly speaking,
> you don't need to block SIGPIPE again in the client, since the sigmask
> is inherited from the initial thread. But that shouldn't hurt.

"More POSIXly correct"? Well, that depends on how you look at it. When
dealing with a process directed signal, say SIGINT, you absolutely need at
least parts of the PIPEMAIN conditional. You should block the signal in
main before creating any threads so that all inherit it. At least, you need
to have the signal blocked in all threads before it might be generated.

However, none of that is relevant here because SIGPIPE isn't a process
directed signal. It should be quite sufficient that client_thread blocks
SIGPIPE before issuing the write() to the broken pipe. The signal must be
delivered to that thread, and should be held pending until sigwait() is
called.

The code IS correct.

At least, that is, for the intent of POSIX. As I said, the actual text of
the standard doesn't quite follow the intent in several accidental
respects, and hadn't been fixed in the version to which Solaris claims
conformance. While the behavior you see is certainly unfortunate, it may be
deliberate and isn't necessarily "illegal" or even "nonconforming".

>> > Doesn't this seem like a Solaris bug? Here's the code in its entirety:
>
> Well, we shall see! But *please* fix first your code and check again!

Granted, that if one accepts that Solaris incorrectly treats SIGPIPE as a
process directed signal, then the posted code isn't correct for Solaris. If
so, then the fact that the code follows the intent (and future wording) of
the standard is more or less irrelevant.

It's hard for an application to actually DO anything with a process directed
SIGPIPE (which is why it needs to be thread directed); but then most people
prefer to avoid SIGPIPE in favor of EPIPE return statuses anyway, and
there's a good reason for that. ;-)

Frank Cusack

unread,
Aug 6, 2003, 4:33:22 PM8/6/03
to
Thanks Dave, Loic. Based on your feedback I'll bring this up with Sun.

Some addt'l comments inline.

On 6 Aug 2003 08:18:01 -0700 loic...@gmx.net (Loic Domaigne) wrote:
> I think I have some ideas regarding that behaviour. But one question
> first: are you by chance using RedHat v9.0 for the kernel 2.4.20 with
> glibc-2.3.2?

Yes.

>> > With Solaris it's really strange. The client thread ALWAYS gets SIGPIPE.
>> > But sometimes, another thread does as well, as best as I can tell:
>> >
>> > [root@brak]# gcc -o block -lpthread -lrt -lsocket -lnsl block.c
>> > [root@brak]# for f in 0 1 2 3 4 5 6 7 8 9 ; do ./block ; done

>> > SIGPIPE delivered to client [0]
>> > SIGPIPE delivered to client [1]
>> > SIGPIPE delivered to client [2]
>> > Broken Pipe [2] !!!
>> > SIGPIPE delivered to client [3]
>> > SIGPIPE delivered to client [4]
>> > SIGPIPE delivered to client [5]
>> > SIGPIPE delivered to client [6]
>> > SIGPIPE delivered to client [7]
>> > SIGPIPE delivered to client [8]
>> > SIGPIPE delivered to client [9]


>> >
>> > This seems really really wrong.
>
> No, I don't think this is sooo wrong. I guess, the SIGPIPE gets
> delivered to the process. As a result, any eligible thread might
> receive the signal, unless you blocked it explicitely.
>
> I believe, that's what you are seeing (e.g. if SIGPIPE is delivered to
> the initial thread, then it results a "Broken Pipe").

Sorry, I wasn't clear about what was going on. I've added numbering
to the quote above. SIGPIPE is delivered to the client thread (first
"2" marker) *and* the initial thread ("Broken Pipe" report) ...

>> > Another note, without any #defines (like above), if I uncomment the
>> > call to nanosleep(), SIGPIPE is always delivered twice! ie, I always
>> > get both messages
>> >
>> > SIGPIPE delivered to client
>> > Broken Pipe

... just like it does here. Above, it's intermittent. Here (with
nanosleep()) it's consistent. A misdirected signal, I can understand.
Receiving the signal twice ... I can't.

> This one looks strange. But, I would first fix the signal handling and
> make it POSIX compliant. And then re-run your experiments. Some of
> your oddities might disappear.

See Dave's comment, I've included it below.

>> > With -DPIPEMAIN, I get only the "delivered" message and a 1s pause, as
>> > I would expect.
>
> Which is IMHO logic, since with -DPIPEMAIN you are more "Posixly
> correct" concerning the handling of SIGPIPE. Well, strictly speaking,
> you don't need to block SIGPIPE again in the client, since the sigmask
> is inherited from the initial thread.

ahh .. I missed that. Thanks.

On Wed, 06 Aug 2003 12:47:58 -0400 David Butenhof <David.B...@hp.com> wrote:
> The code isn't strictly POSIX conforming -- but it is UNIX 98, and Solaris 9
> is UNIX 98 branded. That is, specifically, POSIX lacks the sigpending()
> function; but it's in the Single UNIX Specification.

So many standards to choose from! argh! Is it worthwhile to care about
POSIX these days (ie, just shoot for SUS wrt portable programs)?

> The code IS correct.

Cool. That's reassuring. (and a large part of what I was looking for --
verification that it's not just me).

/fc

Frank Cusack

unread,
Aug 6, 2003, 4:35:28 PM8/6/03
to
On Tue, 05 Aug 2003 09:38:40 -0400 David Butenhof <David.B...@hp.com> wrote:
> SIGPIPE is a "thread directed" signal, and therefore SHALL (must) be
> delivered to the signal that initiated the action -- e.g., calling send()
> on a broken pipe.
>
> This was always the intent, but the wording was incorrect until relatively
> recently. POSIX 1003.1-1996 says "sent to the process" (incorrect) while
> -2001 says "sent to the thread" (correct). (We're still finding occurrences
> of the word "process" that ought to have been changed long ago to
> "thread".) I'm not sure right now what SUSv2 said -- but it explicitly
> defers to 1996 in case of contradiction so, technically speaking, it can't
> really have fixed this.

Can you point me to a [specific] online reference for this? For citation
in bug reports.

/fc

Loic Domaigne

unread,
Aug 6, 2003, 8:08:12 PM8/6/03
to
Hi!

LD> I think I have some ideas regarding that behaviour. But one
LD> question first: are you by chance using RedHat v9.0 for the
LD> kernel 2.4.20 with glibc-2.3.2?
FC>
FC> Yes.

Ok, the story is that due to the restriction of kernel 2.2, with
LinuxThreads (LT) the signal resulting from a system call was always
delivered to the thread that issued the call. This deviates from
POSIX...

However, for the SIGPIPE case, it turns out to be the expected one.
But that's more luck than a really wanted design ;-)

FC> With glibc-2.3.2, if another thread can handle a SIGPIPE,
FC> it goes there. I can understand this.

Now, I believe on RHv9.0, you are using a patched kernel with NPTL
that fixed the signal issues of the kernel 2.2 / LT.

However, your test seems to indicate that SIGPIPE is not handled as
expected. If I understood Mr. Pthread correctly, this should be
delivered to the client thread regardless if you are sigwait'ing or
not on this signal in the server thread.

Maybe the issue you are raising has been already discussed and fixed.
I would definitively have a look in NPTL mailing list archive.

FC> This seems really really wrong.
FC>
LD> No, I don't think this is sooo wrong. I guess, the SIGPIPE gets
LD> delivered to the process. As a result, any eligible thread might
LD> receive the signal, unless you blocked it explicitely.
DB>
DB> Well, it's certainly wrong for SIGPIPE to be delivered to the
DB> process.

Agree.

I should really take care about my wording, espcially when - let's see
what the backcover says, - "a recognized Pthreads authority" hangs on
the newsgroup ;-)

FC> Sorry, I wasn't clear about what was going on. I've added
FC> numbering to the quote above. SIGPIPE is delivered to the
FC> client thread (first "2" marker) *and* the initial thread
FC> ("Broken Pipe" report) ...

No, no... that's was perfectly clear. What I meant was:

your experiment seems to indicate that SIGPIPE is sent to the process,
and not to the thread that issued the send().

So my suggestion was: to rewrite your code as if you were dealing with
an ordinary SIGINT. And see if you get the same behavior as you would
get for a SIGINT.

I want to check if the assumption "SIGPIPE is delivered to the
process" is true or not.

However, I did the mistake to describe the whole thing as "Solaris
treats the SIGPIPE as process oriented signal"... Bad, bad, bad Loic
:(


<snip>

DB> However, none of that is relevant here because SIGPIPE
DB> isn't a process directed signal. It should be quite sufficient
DB> that client_thread blocks SIGPIPE before issuing the write() to
DB> the broken pipe. The signal must be delivered to that thread,
DB> and should be held pending until sigwait() is called.
DB>
DB> The code IS correct.

Agree that the code is correct.

Unfortunately, the theoritical and real world doesn't always match :(

DB> While the behavior you see is certainly unfortunate,
DB> it may be deliberate and isn't necessarily "illegal" or
DB> even "nonconforming".

yes, yes... my explanations were *bad* ...


DB> Granted, that if one accepts that Solaris incorrectly
DB> treats SIGPIPE as a process directed signal, then the posted code
DB> isn't correct for Solaris.

My primary intention was not to define what "means correct code for
Solaris". But to conduct some tests to check if the SIGPIPE is treated


as a process directed signal.


DB> If so, then the fact that the code follows the intent
DB> (and future wording) of the standard is more or less
DB> irrelevant.

Yup!

FC> So many standards to choose from! argh! Is it worthwhile
FC> to care about POSIX these days (ie, just shoot for SUS
FC> wrt portable programs)?

Actually, I ever heard that SUS is a superset of POSIX, and that
actually any serious Un*x should in this respect follow SUS.

But I guess, it makes still sense to follow POSIX these days. Indeed,
POSIX is currently being aligned with SUS. So the next POSIX release
will have a lot of cool new features :)


Loic.

Roger Faulkner

unread,
Aug 6, 2003, 9:43:15 PM8/6/03
to
David Butenhof <David.B...@hp.com> wrote in message news:<3f2f...@usenet01.boi.hp.com>...

>
> > OTOH from SUSv3:
> >
> > | The write() and [XSI] pwrite() functions shall fail if:
> > |
> > | [EPIPE]
> > | An attempt is made to write to a pipe or FIFO that is not open
> > | for reading by any process, or that only has one end open.
> > | A SIGPIPE signal shall also be sent to the thread.
> >
> > So like you, I understand that in this case SIGPIPE should be
> > delivered to the thread that calls send().
>
> SIGPIPE is a "thread directed" signal, and therefore SHALL (must) be
> delivered to the signal that initiated the action -- e.g., calling send()
> on a broken pipe.

David:

Perhaps this just another oversight in the SUSV3 spec, but it gives
two reasons for SIGPIPE being generated on write():

The write() and [XSI] pwrite() functions shall fail if:

[EPIPE] An attempt is made to write to a pipe or FIFO that is not
open for reading by any process, or that only has one end
open. A SIGPIPE signal shall also be sent to the thread.

The write() function shall fail if:

[EPIPE] A write was attempted on a socket that is shut down for
writing, or is no longer connected. In the latter case,
if the socket is of type SOCK_STREAM, the SIGPIPE signal
is generated to the calling process.

So, do I believe the last statement or do I assume it really meant
"to the calling thread"?

Roger Faulkner
roger.f...@sun.com

Loic Domaigne

unread,
Aug 7, 2003, 3:53:54 AM8/7/03
to
Hi Roger,

> > SIGPIPE is a "thread directed" signal, and therefore SHALL (must) be
> > delivered to the signal that initiated the action -- e.g., calling send()
> > on a broken pipe.
>
> David:
>
> Perhaps this just another oversight in the SUSV3 spec, but it gives
> two reasons for SIGPIPE being generated on write():
>
> The write() and [XSI] pwrite() functions shall fail if:
>
> [EPIPE] An attempt is made to write to a pipe or FIFO that is not
> open for reading by any process, or that only has one end
> open. A SIGPIPE signal shall also be sent to the thread.
>
> The write() function shall fail if:
>
> [EPIPE] A write was attempted on a socket that is shut down for
> writing, or is no longer connected. In the latter case,
> if the socket is of type SOCK_STREAM, the SIGPIPE signal
> is generated to the calling process.
>
> So, do I believe the last statement or do I assume it really meant
> "to the calling thread"?

Good point...

I checked out in TC1: this is exactly the same description... I
couldn't find any update ongoing regarding this statement.

Hum, looks like a bug to me (?)


Regards,
Loic.

Frank Cusack

unread,
Aug 7, 2003, 4:13:58 AM8/7/03
to
On 6 Aug 2003 17:08:12 -0700 loic...@gmx.net (Loic Domaigne) wrote:
> FC> Sorry, I wasn't clear about what was going on. I've added
> FC> numbering to the quote above. SIGPIPE is delivered to the
> FC> client thread (first "2" marker) *and* the initial thread
> FC> ("Broken Pipe" report) ...
>
> No, no... that's was perfectly clear. What I meant was:
>
> your experiment seems to indicate that SIGPIPE is sent to the process,
> and not to the thread that issued the send().

Yeah, I see what's going on here now.

I report a thread as having received a signal after calling
sigpending(), which isn't correct; the signal has not been delivered
yet. Loic, you did point this out but I didn't catch on.

So on Solaris, SIGPIPE is always being delivered to the calling thread
(in my limited test case, which is not thorough enough to be definitive)
but then that thread is sometimes scheduled out (or, blocks when I
call nanosleep()) and since Solaris isn't treating the signal as thread-
directed, it "moves" it to the to-be-scheduled thread. Which makes
sense since the signal should be delivered asap (% synchronous signals).

Linux/NPTL basically has the same bug, it's not treating SIGPIPE as
thread-directed. I'll send the maintainers a note.

Robert, can you open a bug for Solaris, or should I pursue it myself?

thanks all
/fc

Frank Cusack

unread,
Aug 7, 2003, 5:31:19 AM8/7/03
to
On Thu, 07 Aug 2003 01:13:58 -0700 Frank Cusack <fcu...@fcusack.com> wrote:
> Robert, can you open a bug for Solaris, or should I pursue it myself?

err, sorry. I meant Roger.

/fc

David Butenhof

unread,
Aug 7, 2003, 9:34:38 AM8/7/03
to
Roger Faulkner wrote:

> Perhaps this just another oversight in the SUSV3 spec, but it gives
> two reasons for SIGPIPE being generated on write():
>
> The write() and [XSI] pwrite() functions shall fail if:
>
> [EPIPE] An attempt is made to write to a pipe or FIFO that is not
> open for reading by any process, or that only has one end
> open. A SIGPIPE signal shall also be sent to the thread.
>
> The write() function shall fail if:
>
> [EPIPE] A write was attempted on a socket that is shut down for
> writing, or is no longer connected. In the latter case,
> if the socket is of type SOCK_STREAM, the SIGPIPE signal
> is generated to the calling process.
>
> So, do I believe the last statement or do I assume it really meant
> "to the calling thread"?

As I think I commented earlier, we're STILL finding incorrect uses of
process.

The intent of the working group is clear: any signal caused by the direct
synchronous action of a thread must be delivered to that thread, or held
pending against the thread if the signal is blocked. A signal that cannot
be attributed to the direct synchronous action of a thread must be
delivered to one and only one thread in the process, or held pending
against the process if all threads have it blocked.

SIGPIPE is generated by a write to a broken pipe; a synchronous action
clearly attributed to the direct action of a thread. It's therefore thread
directed.

BUT, "the standard says what it says", even when it's wrong. The standard
requires that a write() to a socket that is disconnected or shut down must
generate SIGPIPE to the "calling process". (Which is a self-evidently
stupid and meaningless statement since the process didn't make the write
call. ;-) ) Clearly, when someone fixed [EPIPE] to say "thread", they
missed the SECOND [EPIPE].

However, the fact that it's wrong means that it can and should change in a
future edition. Implementations should persue the winds of change and
implement correctly now. Applications of course may be in a tougher
position, if they really care... basically, the official answer would have
to be that until the standard is corrected EITHER the correct behavior or
the specified incorrect behavior is acceptable.

If only standards (or implementations, applications, or anything else) could
be perfect. ;-)

David Butenhof

unread,
Aug 7, 2003, 9:44:49 AM8/7/03
to
Loic Domaigne wrote:

> I should really take care about my wording, espcially when - let's see
> what the backcover says, - "a recognized Pthreads authority" hangs on
> the newsgroup ;-)

On the other hand, how many of you would actually "recognize" me if you saw
me on the street? Shouldn't anyone printing a statement like that be
required to include a photograph to ensure the statement is true? ;-)

> DB> The code IS correct.
>
> Agree that the code is correct.
>
> Unfortunately, the theoritical and real world doesn't always match :(

One of my favorite quotes: "In theory, there's no difference between theory
and practice. In Practice, there's no similarity."

> FC> So many standards to choose from! argh! Is it worthwhile
> FC> to care about POSIX these days (ie, just shoot for SUS
> FC> wrt portable programs)?
>
> Actually, I ever heard that SUS is a superset of POSIX, and that
> actually any serious Un*x should in this respect follow SUS.
>
> But I guess, it makes still sense to follow POSIX these days. Indeed,
> POSIX is currently being aligned with SUS. So the next POSIX release
> will have a lot of cool new features :)

Actually, the 2001 edition of POSIX 1003.1 is the same document as SUSv3. So
these days "POSIX" (meaning, as is often the case, POSIX.1) is the same as
SUS in nearly all respects. (Except where SUSv3 is a superset.)

Implementations should strive to implement the Single UNIX Specification,
because by this strategy we can approach the ideal of, well, a "single
UNIX" interface and portable code. Applications, similarly, should be
designed to work with implementations that conform to SUS. Unfortunately,
they'll always still need to make those critical decisions about whether to
work around implementation (or even specification) bugs.

None of this, however, is the same as saying that POSIX is irrelevant. POSIX
is the basis and core of SUS.

those who know me have no need of my name

unread,
Aug 25, 2003, 5:49:03 PM8/25/03
to
in comp.unix.programmer i read:

[thread directed signal wording problems in susv2 which v3 has tried to
correct]

>Can you point me to a [specific] online reference for this? For citation
>in bug reports.

susv2 is available from <http://www.unix.org/version2/> -- or it was last
time i looked -- and v3 is available from <http://www.unix.org/version3/>.

--
a signature

0 new messages