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

standard close behavior

1 view
Skip to first unread message

Thomas Maier-Komor

unread,
Dec 27, 2009, 6:05:21 AM12/27/09
to
Hi,

I am getting inconsistent behaviors on different systems with the
program below. Now, I am wondering if the behavior of this program is
triggering unspecified behavior according to SUS.

Can anybody comment, weather closing a file-descriptor should unblock a
thread blocked in read(2), when a signal handler uses close(2) on the
same file descriptor.

To trigger the behavior in question compile and run the program, and
simply signal SIGINT by pressing CTRL-C.

The behavior I am seeing is as follows:
- Linux: continues reading on the file descriptor, and the file
descriptor isn't even closed. I.e. read will return valid data, if one
hits enter after pressing CTRL-C

- FreeBSD and Solaris: read(2) returns -1 and sets errno to EBADF

I would have suspected the behavior seen by FreeBSD and Solaris. But is
that expectation valid, or am I triggering unspecified behavior.

Thanks,
Thomas

#define _REENTRANT 1
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>


void sig_handler(int sig)
{
write(STDOUT_FILENO,"sig_handler\n",12);
close(STDIN_FILENO);
}

void *reader(void *ignored)
{
char buf[4096];
char msg[4096];
int err;

buf[0] = 0;
write(STDOUT_FILENO,"reader started\n",16);
err = read(STDIN_FILENO,buf,sizeof(buf));
if (err == -1)
snprintf(msg,sizeof(msg),"errno = %d, errstr =
%s\n",errno,strer
else
snprintf(msg,sizeof(msg),"read = %d, buf = %s\n",err,buf);
write(STDOUT_FILENO,msg,strlen(msg));
return 0;
}

int main(void)
{
pthread_t thr;
int err;
struct sigaction sig;

sig.sa_handler = sig_handler;
err = sigemptyset(&sig.sa_mask);
assert(err == 0);
err = sigaddset(&sig.sa_mask,SIGINT);
assert(err == 0);
sig.sa_flags = SA_RESTART;
err = sigaction(SIGINT,&sig,0);
assert(err == 0);
printf("creating thread...\n");
err = pthread_create(&thr,0,reader,0);
assert(err == 0);
printf("sleeping...\n");
sleep(60);
pthread_join(thr,0);
printf("done...\n");
return 0;
}

Xavier Roche

unread,
Dec 27, 2009, 8:51:23 AM12/27/09
to
Thomas Maier-Komor a �crit :

> I would have suspected the behavior seen by FreeBSD and Solaris. But is
> that expectation valid, or am I triggering unspecified behavior.

The behaviour of file descriptors is nothing but clear regarding
multithreading in POSIX:
<http://www.opengroup.org/onlinepubs/000095399/functions/close.html>
<http://www.opengroup.org/onlinepubs/000095399/functions/read.html>

Linux /seems/ to handle some kind of refcount on the file descriptor
level, hence (AFAICS) closing stdin in one thread won't do anything but
decreasing the count from 2 to 1 (the other thread is still blocked in a
read operation). These are only guesses, I don't know if the underlying
kernel syscall actually handles such refcount or not, but the behaviour
may be consistent with this hypothesis.

[ The same question is also interesting for interrupting an accept()
system call, with a close() call ]

If the goal is "just" to interrupt a read operation, you can, of course,
slightly change your handler code:

- sig.sa_flags = SA_RESTART;
+ sig.sa_flags = 0;

The handled signal (SIGINT) will not trigger a retry within interrupted
system calls anymore, which lets you handle manually the EINTR error
within the reader() function (you may want to retry the call unless some
king of mutexed flag was set to TRUE in your signal handler, because the
system call may be interrupted by someone else). [
pthread_kill()/pthread_sigmask() can also be used to tune the thread(s)
to interrupt. ]

Thomas Maier-Komor

unread,
Dec 27, 2009, 9:21:29 AM12/27/09
to


Hi Xavier,

what it seems to be doing is obvious. My question was really whether the
standard says anything about this.

BTW: changing sa_flags to 0 doesn't even give the behavior you expect. I
suspected read to return with EINTR, too - but of course that isn't
guaranteed, because EINTR will only turn up in the thread that handles
the signal.

In case of Linux, changing sa_flags to 0 has no impact what so ever -
i.e. the interrupt seems to be handled in the thread that is blocked in
sleep(). So this would really require binding SIGINT to the reader
thread, which is pretty restrictve..

Cheers,
Thomas

Xavier Roche

unread,
Dec 27, 2009, 9:39:23 AM12/27/09
to
Thomas Maier-Komor a �crit :

> what it seems to be doing is obvious. My question was really whether the
> standard says anything about this.

The standard (see the opengroup pages in my message) does not seem to
say anything, unfortunately.

> In case of Linux, changing sa_flags to 0 has no impact what so ever

[ Yep, because kill() (and friends) seem to send the signal to the first
thread. A quick solution is to mirror the signal to the correct thread
if necessary:

static pthread_t thr;
void sig_handler(int sig)
{
if (sig != SIGINT)
return;
if (pthread_self() == thr)
return;
write(STDOUT_FILENO,"sig_handler\n",12);
pthread_kill(thr, sig);
}
]

J de Boyne Pollard

unread,
Dec 27, 2009, 9:42:20 AM12/27/09
to
TMK> My question was really whether the
TMK> standard says anything about this.

And your answer is "Yes". Read what the standard says about non-
cancelled I/O requests completing as if the close() didn't occur until
the end of the I/O request.

Thomas Maier-Komor

unread,
Dec 27, 2009, 9:44:06 AM12/27/09
to

Thank you for the pointer.

- Thomas

Thomas Maier-Komor

unread,
Dec 27, 2009, 9:48:29 AM12/27/09
to


Hi Xavier,

that is a good idea.

Thanks,
Thomas

Xavier Roche

unread,
Dec 27, 2009, 9:52:18 AM12/27/09
to
>> if (pthread_self() == thr)

Just a last small note for readers: portable code should use
pthread_equal(pthread_self(), thr) instead of the == operator, for
cleaner code.

Rainer Weikusat

unread,
Dec 27, 2009, 1:03:26 PM12/27/09
to

When there is an outstanding cancelable asynchronous I/O
operation against fildes when close() is called, that I/O
operation may be canceled. An I/O operation that is not
canceled completes as if the close() operation had not yet
occurred. All operations that are not canceled shall complete
as if the close() blocked until the operations completed.

There is no asynchronous I/O involved in your program and hence, this
paragraph doesn't apply to it since it refers to cancelable,
asynchronous I/O operations.

Chris Friesen

unread,
Dec 28, 2009, 12:48:40 AM12/28/09
to
On 12/27/2009 12:03 PM, Rainer Weikusat wrote:

> When there is an outstanding cancelable asynchronous I/O
> operation against fildes when close() is called, that I/O
> operation may be canceled. An I/O operation that is not
> canceled completes as if the close() operation had not yet
> occurred. All operations that are not canceled shall complete
> as if the close() blocked until the operations completed.
>
> There is no asynchronous I/O involved in your program and hence, this
> paragraph doesn't apply to it since it refers to cancelable,
> asynchronous I/O operations.

Which as far as I can tell means that the issue in the original message
is not specifically addressed in the standard.

I suspect the linux/glibc folks decided to treat a read() running on
another thread as though it were an async I/O operation.

Interesting case.

Chris

Barry Margolin

unread,
Dec 28, 2009, 12:57:33 AM12/28/09
to
In article <hh7rir$2ot$3...@news.httrack.net>,
Xavier Roche <xro...@free.fr.NOSPAM.invalid> wrote:

> Thomas Maier-Komor a �crit :
> > what it seems to be doing is obvious. My question was really whether the
> > standard says anything about this.
>
> The standard (see the opengroup pages in my message) does not seem to
> say anything, unfortunately.

Your previous message said that the standard is clear on it (unless when
you said "nothing but clear" you meant to say "anything but clear" --
your name and email address suggest that you're not a native English
speaker, so you might not know the idioms correctly).

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***

Xavier Roche

unread,
Dec 28, 2009, 3:50:00 AM12/28/09
to
Barry Margolin a �crit :

> you said "nothing but clear" you meant to say "anything but clear"

Oh gosh, yes, you're perfectly right - thanks for fixing that. I meant
that the opengroup pages were _NOT_ clear at all.

Generally speaking, I find the POSIX spec a bit unclear regarding thread
issues and/or seems a bit "unaware" of it (like in this case) time to time.

0 new messages